[SalesForce] Why won’t the Lightning Component Attributes store entered value

2/15/2017 Update

Per comment from Doug in one of the answers this is an issue with Locker service. I verified that with Locker disabled is works as expected…

In Spring 17 as of today, with locker enabled it is still not working however

Update

So, since we get an internal error when passing classes/objects to apex controllers AND if values are not set in the controller when the component requests them then there is no way to pass those properties back (see below), I am left with this mess for every non string property

    @AuraEnabled public String endDate {
        get {
            return endDate == null ? '' : endDate;
        }
        set;
    }
    private date endDateValue {
        get {
            if (string.isBlank(endDate)) return null;

            return date.valueOf(endDate);
        }
        set {
            if (value == null) endDate = null; else endDate = string.valueOf(value);

        }
    }

Should not have to go through all that to use a simple class property in a component…Ugh

Original discussion

So I have been fighting with this for a while. Maybe my mindset is wrong and what I am doing is correct but it just seems like a lot of extra work.

Scenario

  1. Component with attribute set to a type of a custom class
  2. Input boud to a property in that class

Now is it too much to as that when the data is entered in the input that it is reflected in the component? The actual input keeps the value but the attribute is never set….

It makes it extremely difficult to pass data around. Right not I am manually setting the attribute properties based on the value input in the component.

So the examples:

The first input when data is input always logs as:

{}

The second input the data logs correctly when value are input into the field.

enter image description here

The weird part is that accessing the actual property outputs as expected:

console.log(component.get('v.myCustomClass.myTestString'));

outputs

a

as shown in the second debug line in the image

enter image description here

Custom Class

public class myProblemClass {
    @AuraEnabled public string myTestString {get;set;}

    public myProblemClass(){

    }

    public myProblemClass(String setDefault){
        mytestString = setDefault;
    }
}

Apex Controller

public class myProblemController {

    @AuraEnabled
    public static myProblemClass newProblemClass(){
        return New myProblemClass();
    }

    @AuraEnabled
    public static myProblemClass newProblemClassInitial(){
        return New myProblemClass('initial');
    }

}

Lightning Component

<aura:component description="myProblemComponent" controller="myProblemController">

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

    <aura:attribute name="myCustomClass" type="myProblemClass"/>
    <aura:attribute name="myCustomClassInitial" type="myProblemClass"/>

                <div class="slds-form-element">
                    <label class="slds-form-element__label" for="text-input-01">Input Label</label>
                    <div class="slds-form-element__control">
                        <ui:inputText aura:id="firstInput" updateOn="keyup" keyup="{!c.logMe}" requiredIndicatorClass="slds-required"
                                      required="true" value="{!v.myCustomClass.myTestString}" placeholder="Enter Me"
                                      class="slds-input"/>
                    </div>
                </div>

                <div class="slds-form-element">
                    <label class="slds-form-element__label" for="text-input-01">Input Label</label>
                    <div class="slds-form-element__control">
                        <ui:inputText updateOn="keyup" keyup="{!c.logMe}" requiredIndicatorClass="slds-required"
                                      required="true" value="{!v.myCustomClassInitial.myTestString}" placeholder="Enter Me"
                                      class="slds-input"/>
                    </div>
                </div>

</aura:component>

Component Controller

({
    doInit: function (component, event, helper) {

        var action = component.get("c.newProblemClass");
        var actionInitial = component.get("c.newProblemClassInitial");

        action.setCallback(this, function (response){
            component.set('v.myCustomClass',response.getReturnValue());
        })

        actionInitial.setCallback(this, function (response){
            component.set('v.myCustomClassInitial',response.getReturnValue());
        })

        $A.enqueueAction(action);
        $A.enqueueAction(actionInitial);

    },
    logMe: function(component,event,helper){
        console.log(JSON.stringify(component.get('v.myCustomClass')));
        console.log(component.get('v.myCustomClass.myTestString'));
        console.log(JSON.stringify(component.get('v.myCustomClassInitial')));
    }
})

Now, I get that in the first attribute that I am not setting the value of the class property thus the Java object does not contain anything. However, when I set a value in the first Input that is Bound to the class property I would expect it to set it.

The questions:

  1. Are my expectations incorrect
  2. If #1 is true then is setting the value of the attribute as follows the correct way. Keeping in mind my real class has 20-30 properties so that is a lot of extra code to write just to set the values

     component.get('v.myCustomClass').myTestString = 
          component.find('firstInput').get('v'value');
    

So as Ashwani points out, the binding of the value on the input get set properly so you can access the property directly. So why does it still output as {} when logging the attribute.

Now if I do this:

    component.get('v.myCustomClass').myTestString = 'ABCDEF';
    console.log(JSON.stringify(component.get('v.myCustomClass')));

then JSON of the attribute outputs the correct JSON string (see 4th debug line). So I can set it in the JS but there is no way to set it in using the actual Input field?? But you can access the property directly when set by the input field v.myCustomClass.mytestString….Confusing…..

enter image description here

Best Answer

Code is working as expected.

In the above approach there is basic problem. Lightning does not initialize any variable itself.

In controller testString is not defined so, it is not even getting set and result {}:-

public class myProblemClass {
    @AuraEnabled public string myTestString {get;set;}

    public myProblemClass(){
    }

    public myProblemClass(String setDefault){
        mytestString = setDefault;
    }
}

Define variable in no argument constructor and it will start working:-

public myProblemClass(){
   mytestString = '';
}

There is a framework logic. Only you know there is myCustomClass.myTestString but framework doesn't know.

Take the example: set the input as:

 <ui:inputText aura:id="firstInput" updateOn="keyup" keyup="{!c.logMe}" requiredIndicatorClass="slds-required"
                                      required="true" value="{!v.myCustomClass.myTestStringNOTEXIST}" placeholder="Enter Me"
                                      class="slds-input"/>

and access it as

console.log( ' 1.1 '+component.get('v.myCustomClass.myTestStringNOTEXIST'));

You will get the value but it is not the variable in controller.

Related Topic