如何从指令链接功能更新AngularJS模型?

时间:2013-08-15 09:16:49

标签: angularjs angularjs-directive angularjs-scope

我想要实现的是为输入字段添加额外的接口,以便通过单击+和 - 按钮来增加和减少其中的数值。

(实质上它是输入[type = number]字段在chrome上的内容,但我希望这是跨浏览兼容的,并且还可以完全控制所有浏览器的演示文稿。)

视野中的代码:

<input data-ng-model="session.amountChosen" type="text" min="1" class="form-control input-small" data-number-input>

指令代码:

app.directive('numberInput', function() {
return {
    require: 'ngModel',
    scope: true,
    link: function(scope, elm, attrs, ctrl) {

        var currValue = parseInt(scope.$eval(attrs.ngModel)),
            minValue = attrs.min || 0,
            maxValue = attrs.max || Infinity,
            newValue;

        //puts a wrap around the input and adds + and - buttons
        elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');

        //finds the buttons ands binds a click event to them where the model increase/decrease should happen
        elm.parent().find('a').bind('click',function(e){

            if(this.text=='+' && currValue<maxValue) {
                newValue = currValue+1;    
            } else if (this.text=='-' && currValue>minValue) {
                newValue = currValue-1;    
            }

            scope.$apply(function(){
                scope.ngModel = newValue;
            });

            e.preventDefault();
        });


    }
  };

})

这可以通过范围。$ eval(attrs.ngModel)检索当前模型值,但无法设置新值。

后果编辑:这是现在可以使用的代码(如果您不想看到此问题的解决方案)

app.directive('numberInput', function() {
  return {
    require: 'ngModel',
    scope: true,
    link: function(scope, elm, attrs, ctrl) {

        var minValue = attrs.min || 0,
            maxValue = attrs.max || Infinity;

        elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
        elm.parent().find('a').bind('click',function(e){

            var currValue = parseInt(scope.$eval(attrs.ngModel)),
                newValue = currValue;

            if(this.text=='+' && currValue<maxValue) {
                newValue = currValue+1;    
            } else if (this.text=='-' && currValue>minValue) {
                newValue = currValue-1;    
            }

            scope.$eval(attrs.ngModel + "=" + newValue);
            scope.$apply();            

            e.preventDefault();
        });
    }
  };
})

3 个答案:

答案 0 :(得分:28)

应使用

ngModelController方法代替$ eval()来获取和设置ng-model属性的值。

在使用数值计算属性时,不需要

parseInt(),因为$ eval会将值转换为数字。 $ eval应该用于设置变量minValue和maxValue。

指令不需要创建子范围。

$ apply()不需要,因为ngModelController方法(特别是$ render())将自动更新视图。但是,正如@Harijs在下面的评论中指出的那样,如果应用的其他部分也需要更新,则需要$ apply()。

app.directive('numberInput', function ($parse) {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {
            var minValue = scope.$eval(attrs.min) || 0,
                maxValue = scope.$eval(attrs.max) || Infinity;
            elm.wrap('<div class="number-input-wrap"></div>').parent()
                .append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
            elm.parent().find('a').bind('click', function (e) {
                var currValue = ctrl.$modelValue,
                    newValue;
                if (this.text === '+' && currValue < maxValue) {
                    newValue = currValue + 1;
                } else if (this.text === '-' && currValue > minValue) {
                    newValue = currValue - 1;
                }
                ctrl.$setViewValue(newValue);
                ctrl.$render();
                e.preventDefault();
                scope.$apply(); // needed if other parts of the app need to be updated
            });
        }
    };
});

fiddle

答案 1 :(得分:4)

您不希望替换scope.ngModel变量,而是替换该变量后面的值。当您读取链接函数第一行中的值时,您已经这样做了:

 currValue = parseInt(scope.$eval(attrs.ngModel))
 //                         ^^^^^^^^^^^^^^^^^^^^

如果它是一个普通值,如myProperty,你可以在范围内使用它:

 scope[attr.ngModel] = newValue

但如果你有一个表达式值,就像container.myProperty一样,这将不起作用。在这种情况下(这是更通用的类型,你应该瞄准)你必须评估设置到范围的值,如下所示:

scope.$eval(attrs.ngModel + "=" + newValue)

我必须承认,$eval部分有点难看,因为它是带有eval链接的JavaScript,但它可以解决问题。请记住,当您想要设置字符串值时,它可能无法以这种方式工作。然后你必须逃避这些价值观。

希望有所帮助;)

答案 2 :(得分:3)

.directive('numberInput', function ($parse) {
   return {
      require: 'ngModel',
      link: function (scope, elm, attrs){ 
        elm.bind("click", function() {
          var model = $parse(attrs.ngModel);
          var modelSetter = model.assign;
          scope.$apply(function() {
               modelSetter(scope, model);
          });
     }
  }
})