Here is my scenario:
cols = [{field="product.productId"},{field="product.productPrice"}];
data = {products:[{product:{productId:1,productPrice:10}, {product:{productId:2, productPrice:15}}]}
This is what I want to achieve:
<div ng-repeat="product in data.products">
<div ng-repeat="col in cols">
<input type="text" ng-model="product[col.field]"></>
</div>
</div>
The problem arises when 'col.field' contains multiple elements. The correct way to use ng-model would be "product[some][deep][field]" to allow for flexibility with changing data and columns. Although this approach worked for a specific case, making it generic presents challenges.
My attempts at creating a generic solution included:
Recompiling the 'input' element. While this generated the correct HTML with ng-model="product['some']['deep']['field']", the binding was not successful. It seems there might be an issue with the scope during compilation. Adding attributes like ng-init="hello='Hey'" and ng-model="hello" did work properly, indicating a misunderstanding regarding scope here.
compile: function (templateElement) { templateElement[0].removeAttribute('recursive-model'); templateElement[0].removeAttribute('recursive-model-accessor'); return { pre: function (scope, element, attrs) { function convertDotToMultiBracketNotation(dotNote) { var ret = []; var arrayOfDots = dotNote.split('.'); for (i = 0; i < arrayOfDots.length; i++) { ret.push("['" + arrayOfDots[i] + "']"); } return ret.join(''); } if (attrs.recursiveModel && attrs.recursiveModelAccessor) { scope[scope.recursiveModel] = scope.ngModel; element[0].setAttribute('ng-model', scope.recursiveModel + convertDotToMultiBracketNotation(scope.recursiveModelAccessor)); var res = $compile(element[0])(scope); console.info('new compiled element:', res); return res; } } } }
Adjusting the NgModelController for formatting and parsing. By setting the entire 'row' object into ng-model and utilizing formatter/parser to manipulate the desired field, everything seemed to work until the field was cleared. At that point, modelCtrl.$modelValue appeared to be wiped out entirely. In summary - console.log displayed:
Setting field to val 'Text' on row [object]
Setting field to val 'Tex' on row [object]
Setting field to val 'Te' on row [object]
Setting field to val 'T' on row [object]
Setting field to val '' on row [object]
Setting field to val 'A' on row undefined
link: function (scope, element, attrs, ctrls) {
if(ctrls[2] && scope.recursiveModelAccessor){
var modelCtrl = ctrls[2];
modelCtrl.$formatters.push(function (inputValue) {
function getValue(object, string){
var explodedString = string.split('.');
for (i = 0, l = explodedString.length; i < l; i++) {
object = object[explodedString[i]];
}
return object;
};
function getValueRecursive (row, field) {
if (field instanceof Array) {
var ret = [];
for (var i = 0; i < col.field.length; i++) {
ret.push(getValue(row, field[i]));
}
return ret.join('/');
} else {
return getValue(row, field);
}
};
return getValueRecursive(modelCtrl.$modelValue, scope.recursiveModelAccessor);
});
modelCtrl.$parsers.push(function (inputValue) {
function setValueRecursive (row, field, newValue) {
if (field instanceof Array) {
var firstField = field.shift();
if(field.length==1){
field = field[0];
}
setValueRecursive(row[firstField], field, newValue);
} else {
console.log("Setting "+field+" to val:"+newValue+" on row:"+row);
row[field]=newValue;
}
};
setValueRecursive(modelCtrl.$modelValue, scope.recursiveModelAccessor.split('.'), modelCtrl.$viewValue);
return modelCtrl.$modelValue;
});