If the parent is simply concerned about getting notifications on its data changing, you can use an aura:valueChange event in the parent directly:
<aura:component>
<aura:handler name="change" value="{!v.elems}" action="{!c.valueChanged}" />
<c:dummycomponent...>
</aura:component>
If you want to do it as an event from the child, then you've got to go down the path of using aura:registerEvent in the child component, which ultimately means you have to fire the change event manually, in which case you can then use the onchange mechanism from your original post.
In your child component:
<aura:registerEvent name="onchange" type="aura:valueChange" />
And in your controller code:
fireChangeEvent: function(component, event, helper) {
var valueChangeEvent = component.getEvent("onchange");
valueChangeEvent.setParams(
// set desired values here
);
valueChangeEvent.fire();
}
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
Since both parent and child components are in same namespace, you can modify the elements inside child component from parent like below :
However you cannot access elements inside
childComponentElement
since it is oflightning
namespace.A better approach (future proof) would be to use public methods:
Parent.JS:
Child.cmp:
Child.JS: