Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using expressions in ngModel in Angular.js

Giving the code inside of my controller:

$scope.entity = {
  firstName: 'Jack',
  lastName: 'Bauer',
  location: {
    city: 'New York'
  }
};
$scope.path = 'location.city';

How do I dynamically bind ngModel to the property of the entity specified by path?

I've tried something like this, but to no avail:

<input ng-model="'entity.' + path">
like image 721
Slava Fomin II Avatar asked Oct 15 '15 16:10

Slava Fomin II


2 Answers

After reading and using Sander Elias' answer, I was using this, but ran into another problem.

When combining his result with ng-required="true" in the <input> you could not empty the field, because when the field would be empty, the newVal is passed as undefined.

After some more research, I found an isssue on GitHub that addresses and solves this problem.

Here is what Sander's and the GitHub answer combined look like:

$scope.propertify = function (string) {
    var property = $parse(string);
    var propAssign = property.assign;
    return function (newVal) {
        if (arguments.length) {
            newVal = angular.isDefined(newVal)?  newVal : '';
            propAssign($scope, newVal);
        }
        return property($scope);
    };
};

The argument.length reflects the number of values that are passed to the getter/setter and will be 0 on a get and 1 on a set.

Besided that, I added the angular.isDefined() as Sumit suggested in a comment to also save false and empty ("") values.

Here is an updated Plunker

like image 173
Jannis Nexor Ianus Betz Avatar answered Oct 01 '22 22:10

Jannis Nexor Ianus Betz


Slava, I'm not too sure if this is a good idea to begin with. But anyhow, You need to make your model getterSetter aware by adding this property to your input ng-model-options="{ getterSetter: true }. Then you need a function in your controller that builds a getterSetter out of a sting.

<input type="text" ng-model="propertify('entity.' + path)" ng-model-options="{ getterSetter: true }">

That's how the resulting template would look.

Luckily angular has an $parse service that makes this a lot easier. so something like this would need to be in your controller, or even better in a injected service.

  $scope.propertify = function (string) {
      var p = $parse(string);
      var s = p.assign;
      return function(newVal) {
          if (newVal) {
              s($scope,newVal);
          }
          return p($scope);
      } ;
  };

That will return a getter-setter function that handles this for you. see it in action in this plunk

like image 35
Sander Elias Avatar answered Oct 01 '22 23:10

Sander Elias