在Angular中,在* ngModel绑定输入的`value`

时间:2015-10-23 01:49:19

标签: angularjs data-binding angular-ngmodel

在我的Angular应用程序中,我定义了一个包装<input type="range">的自定义滑块指令。我的自定义指令支持ngModel将滑块的值绑定到变量。自定义指令还需要fraction-size属性。它对值进行计算,然后使用结果设置包装step的{​​{1}}值。

当我将这两个功能组合在一起时,我发现了一个错误 - <input>和我的绑定属性值。它们以错误的顺序运行。

这是一个演示:

ngModel
angular.module('HelloApp', []);

angular.module('HelloApp').directive('customSlider', function() {
    var tpl = "2 <input type='range' min='2' max='3' step='{{stepSize}}' ng-model='theNum' /> 3";
    
    return {
        restrict: 'E',
        template: tpl,
        require: 'ngModel',
        scope: {
            fractionSize: '='
        },
        link: function(scope, element, attrs, ngModelCtrl) {
            scope.stepSize = 1 / scope.fractionSize;
            
            scope.$watch('theNum', function(newValue, oldValue) {
                ngModelCtrl.$setViewValue(newValue);
            });
            
            ngModelCtrl.$render = function() {
                scope.theNum = ngModelCtrl.$viewValue;
            };
        }
    };
});

angular.module('HelloApp').controller('HelloController', function($scope) {
	$scope.someNumber = 2.5;
});

上面的滑块应该从中间开始,表示2.5。但它实际上从右边开始(代表3)。滑块固定自身,如果您拖动它,则允许值为2.5,或者如果您通过编辑文本字段更改其边界值。

我已经弄清楚为什么在演示中发生这种情况 - 我只是不知道如何修复它。目前,当一个新的自定义滑块动态添加到页面时,包装的<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script> <div ng-app="HelloApp" ng-controller="HelloController"> <h3>Custom slider</h3> <custom-slider ng-model="someNumber" fraction-size="10"></custom-slider> <h3>View/edit the slider’s value</h3> <input ng-model="someNumber"></input> </div>的{​​{1}}未定义,默认为1.然后<input>将值设置为2.5 - 但是step为1,ngModel中的值四舍五入为3.最后,step设置为input - 对于它来说太晚了。

如何在 step设置输入0.1之前确保step属性的值绑定

在上面的示例中,滑块位于页面加载页面上。在我的真实代码中,动态添加了多个新的滑块。每当添加它们时,它们都应以正确的顺序绑定它们ngModelvalue

我不喜欢的解决方法

解决方法是在模板中对step进行硬编码,而不是动态设置。如果我这样做,我的自定义滑块指令将毫无用处,我会删除它并直接使用value

step

如果我使用它,滑块的<input>设置正确,没有舍入。但是我想保留我的自定义指令<input type="range" min="2" max="3" step="0.1" ng-model="someNumber"> ,以便抽象出value的计算。

我试过的东西不起作用

  • 更改模板中customSliderstep属性的顺序没有任何效果。
  • 我开始尝试添加step函数,而不仅仅是ng-model函数。但我无法在compile中设置link,因为在编译阶段stepSize不可用。
  • 我尝试将指令的compile功能拆分为预链接和后链接功能。但是,无论我在scope还是在link中设置scope.stepSize,该页面都会像以前一样工作。
  • 在设置pre之后立即手动拨打post只会抛出scope.$digest()
  • 我认为自定义指令优先级不适合这个,因为只涉及一个自定义指令。我对scope.stepSize的值的绑定不是自定义指令,它只是原始Error: [$rootScope:inprog] $digest already in progress - 绑定。我认为绑定step是一个足够简单的任务,它不应该包含在它自己的指令中。

3 个答案:

答案 0 :(得分:2)

link函数中,通过添加step属性来操作DOM。

您还可以通过将ngModel放入theNum: '=ngModel'来简化与外部scope的绑定。

var app = angular.module('HelloApp', []);

app.directive('customSlider', function () {
    var tpl = "2 <input type='range' min='2' max='3' ng-model='theNum' /> 3";
    
    return {
        restrict: 'E',
        template: tpl,
        require: 'ngModel',
        scope: {
            fractionSize: '=',
            theNum: '=ngModel'
        },
        link: function (scope, element, attrs, ngModelCtrl) {
            var e = element.find('input')[0];
            
            var step = 1 / scope.fractionSize;
            e.setAttribute('step', step);
            
            scope.$watch('fractionSize', function (newVal) {
                if (newVal) {
                    var step = 1 / newVal;
                    e.setAttribute('step', step);
                }
            });
        }
    };
});

angular.module('HelloApp').controller('HelloController', function($scope) {
    $scope.someNumber = 2.5;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>

<div ng-app="HelloApp" ng-controller="HelloController">
    <h3>Custom slider</h3>
    
    <custom-slider ng-model="someNumber" fraction-size="10"></custom-slider>
    
    <h3>View/edit the slider’s value</h3>
    <input ng-model="someNumber"></input> {{ someNumber }}
</div>

答案 1 :(得分:2)

进一步采用@pixelbits' answer直接DOM操作,我根本不会在内部输入上有另一个ng-model,而是直接在输入上设置/获取属性

  • 从输入设置/获取值时,您需要考虑一个抽象级别。原始DOM事件和元素。

  • 因此,您不仅限于Angular允许这些元素的内容(实际上:它似乎无法在没有解决方法的情况下处理您的用例)。如果浏览器允许它在范围输入上,您可以这样做。

  • 在一个UI小部件上播放2个ngModelControllers,设置一个值可能会让人感到有些困惑,至少对我而言!

  • 如果您需要/想要使用解析器和验证器,您仍然可以访问外部ngModelController管道及其所有功能。

  • 您可以节省额外的观察员(但这可能是微观/过早优化)。

请参阅下面的示例。

&#13;
&#13;
angular.module('HelloApp', []);

angular.module('HelloApp').directive('customSlider', function() {
    var tpl = "2 <input type='range' min='2' max='3' /> 3";
    
    return {
        restrict: 'E',
        template: tpl,
        require: 'ngModel',
        scope: {
            fractionSize: '='
        },
        link: function(scope, element, attrs, ngModelCtrl) {
            var input = element.find('input');          
            input.prop('step', 1 / scope.fractionSize);

            input.on('input', function() {
              scope.$apply(function() {
                ngModelCtrl.$setViewValue(input.prop('value'));
              });
            });

            ngModelCtrl.$render = function(value) {
               input.prop('value', ngModelCtrl.$viewValue);
            };
        }
    };
});

angular.module('HelloApp').controller('HelloController', function($scope) {
    $scope.someNumber = 2.5;
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>

<div ng-app="HelloApp" ng-controller="HelloController">
    <h3>Custom slider</h3>
    
    <custom-slider ng-model="someNumber" fraction-size="10"></custom-slider>
    
    <h3>View/edit the slider’s value</h3>
    <input ng-model="someNumber"></input>
</div>
&#13;
&#13;
&#13;

也可在http://plnkr.co/edit/pMtmNSy6MVuXV5DbE1HI?p=preview

获取

答案 2 :(得分:0)

我不确定这是否是正确的方法,但是向主控制器添加超时可以解决问题。

$timeout(function() {
    $scope.someNumber = 2.5;
});

编辑:虽然它起初看起来像是一个肮脏的黑客,但请注意,(最终)$ scope变量通常比模板分配更常见,因为需要额外的ajax调用来检索值。