Basically, the setCallback()
function sets a function to run after the action finishes. You might be wondering why we can't just call getReturnValue() on the action right after enqueuing it and why the setCallback() function is even necessary. Let's see...
LightningApplication
<aura:application >
<c:LightningComponent />
</aura:application>
ServerSideController
public class ServerSideController {
@AuraEnabled
public static String getAnAccountName(){
list<Account> alist = [SELECT Name FROM Account LIMIT 1];
if ( alist.size() == 0 ) {
AuraHandledException e = new AuraHandledException('No Accounts Found');
e.setMessage('No Accounts Found');
throw e;
}
return alist[0].Name;
}
}
LightningComponent
<aura:component controller="ServerSideController">
<ui:button press="{!c.getAName}" label="Get A Name"/>
</aura:component>
LightningComponentController Take 1
({
getAName : function(component,event,helper){
var action = component.get("c.getAnAccountName");
$A.enqueueAction(action);
console.log(action.getReturnValue());
console.log(action.getState());
}
})
Here, we get the action, we enqueue it, and get the return value right after. The problem with this is that the action has only been enqueued. It hasn't actually returned with a value yet.
So, if the value hasn't been returned yet, why not just keep checking the state of the action to see when it is finished, and then get the value after?
LightningComponentController Take 2
({
getAName : function(component,event,helper){
var action = component.get("c.getAnAccountName");
$A.enqueueAction(action);
while (action.getState() != "SUCCESS" && action.getState() != "ERROR" && action.getState() != "ABORTED"){
console.log("waiting...");
}
console.log(action.getReturnValue());
}
})
This doesn't work out so well…
Clearly we are checking the status of action too often. What if we try checking the value less often, say once per second:
LightningComponentController Take 3
getAName : function(component,event,helper){
var action = component.get("c.getAnAccountName");
$A.enqueueAction(action);
var intervalId;
var checkTheAction = function(){
console.log(action.getState());
if (action.getState() === "SUCCESS" || action.getState() === "ERROR" || action.getState() === "ABORTED"){
console.log(action.getReturnValue());
window.clearInterval(intervalId);
}
};
intervalId = window.setInterval(checkTheAction,1000);
}
It works now, but then what would the optimal value be for the interval? Rather than leave us to try to configure this ourselves, luckily we have callbacks. They can get us the value of the action right after it returns, without bogging the computer down while it's waiting.
LightningComponentController Take 4
({
getAName : function(component,event,helper){
var action = component.get("c.getAnAccountName");
action.setCallback(this,function(response){console.log(response.getState());console.log(response.getReturnValue());});
$A.enqueueAction(action);
}
})
The setCallback() function has three possible parameters. There are the two that you are already using, and also from the documentation:
setCallback() has a third parameter that registers the action state
that will invoke the callback. If you don't specify the third argument
for setCallback() , it defaults to registering the SUCCESS and ERROR
states. To set a callback for another state, such as ABORTED , you can
call setCallback() multiple times with the action state set explicitly
in the third argument. For example:
action.setCallback(this, function(response) { ...}, "ABORTED");
The action states that can be used for the third parameter being:
SUCCESS The action executed successfully
ERROR The server returned an error
ABORTED The action was aborted
ALL -- the same as undefined, covers SUCCESS, ERROR and INCOMPLETE
INCOMPLETE
There are two other possible values for the action state, but they cannot be used as the third parameter for the setCallback() function:
NEW The action was created but is not in progress yet
Something has gone wrong. Action.setCallback(): Invalid callback name 'NEW'
RUNNING The action is in progress
Something has gone wrong. Action.setCallback(): Invalid callback name 'RUNNING'
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:
Best Answer
To edit a contact you need to pass JSON string of your contact. Then in your controller deserialize it as below.
Apex Controller
Popup component controller.js make changes in your saveContact() as below
this code is to create and update the contact. Hope it solves your issue.