Jan-Paul Kleemans
Jan-Paul Kleemans

Reputation: 828

Angular ng-model dynamic getters and setters

I want to use ng-model with an external model-service. This model has two methods: getValue(variable) and setValue(variable).

So in my html I want to be able to do:

<input type="text" ng-model="balance">

Note: balance is not defined on $scope in my controller. And because we are dealing with more then 4000 different variables, I don't want to define them all on $scope.

And then on change it must call the setValue() method of the model. So in my controller I would like to have something like:

$catchAllGetter = function(variable) { // e.g. variable = 'balance'
     var value = Model.getValue(variable);
     return value;
}

$catchAllSetter = function(variable, value) { // called on change
     Model.setValue(variable, value);
}

Is something like this possible with Angular?

Upvotes: 5

Views: 3260

Answers (7)

Ivan Poliakov
Ivan Poliakov

Reputation: 2475

You can evaluate your model function dynamically, e.g.

<input type="text" ng-model="myModel(var)">

And in the controller:

$scope.myModel = function(var) {
  return function(newValue) {
     // this is a regular model function but you can use 'var' here
     ...
  }
}

Upvotes: 0

ashish
ashish

Reputation: 878

ES5 Object properties to the rescue:

Object.defineProperty($scope, 'balance', {
  enumberable: true,
  get: function () {
    // -- call your getter here
  },
  set: function (val) {
    // -- call the setter here
  }
});

This is native Javascript, so it does not get faster than this.

Upvotes: 0

Manish Kr. Shukla
Manish Kr. Shukla

Reputation: 4477

My approach is similar to @Dan Prince, but the implementation differs a bit

Create a directive, that accepts name of the model variable, and then inject your model service in the directive itself to perform the getting and setting.

Edit : As specified by @Werlang, writing an attribute that replaces ngModel will refrain you from features like validation, formatting, debounced update, ng-change etc. So instead of writing a replacement, we will instead wire up a supplementary attribute

.

    app.directive('dynamicInput', function() {
      return {
        restrict: 'A',
        link: function(scope, el, attr) {
              scope.variableName = angular.copy(attr.ngModel); // Saving the variable name

              scope[attr.ngModel] = (attr.ngModel + '_1'); // Setting a dummy value in the scope variable.
              // In your case it will look something like scope[attr.ngModel] = Model.getValue(attr.ngModel);

                scope.$watch(attr.ngModel, function(newValue, oldValue) {

                  console.log(scope.variableName + " ==> " + newValue);

                  //Model.setValue(scope.variableName, newValue);

              });

        }
      };
    })

Then in your HTML :

    <input ng-model='balance' dynamic-input />

Upvotes: 3

New Dev
New Dev

Reputation: 49590

ngModel supports getter and setters. Here's how it works:

<input ng-model="balance" ng-model-options="{ getterSetter: true }">

This works if balance is a getter/setter function:

$scope.balance(100);      // sets 100
var b = $scope.balance(); // returns 100

You don't need to expose each variable on the scope - you could just expose the Model service that you use in your example:

$scope.Model = Model;

then, in the View, bind to whatever property you need:

<input ng-model="Model.balance" ng-model-options="{ getterSetter: true }">

Upvotes: 1

Andr&#233; Werlang
Andr&#233; Werlang

Reputation: 5964

Have all your variables in an object array:

[
  {key: "Variable 1", value: 1, kind: "number"},
  {key: "Variable 2", value: "some text", kind: "text"},
  {key: "Variable 3", value: new Date(), kind: "date"}
]

Then in your view you shall create them with the help of an ng-repeat:

<div ng-repeat="variable in myVariables">
    <input type="{{variable.kind}}" ng-model="variable.value" ng-change="changed(variable)">
</div>

If you need to update your external service, implement a method changed(variable) in your controller.

Upvotes: 0

saygon91
saygon91

Reputation: 116

look at example, i created for you. I hope I have understood you correctly

$scope.$watch('variables', function(newValue) {
  console.log("triggers on variables change");
  angular.forEach(newValue, function(value, key) {
    Model.setValue(key, value);
  });  
}, true);

Upvotes: 1

Dan Prince
Dan Prince

Reputation: 29999

You can create a new directive which implements this behaviour.

<input model-getter='getFn()' model-setter='setFn($value)' />

This would be fairly straightforward to implement:

app.directive('modelGetter', function() {
  return {
    restrict: 'A',
    scope: {
      get: '&modelGetter',
      set: '&modelSetter'
    },
    link: function(scope, element) {
      element.val(scope.get());
      element.on('change', function() {
        var val = element.val();
        scope.set({ $value: val });
      });
    }
  };
})

Upvotes: 1

Related Questions