Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have an array containing variable names, example:

var names = ['address.street','address.city'];

I want to create input fields out of these, and I'm using AngularJS. No big deal:

<div ng-repeat="n in names">
    <input type="text" ng-model="data[n]" />
</div>

The resulting $scope.data object is:

{
    "address.street" : ...,
    "address.city" : ...
}

Which, by the way, is not exactly what I'm trying to achieve. Is there a syntax that could lead me to an object as the following one as result?

{
    "address" : {
        "street" : ...,
        "city" : ...
    }
}

Please consider that I can have even more than one level of nesting, this is just an example.

share|improve this question
 
Do you mean your $scope.data is { "address" : { "street" : ..., "city" : ... } }. Its not clear –  Maxim Shoustin Nov 21 '13 at 11:52
 
I'm trying to make it like you wrote –  Lorenzo Marcon Nov 21 '13 at 11:58
add comment

2 Answers

up vote 1 down vote accepted

I do not think models should be accessed this way.

However, this was curious question and the solution is a bit fun.

The problem is that ng-model requires a reference and thought Javascript sends modifiable copies of objects, it does not have pass-by-reference semantics and we cannot just pass a string to ng-model.

However, arrays and objects do have this property. Hence, the solution is to return an array whose 0th element will be the reference for ng-model. This is also the hacky part since all your objects are now arrays with '1' element.

The other solution would be to return an object for each case instead of 1 element array.

Solution using embedded objects

Here is the solution using an embedded object: http://plnkr.co/edit/MuC4LE2YG31RdU6J6FaD?p=preview which in my opinion looks nicer.

Hence, in your controller:

$scope.getModel = function(path) {
  var segs = path.split('.');
  var root = $scope.data;

  while (segs.length > 0) {
    var pathStep = segs.shift();
    if (typeof root[pathStep] === 'undefined') {
      root[pathStep] = segs.length === 0 ? { value:  '' } : {};
    }
    root = root[pathStep];
  }
  return root;
}

And in your template:

<p>Hello {{data.person.name.value}}!</p>
<p>Address: {{data.address.value}}</p>
<input ng-model="getModel('person.name').value" />
<input ng-model="getModel('address').value" />

Solution using single element array

Here is the shortest (albeit hacky) solution I could come up with: http://plnkr.co/edit/W92cHU6SQobot8xuElcG?p=preview

Hence, in your controller:

$scope.getModel = function(path) {
  var segs = path.split('.');
  var root = $scope.data;

  while (segs.length > 0) {
    var pathStep = segs.shift();
    if (typeof root[pathStep] === 'undefined') {
      root[pathStep] = segs.length === 0 ? [ '' ] : {};
    }
    root = root[pathStep];
  }
  return root;
}

And in your template:

<p>Hello {{data.person.name[0]}}!</p>
<p>Address: {{data.address[0]}}</p>
<input ng-model="getModel('person.name')[0]" />
<input ng-model="getModel('address')[0]" />
share|improve this answer
 
I see the effort you made, and thanks for the detailed explanation. This is close, but the output data object would be different than the format I'm looking for (you can see it in my question). Given the restrictions I have, maybe yours is the only viable way, in this case I probably have to post-process again the resulting object to transform it in the format I need. –  Lorenzo Marcon Nov 21 '13 at 16:59
 
@LorenzoMarcon Thanks for accepting the answer. I do realise that this is not the solution you wanted, but this was an interesting problem to think about anyway. You have my (+1) for the fun. :) –  musically_ut Nov 22 '13 at 9:53
add comment

If you can restructure your models, you can simply do like this:

Controller

$scope.names = {
    "address":[
        "street",
        "city"
    ]
};

$scope.data = {
    address:{
        street:"",
        city:""
    }
};

HTML

<div ng-repeat="(key, values) in names">
    <div ng-repeat="value in values">
        <input type="text" ng-model="data[key][value]" />
    </div>
</div>
share|improve this answer
 
From the question: "Please consider that I can have even more than one level of nesting, this is just an example." –  musically_ut Nov 21 '13 at 12:09
 
We would have nested ng-repeats in that case. –  CodeHater Nov 21 '13 at 12:11
 
Indeed, there are better ways of approaching the problem, but this problem itself is a curious one. :-) –  musically_ut Nov 21 '13 at 12:15
 
Thanks for the answer, I was looking for a way to do this without changing the initial model array. Furthermore, some elements could have no nesting (e.g. name) some other may have one or more (e.g. address.street), but I don't know it in advance, so this solution is quite limited for my case –  Lorenzo Marcon Nov 21 '13 at 16:55
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.