0

I have seen several questions similar to this but my question is slightly different.

I have an object on my controller that looks something like this:

$scope.data = {
  foo: {bar: 1,
        bas: 2},
  biz: 3,
  blat: 4
};

I would like to create an input that can have its ng-model dynamically assigned to any of those values.

<label>Define Model</label>
<input type="text" ng-model="input.model" />

<label>Data for {{input.model}}:</label>
<input type="text" ng-model="{{input.model}}">

Ideally this would allow me to set the "Define Model" input to something like data.foo.bas and the "Data for data.foo.bas" input would have a value of 2.

I am aware I can do something like this:

<label>Define Model</label>
<input type="text" ng-model="input.model" />

<label>Data for {{input.model}}:</label>
<input type="text" ng-model="data[input.model]">

but this would only allow me to access the biz and blat attributes. Does anyone have any ideas how this might be done? Thanks.

Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
yodaisgreen
  • 2,310
  • 3
  • 22
  • 27

3 Answers3

3

you can use $parse

$scope.$watch('input.model', function(newVal) {
    $scope.definedModel = $parse(newVal)($scope.data);
});

here $parse try to match the value from $scope.data and bind it with definedModel

demo plunker

Tasnim Reza
  • 6,058
  • 3
  • 25
  • 30
0

You can do this, but only if you slightly modify the form of your data array, so that each model has its own object (and not just a primitive), with a common structure:

var data = {
  foo: {
    bar: {
      value: 1
    },
    bas: {
      value: 2
    }
  },
  biz: {
    value: 3
  },
  blat: {
    value: 4
  }
};

This is so you can pass the objects around so ngModel can still reference (and so change) the original object.

You then need a function to convert a "path" of like "foo.bas", to return the correct object, modified from this answer:

var getProperty = function(obj, prop) {
  var parts = prop.split('.'),
      last = parts.pop(),
      l = parts.length,
      i = 1,
      current = parts[0];

  if (l === 0) return obj[prop];

  while((obj = obj[current]) && i < l) {
      current = parts[i];
      i++;
  }

  if(obj) {
      return obj[last];
  }
}

And you then have to watch the input.model variable, to make sure that the scope's model variable is set to the right object:

$scope.$watch('input.model', function(name) {
  $scope.model = getProperty(data, name);
});

And finally this is all controlled by inputs that set "input.model" and "model" on the scope:

<input placeholder="Model name" ng-model="input.model" />
<input placeholder="Model value" ng-model="model.value" />

This can be seen in action at this Plunker

Edit: As per the answer from @Reza , you can use $parse instead of the getProperty function above. Using this, the watcher can be

$scope.$watch('input.model', function(name) {
  $scope.model = $parse(name)(data);
});

which can be seen in this Plunker. I have to admit it's a nicer way with less code.

Community
  • 1
  • 1
Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
0

Maybe try recompiling the element with the ng-model attribute you want: https://gist.github.com/djdmbrwsk/9a7004492a709044e597

djdmbrwsk
  • 582
  • 5
  • 10