Reputation: 1703
Rephrasing question for clarification purpose.
Plunkr
View:
<input type="text" ng-model="form['data']['sampleData']">
<input type="text" ng-model="form[bindingPrefix][bindingSuffix]">
<input type="text" ng-model="form[bindingValue]">
Controller:
$scope.form = {
data: {
sampleData: '123'
}
};
$scope.bindingValue = 'data.sampleData';
$scope.bindingPrefix = 'data';
$scope.bindingSuffix = 'sampleData';
Desired effect:
I would expect form[bindingValue]
to yield the effect as form[bindingPrefix][bindingSuffix]
without purposely separating bindingValue
to bindingPrefix
and bindingSuffix
as bindingValue could be a dynamic value such as data.sampleData.childData
, data.sampleData.childData.childChildData
in an array for ng-repeat the model.
P/S: bindingValue is something that pass from Server side and i have no control over it.
========================================================================== Might work from this plunkr over here. Ideally, the view should not be modified.Click here
Upvotes: 0
Views: 248
Reputation: 3721
I created a directive called my-dynamic-model
which is referenced by your <input>
elements. This contains a reference to the scope variable which is $parsed
to refer to the correct $scope.bindingValue
array.
See the attached working plunkr.
You can now specify the hierarchy in $scope.bindingValue
to be as deep as you want and it will properly update that $scope
variable. Just make sure that it's a complete $scope
object hierarchy path.
CODE:
var app = angular.module('app', []);
app.controller('MyController', function($scope) {
$scope.form = {
data: {
sampleData: '1234',
sampleData1: {
sampleData2: '2345'
}
}
};
$scope.bindingValue = ['form.data.sampleData', 'form.data.sampleData1.sampleData2'];
});
app.directive('myDynamicModel', function( $parse, $log ) {
return function( scope, el, attrs ) {
var model = $parse( attrs.myDynamicModel );
var finalModel = $parse(model(scope));
finalModel.assign(scope, finalModel(scope));
scope.$apply();
el.bind('keyup', function() {
finalModel.assign(scope, el.val());
if (!scope.$$phase) scope.$apply();
})
}
});
HTML:
<div ng-controller="MyController">
<input type="text" ng-model="form.data.sampleData" my-dynamic-model="bindingValue[0]" placeholder="Update me">
<input type="text" ng-model="form.data.sampleData1.sampleData2" my-dynamic-model="bindingValue[1]" placeholder="Update me too">
<div>{{ form.data.sampleData }}</div>
<div>{{ form.data.sampleData1.sampleData2 }}</div>
</div>
Upvotes: 1
Reputation: 4076
Even though the path could be variable length, we can reduce the problem to only use path of one variable. This should work as long as you don't break the structure of the data object (or if you do, remember to run this preparation code again).
So we have data
$scope.form = {
data: {
sampleData: '123'//This could be even deeper in the object, can't know for sure
}
};
but the only variable name that we will need to keep the linkage between the sampleData
and the containing object is the last one. "sampleData". All the other property names can be thrown away if we just get a reference to the data
obejct and "sampleData" property name.
In controller:
//Get the path from the server, split it to create an array of property names
var path = 'data.sampleData'.split('.');
//We'll start changing these soon
var prevValue = $scope.form, nextValue;
for(var i = 0; i < path.length - 1; i++){//Note that we are not looping all the way through (-1)!
//Get all the properties one by one
nextValue = prevValue[path[i]];
if(nextValue == undefined){
//This is an error, the data didn't conain the property that it was supposed to.
//It's up to you how to handle this. Doing the following will add the missing properties and keep things working.
nextValue = prevValue[path[i]] = {};
}
//The prevValue will first be $scope.form, then form.data
prevValue = nextValue;
}
//$scope.bindingContainer is a reference to $scope.form.data object
$scope.bindingContainer = prevValue;
//$scope.bindingValue is the last property name, "sampleData"
$scope.bindingValue = path[path.length-1];
In template:
<input type="text" ng-model="bindingContainer[bindingValue]">
And everything should just work (again, as long as you don't change $scope.form.data = somethingElse
).
We are cheating a bit, of course, because now the template does not reference to the original $scope.form
object at all. It shouldn't matter though, because it has a reference to the data object and its property "sampleData", so as long as $scope.form
is referencing to the same data
object we've got all we need.
Upvotes: 1
Reputation: 3721
In your controller, create a scope object like this:
$scope.data = {
sampleData: {
childSampleData: null
},
anotherItem: null,
moreData: {
Child1: null,
Child2: null
}
}
Your HTML should reference the scope object like this:
<input type="text" ng-model="data.sampleData.childSampleData">
<input type="text" ng-model="data.anotherItem">
<input type="text" ng-model="data.moreData.Child1">
<input type="text" ng-model="data.moreData.Child1">
Unfortunately, you can't reference an ngModel
in the way that your code shows. So it's incorrect to say ng-model="form[bindingPrefix][bindingSuffix]"
because you can't access the form
object here. But you can access child objects using dot notation as I did in the HTML.
If you're not sure which ngModel
needs to be updated, you should instead use a function like this:
<input type="text" ng-model="item1" ng-change="updateModel()">
$scope.updateModel = function() {
$scope.data[bindingPrefix][bindingSuffix] = $scope.item1;
}
Upvotes: 0
Reputation: 3975
Or this might be along the lines your currently writing with angular, also here is an excellent article on controller as syntax.
function ExampleCtrl($scope) {
$scope.bindingValue = data.sampleData;
$scope.bindingPrefix = 'data';
$scope.bindingSuffix = 'sampleData';
}
// Controller or Controller as syntax is a reference the controller just a short-hand name.
<body ng-app="ExampleApp">
<div class="example" ng-controller="ExampleCtrl">
<input type="text" ng-model="bindingValue">
</div>
</body>
Try something like this, maybe some syntax differences:
function ExampleCtrl() {
var ctrl = this;
ctrl.bindingValue = data.sampleData;
ctrl.bindingPrefix = 'data';
ctrl.bindingSuffix = 'sampleData';
}
<body ng-app="ExampleApp">
<div class="example" ng-controller="ExampleCtrl as ctrl">
<input type="text" ng-model="ctrl.bindingValue">
</div>
</body>
Upvotes: 0