1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Lightning Web コンポーネントで動的入力フォームを生成する

Posted at

概要

LightningWebコンポーネントで入力フォームを動的に追加・削除する機能を簡易的にはでありますが作成したいと思います。
LWCではどうやらcreateElementのような動的にDOMを作成する機能を意図的に制限しているようです。
そのため、HTMLテンプレートとして用意されているfor:eachディレクティブを使用し、Listを反復処理させる事で入力フォームの追加・削除を実現していきます。

実装イメージ

image.png

全体コード

form.html
<template>
    <lightning-card title="顧客登録">
        <lightning-button label="Add" slot="actions" onclick={addRow}></lightning-button>
        <div class="slds-p-horizontal_small">        
            <table class="slds-table slds-table_bordered slds-table_cell-buffer"> 
                <thead>
                    <tr class="slds-text-title_caps">
                        <th scope="col">
                            <div class="slds-truncate"></div>
                        </th>
                        <th scope="col" class="table-body">
                            <div class="slds-truncate"></div>
                        </th>
                        <th scope="col" class="table-body">
                            <div class="slds-truncate">メールアドレス</div>
                        </th>
                        <th scope="col" class="table-body">
                            <div class="slds-truncate"></div>
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <template for:each={list} for:item="row" for:index="index">
                        <tr key={row.key} id={row.key}> 
                            <td class="table-body" style="padding-bottom:20px;">
                                <lightning-input data-id={index} type="text" onchange={handleChangeLastName}></lightning-input>
                            </td>
                            <td class="table-body" style="padding-bottom:20px;">
                                <lightning-input data-id={index} type="text" onchange={handleChangeFirstName}></lightning-input>
                            </td>
                            <td class="table-body" style="padding-bottom:20px;">
                                <lightning-input data-id={index} type="email" onchange={handleChangeEmail}></lightning-input>
                            </td>
                            <td>
                                <lightning-button-icon 
                                    icon-name="utility:delete"
                                    data-id={index}       
                                    alternative-text="Delete"     
                                    class="slds-m-left_xx-small"
                                    onclick={removeRow} 
                                    title="Delete">
                                </lightning-button-icon>
                            </td>
                        </tr>
                    </template>
                </tbody>
            </table>
        </div>
        <p slot="footer">
            <lightning-button name="Save" label="登録" onclick={handleClickSubmit} disabled={disabled}></lightning-button>
        </p>
    </lightning-card>
</template>
form.js
import { LightningElement, track } from 'lwc';

export default class DynamicForm extends LightningElement {
    @track list = [];
    index = 0;
    item = {};

    addRow(){
        this.index++;
        var i = this.index;
        var item = {};
        item.key = i;
        this.list.push(item);
    }
    removeRow(e){
        var selectedRow = e.currentTarget;
        var key = selectedRow.dataset.id;
        if(this.list.length>1){
            this.list.splice(key, 1);
            this.index--;
        }else if(this.list.length == 1){
            this.list = [];
            this.index = 0;
        }
    }
    handleChangeLastName(e){
        var selectedRow = e.currentTarget;
        var key = selectedRow.dataset.id;
        this.list[key].lastname = e.detail.value;
    }
    handleChangeFirstName(e){
        var selectedRow = e.currentTarget;
        var key = selectedRow.dataset.id;
        this.list[key].firstname = e.detail.value;
    }
    handleChangeEmail(e){
        var selectedRow = e.currentTarget;
        var key = selectedRow.dataset.id;
        this.list[key].email = e.detail.value;
    }
    handleClickSubmit(){
        // 登録処理
    }
}

解説

js側で宣言したlist変数をHTML側で反復処理していきます。

<tbody>
    <template for:each={list} for:item="row" for:index="index">
        <tr key={row.key} id={row.key}> 
            <td class="table-body" style="padding-bottom:20px;">
                <lightning-input data-id={index} type="text" onchange={handleChangeLastName}></lightning-input>
            </td>
            <td class="table-body" style="padding-bottom:20px;">
                <lightning-input data-id={index} type="text" onchange={handleChangeFirstName}></lightning-input>
            </td>
            <td class="table-body" style="padding-bottom:20px;">
                <lightning-input data-id={index} type="email" onchange={handleChangeEmail}></lightning-input>
            </td>
            <td>
                <lightning-button-icon 
                    icon-name="utility:delete"
                    data-id={index}       
                    alternative-text="Delete"     
                    class="slds-m-left_xx-small"
                    onclick={removeRow} 
                    title="Delete">
                </lightning-button-icon>
            </td>
        </tr>
    </template>
</tbody>

Addボタンがクリックされた際にこのlist変数に対してindexを値として持つキーを設定します。

addRow(){
    this.index++;
    var i = this.index;
    var item = {};
    item.key = i;
    this.list.push(item);
}

また、HTMLで反復処理している各アイテムの要素に対して、indexをdata-idとして与える事でlist変数と入力フォームの各行を連動させていきます。
フォームに文字列が入力された際、currentTargetでクリックされた要素を取得。dataset.idでdata-idの値を取得します。

handleChangeLastName(e){
    var selectedRow = e.currentTarget;
    var key = selectedRow.dataset.id;
    this.list[key].lastname = e.detail.value;
}
handleChangeFirstName(e){
    var selectedRow = e.currentTarget;
    var key = selectedRow.dataset.id;
    this.list[key].firstname = e.detail.value;
}
handleChangeEmail(e){
    var selectedRow = e.currentTarget;
    var key = selectedRow.dataset.id;
    this.list[key].email = e.detail.value;
}

ここで取得したdata-idのindex値に対応するlist変数の要素に対してキーバリューをセットする事で入力値を各要素に格納します。

また、行を削除する時は入力を格納する時と同様にクリックされた要素のdata-idを取得し、list変数から該当のindex要素を削除します。

removeRow(e){
    var selectedRow = e.currentTarget;
    var key = selectedRow.dataset.id;
    if(this.list.length>1){
        this.list.splice(key, 1);
        this.index--;
    }else if(this.list.length == 1){
        this.list = [];
        this.index = 0;
    }
}

最後に

後はlistをapex側に送り、自由に作成処理を行ってください。
私はjs側でjson文字列に変換した後、apex側で再度プリミティブ型に変換して処理しました。
もっと効率的な手法があればご指摘頂きたいです。
追加するフォームを別コンポーネントとして作成し連携させれば、より柔軟にDOM操作が行えそうですね。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?