我可以使用隔离范围的ng模型吗?
我正在创build简单的ui-datetime指令。 它将javascript Date对象分成_date,_hours和_minutes部分。 _date使用jquery ui datepicker,_hours和_minutes – 数字input。
angular.module("ExperimentsModule", []) .directive("uiDatetime", function () { return { restrict: 'EA', replace: true, template: '<div class="ui-datetime">' + '<input type="text" ng-model="_date" class="date">' + '<input type="number" ng-model="_hours" min="0" max="23" class="hours">' + '<input type="number" ng-model="_minutes" min="0" max="59" class="minutes">' + '<br />Child datetime1: {{datetime1}}' + '</div>', require: 'ngModel', scope: true, link: function (scope, element, attrs, ngModelCtrl) { var elDate = element.find('input.date'); ngModelCtrl.$render = function () { var date = new Date(ngModelCtrl.$viewValue); var fillNull = function (num) { if (num < 10) return '0' + num; return num; }; scope._date = fillNull(date.getDate()) + '.' + fillNull(date.getMonth() + 1) + '.' + date.getFullYear(); scope._hours = date.getHours(); scope._minutes = date.getMinutes(); }; elDate.datepicker({ dateFormat: 'dd.mm.yy', onSelect: function (value, picker) { scope._date = value; scope.$apply(); } }); var watchExpr = function () { var res = scope.$eval('_date').split('.'); if (res.length == 3) return new Date(res[2], res[1] - 1, res[0], scope.$eval('_hours'), scope.$eval('_minutes')); return 0; }; scope.$watch(watchExpr, function (newValue) { ngModelCtrl.$setViewValue(newValue); }, true); } }; }); function TestController($scope) { $scope.datetime1 = new Date(); }
的jsfiddle
在github上: https : //github.com/andreev-artem/angular_experiments/tree/master/ui-datetime
据我所知 – 创build新组件时的最佳做法是使用隔离范围。
当我试图使用孤立的范围 – 没有任何工作。 ngModel。$ viewValue === undefined。
当我试图使用新的范围(我的例子,不是很好的变种imho) – ngModel使用新创build的范围上的值。
当然,我可以通过“=expression式”( 示例 )创build指定隔离范围和ngModel值。 但我认为,使用ngModelController是一个更好的做法。
我的问题:
- 我可以使用ngModelController隔离作用域吗?
- 如果不可能哪个解决scheme更好地创build这样的组件?
replacescope: true
的scope: { datetime1: '=ngModel'}
在你的第一个提琴似乎很好 – 小提琴 。 不幸的是,你的“榜样”小提琴的链接被打破,所以我不知道你在那里试过什么。
所以,ngModelController似乎可以与隔离范围一起使用。
这里有一个较小的小提琴,它在HTML /视图中使用ng-model,一个隔离范围,以及链接函数fiddle中的$ setViewValue。
更新 :我刚刚发现了一些相当有趣的东西:如果隔离范围属性被赋予不同的名称 – 例如,说dt1而不是datetime1 – scope: { dt1: '=ngModel'}
– 它不再有效! 我猜测,当我们require: 'ngModel'
,ngModelController使用HTML /视图中的名称(即ng-model属性值)在隔离范围上创build一个属性。 所以如果我们在对象散列中指定相同的名字,一切都很好。 但是,如果我们指定一个不同的名称,那么这个新的范围属性(例如,dt1)就不会和我们需要的ngModelController相关联。
这是一个更新的小提琴 。
使您的指令以比ngModel更高的优先级运行,并更正您的隔离范围的模型绑定。 我select了与input指令相同级别的“100”的优先级,在像ngRepeat这样的高优先级模板操作之后,但在ngModel使用的默认值0之前,
以下是示例代码:
myDirective = function() { return { compile: function(tElement, tAttrs, transclude) { // Correct ngModel for isolate scope if (tAttrs.ngModel) { tAttrs.$set('model', tAttrs.ngModel, false); tAttrs.$set('ngModel', 'model', false); } return { post: function(scope, iElement, iAttrs, controller) { // Optionally hook up formatters and parsers controller.$formatters.push(function(value) { // ... }) // Render return controller.$render = function() { if (!controller.$viewValue) { return; } angular.extend(scope, controller.$viewValue); }; } }; }, priority: 100, require: '^ngModel', scope: { model: '=' }, }; }
在编译期间,指令检查ngModel属性是否存在。 此检查使用Angular的属性对标准化值进行处理。 如果该属性存在,则将其replace为'model'(而不是'ngModel'),该名称是数据绑定到我们的隔离区中的名称。 但是,我们还必须创build一个属性,以便Angular可以为我们执行数据绑定。 这两个属性都可以(在你的select)修改一个false
参数,保持DOM不变。
我想我有同样的问题,我发现部分可用的解决scheme。
所以,这个问题有几个部分:
- 你的自定义指令需要一些私有属性,即隔离范围
- DOM节点只能有一个范围,所有的指令都是共享的
- ngModel =“something”绑定到共享(独立)作用域中的“something”,这是实际的问题
所以,我的第一步是重写我的指令,使用scope:true
而不是scope:{...}
(实际上,这是一个要求,因为我想在我的指令的transcluded内容中使用一些全局范围属性) attrs.$observe()
, $scope.$parent.$watch()
等帮助。
然后在compile()
我将ngModel
重新绑定到父范围的属性: attrs.$set('ngModel', '$parent.' + attrs.ngModel, false)
。 就这样。
这里是我的指令,剥离了非必要的代码:
angular.module('App', []).directive('dir', function () { return { /* This one is important: */ scope:true, compile:function (element, attrs, transclude) { /* The trick is here: */ if (attrs.ngModel) { attrs.$set('ngModel', '$parent.' + attrs.ngModel, false); } return function ($scope, element, attrs, ngModel) { // link function body }; } }; });
试试这个版本:
.directive('myDir', function() { return { restrict: 'EA', scope: { YYY: '=ngModel' }, require: 'ngModel', replace: true, template: function render(element, attrs) { var type = attrs.type || 'text'; var required = attrs.hasOwnProperty('required') ? " required='required'" : ""; return "<input ng-model='YYY' type="' + type + '" + required + ' />'; } }; });