[SalesForce] get values in lightning:input with $A.createComponents

I have worked simple things with lightning components I am trying to make things more complex but with lockservices I see that everything is more difficult because I can not access the DOM

For example in angular 2 create a dynamic form based on fieldset in one day I want to migrate it to lightning but I see that it is complicated I already have part of the code

The problem is that with lightning: input I can not access the value of the fields and try in different ways but I can not access.

Example 1 aura:id

var val = "{! v.obj." + fi.name + "}";

["lightning:input", {"label": fi.label, "name": fi.name, "aura: id": val}]

When using expression the form is generated but something strange happens the input can not be edited is blocked

EXAMPLE 2 try to use class

var val = "fi.name";
arrayForm.push (["lightning:input", {"label": fi.label, "name": fi.name, "class": val}]);

lightning:input does not support add class I don't see in the DOM

EXAMPLE 3 getElementsByName

use document.getElementsByName ("City__c") to get the value.
I get this error "Illegal invocation at NodeList.remoteFunction"

if I do it with ui or normal input text it works but it does not make sense because I have to add all the html and the slds.

helper

    getFieldSetHelper : function(component) {
    var action = component.get("c.getFieldSetApex");

    var objectName=component.get("v.objectName");
    var fieldSetName=component.get("v.fieldSetName");

    var spinner = component.find("mySpinner");
    $A.util.toggleClass(spinner, "slds-hide");

    action.setParams({ "objectName" : objectName,"fieldSetName":fieldSetName });

    action.setCallback(this, function(response) {
        var state = response.getState();
        if (component.isValid() && state === "SUCCESS") {
            var retValues=response.getReturnValue();
            console.log(retValues.obj);

            component.set("v.obj",retValues.obj);
            component.set("v.fieldsLst", retValues.fields);
            this.doInit(component);
        }
        $A.util.toggleClass(spinner, "slds-hide");
    });
    $A.enqueueAction(action);

},
close: function(component) {

},
save: function(component) {

    //var data = document.getElementsByName("City__c");
    //var data = document.querySelector("City__c");
    //var data = component.find("City__c");
    //var data = component.get("v.obj");
    console.log(data);

},
doInit : function(component) {

    var fieldsLst=component.get("v.fieldsLst");

    var obj=component.get("v.obj");

    var arrayForm=[];
    var objectName=component.get("v.objectName");

    for(var fi of fieldsLst){
        var val="{!v.obj."+fi.name+"}";
        var idy=objectName+"_"+fi.name;
        if(fi.type=='STRING'){

    //Here I try in different ways

            arrayForm.push(["lightning:input",{"label":fi.label,"name":fi.name,"class":idy}]);
        }
    }

    $A.createComponents(arrayForm,
                        function(components, status, errorMessage){
                            if (status === "SUCCESS") {
                                var body = component.get("v.body");
                                body.push(components);
                                component.set("v.body", components);
                                component.set("v.obj", components);

                            }
                            else if (status === "INCOMPLETE") {
                                console.log("No response from server or client is offline.")
                            }
                                else if (status === "ERROR") {
                                    console.log("Error: " + errorMessage);
                                }
                        }
                       );


}

    })

component

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" controller="FieldSetToForm">
<aura:handler name="init" value="{!this}" action="{!c.getFieldSetInit}" />
<aura:attribute name="objectName" type="string" default="Account"/>
<aura:attribute name="fieldSetName" type="string" default="accountFS"/>
<aura:attribute name="fieldsLst" type="FieldSetToForm.Inputx[]"/>

<aura:attribute name="obj" type="sobject"/>

<lightning:button variant="brand" label="Close" iconName="utility:download" iconPosition="left" onclick="{!c.close}" />
 <lightning:button variant="brand" label="Save" iconName="utility:download" iconPosition="left" onclick="{!c.save}" />

<input type="hidden" name="valuesData" value="" id="valuesData"/>
{!v.body}



<div class="holder">
    <lightning:spinner variant="brand" size="large" aura:id="mySpinner" alternativeText="..."/>
</div>

controller

({
getFieldSetInit : function(component, event, helper) {
    helper.getFieldSetHelper(component);        
},
close : function(component, event, helper) {
    helper.close(component);
},
save : function(component, event, helper) {
    helper.save(component);
}
})

I Love Angular

I feel that lightning is not mature but I want to use it …

Best Answer

In order to bind to something dynamically, you need a reference. Here is a very basic, no-frills, slightly buggy implementation that demonstrates that this basically works.

Notice how I use component.getReference to dynamically link to a field. Also notice how I can bind to the same field twice, which literally lets me modify the same field more than once in real-time, just to prove that they are real references.

With a little more error checking and fact-gathering, this could indeed be a dynamic form with some basic describe data, etc. Notice how I do not use aura:id at all. I simply bound all the fields to a common object, and I could get and retrieve the values at will (in this case, demonstrated through Object.keys). I could also bind this object to, say, a force:recordData to dynamically save the data back to the server, or send it through a server action in Apex.


<aura:application extends="force:slds">
    <aura:attribute name="accountRecord" type="Account" default="{ 'sobjectType': 'Account' }" />
    <aura:attribute name="edits" type="Aura.Component[]" />
    <aura:attribute name="fieldName" type="String" />
    <aura:attribute name="output" type="String" />

    <aura:handler name="init" value="{!this}" action="{!c.init}" />

    {!v.output}

    <hr />

    {!v.edits}

    <ui:inputText value="{!v.fieldName}" />
    <ui:button press="{!c.addField}" label="Add" />
    <br/>
    <ui:button press="{!c.display}" label="Show Data" />
</aura:application>

({
    init: function(component, event, helper) {
        component.set("v.edits", []);
    },
    addField: function(component, event, helper) {
        var field = component.get("v.fieldName"), 
            obj = component.get("v.accountRecord");
        obj[field] = "";
        component.set("v.accountRecord", obj);
        $A.createComponent(
            "lightning:input", {
                value: component.getReference("v.accountRecord."+component.get("v.fieldName")),
                label: component.get("v.fieldName"),
                type: "Text"
            },
            $A.getCallback(function(newCmp, status, error) {
                var edits = component.get("v.edits");
                edits.push(newCmp);
                component.set("v.edits", edits);
            })
        );
    },
    display: function(component, event, helper) {
        var obj = component.get("v.accountRecord");
        var results = [];
        Object.keys(obj).forEach(function(key) { results.push(key+":"+obj[key])});
        component.set("v.output", results.join('\n'));
    }
})

Here's a demo of me dynamically binding stuff:

Dynamic Component Generation