1

I face an issue with data binding in LWC if I want to use dynamic form. Let's say I have html like this:

 <template for:each={data} for:item="item">
    <lightning-combobox
        key={item.name}
        label={item.name}
        required={item.isRequired}
        placeholder="Select"
        options={item.allowedValues}
     >
    </lightning-combobox>
 </template>

and where {data} is JSON object with all data I need for view .

I need to gather all data(values of selected combobox values) and make JSON from it. Is there any data binding possibilities in LWC for such situations?

For now I have found out only opportunity to use:

var selectFields = this.template.querySelectorAll("combobox"); 

which is not scalable, since I have not only combobox inside template for:each...

4 Answers 4

2

To track the values of your lightning-combobox and other form components, track the index of each item in the for:each template, use the value attribute in the combobox, and handle the onchange event:

<template for:each={data} for:item="item" for:index="index">
   <lightning-combobox
       key={item.name}
       data-index={index}
       label={item.name}
       required={item.isRequired}
       placeholder="Select"
       options={item.allowedValues}
       value={item.myPropertyValue}
       onchange={handleChange}>
   </lightning-combobox>
</template>

In your JS class, assign the value:

@track data;

handleChange(event) {
  this.data[event.target.dataset.index].myPropertyValue = event.target.value;
}

If your Javascript object is a Proxy, you may need to do JSON.parse(JSON.stringify(this.data)) in some fashion in order to be able to directly set the properties as I've illustrated above.

See docs on:

0
0

Welcome to the forum.

You can give a name to the combobox

 <lightning-combobox
    name ="comboboxName"
    key={item.name}
    label={item.name}
    required={item.isRequired}
    placeholder="Select"
    options={item.allowedValues}>
</lightning-combobox>

After that you can access this combobox, only this one, from you JS like this:

let myCombobox = this.template.querySelector('.comboboxName');

that way you can access that specific combobox...

3
  • I just want to avoid this.template.querySelector and want to find possibility to bind data in html and js. Since this.template.querySelector is manipulating with the DOM. Nov 26, 2019 at 15:21
  • But you are getting the data first in LWC JS by using a connectedCallback? Maybe is better to post all the code so we can see how are you getting the data... are you using data service? Nov 26, 2019 at 15:24
  • yes. At first I get the {data} from Apex in connectedCallback then i use it in JS and then in html for:each={data} Nov 26, 2019 at 15:30
0

This is the code snippet of html.


                        <template for:each={data} for:item="item" >
                            <template if:false={item.isRequired}>
                                <c-child-textcomponent key={item.name}
                                    get-item-from-parent={item}>
                                </c-child-textcomponent>
                                <c-child-selectcomponent key={item.name}
                                    get-item-from-parent={item}>
                                </c-child-selectcomponent>
                                <c-child-rich-textcomponent key={item.name}
                                    get-item-from-parent={item}>
                                </c-child-rich-textcomponent>
                                <c-child-floatcomponent key={item.name}
                                    get-item-from-parent={item}>
                                </c-child-floatcomponent>
                                <c-child-date-timecomponent key={item.name}
                                    get-item-from-parent={item}>
                                </c-child-date-timecomponent>
                                <c-child-datecomponent  key={item.name}
                                    get-item-from-parent={item}>
                                </c-child-datecomponent>
                            </template>
                        </template>

                        &nbsp;&nbsp;&nbsp;&nbsp;
                        <div style="margin-left:4%">
                            <div>
                                <lightning-input label="" name="file uploader" onchange={handleFilesChange} type="file" multiple></lightning-input>
                            </div>
                            <br/>
                        </div>
                    </div>
                </div>
                <footer class="slds-modal__footer">
                    <lightning-button label="Cancel" variant="neutral" onclick={closeModal}></lightning-button>&nbsp;&nbsp;&nbsp;&nbsp;
                    <lightning-button label="Save&Sync" variant="brand" onclick={saveMethod}></lightning-button>
                </footer>
            </div>
        </section>
        <div class="slds-backdrop slds-backdrop_open"></div>

   </template>
</div>

And js:

import { LightningElement, api, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent'
import postJiraIssue from '@salesforce/apex/JiraController.postJiraIssue';
import getRestProjectIssues from '@salesforce/apex/JiraController.getRestProjectIssues';
import getRestProjectIssueTypeFields from '@salesforce/apex/JiraController.getRestProjectIssueTypeFields';
import postJiraAttachment from '@salesforce/apex/JiraController.postJiraAttachment';
import JIRA_LOGO from '@salesforce/resourceUrl/jiraLogo';
import {parseIssues, getJiraProjects, createPostJson, getAttachmentArray } from 'c/jiraButtonPopupHelper';

export default class JiraButtonPopup extends LightningElement {

    @api recordId;
    @track jiraLogo = JIRA_LOGO;
    @track openmodel = false;
    @track projectDropdown = null;
    @track issueTypeDropdown = null;
    @track data =[];
    projectKey;
    issueTypeId;

    connectedCallback() {  
        this.projectDropdown = getJiraProjects();  
    }

    get projectOptions() {
        return this.projectDropdown;
    }

    get issueTypeOptions() {
        return this.issueTypeDropdown;
    }

    handleProjectChange(event) {
        this.data =null;
        this.projectKey = event.detail.value;
        getRestProjectIssues({ projectKey: this.projectKey})
        .then(result=>{
            if(result){
                this.issueTypeDropdown = parseIssues(result);
            }
        });
    }

    handleIssueTypeChange(event) {
        this.data = null;
        this.issueTypeId = event.detail.value;
        getRestProjectIssueTypeFields({projectKey: this.projectKey, issueTypeId: this.issueTypeId})
        .then(result => {
            if(result){
                this.data = result;
            }
        });

    }

    openmodal() {
        this.openmodel = true;
    }

    closeModal() {
        this.data = null;
        this.openmodel = false;
    } 

    saveMethod() {

        var richTextFields = this.template.querySelectorAll("c-child-rich-textcomponent");
        var textFields = this.template.querySelectorAll("c-child-textcomponent");
        var selectFields = this.template.querySelectorAll("c-child-selectcomponent");
        var floatFields = this.template.querySelectorAll("c-child-floatcomponent");
        var dateTimeFields = this.template.querySelectorAll("c-child-date-timecomponent");
        var dateFields = this.template.querySelectorAll("c-child-datecomponent");
        var postJson = createPostJson(this.projectKey, this.issueTypeId, richTextFields, textFields, selectFields, floatFields, dateTimeFields, dateFields);

        console.log(JSON.stringify(postJson));

        postJiraIssue({jsonBody:JSON.stringify(postJson)})
        .then(data => {

            if(data.length>20){
                var parsedErrors = JSON.parse(data);
                var errorMessages ='';
                for(var key in parsedErrors){  
                    errorMessages = errorMessages + ' ' + parsedErrors[key];
                }  
                console.log(errorMessages);
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Please Fill The Required Fields',
                        message: errorMessages,
                        variant: 'error'
                    }),
                );  
            }else{
                this.createdJiraIssueKey = data;
                console.log(data);
                for(var z=0; z<this.attachmentArray.length; z++){      
                    postJiraAttachment({fileBody:this.attachmentArray[z].blobArray, fileName:this.attachmentArray[z].filename, projectKey:data}); 
                }
                this.data = null;          
                this.closeModal();
            }
        });
    }

    handleFilesChange(event) {
        this.attachmentArray = getAttachmentArray(event);
    } 
}
0

It's advisable to create a child component that only renders a single form and pass in a single item, the child component would render the form for a single item and have onchange handlers on all it's components, it would also expose a method that the parent component can call to get the edited item.

When the submit/save button is pressed you'd use this.template.querySelectorAll('c-child-component') and call the method to get the edited items for each one.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .