我正在创建简单的ui-datetime指令。它将javascript Date对象拆分为_date,_hours和_minutes部分。 _date使用jquery ui datepicker,_hours和_minutes - 数字输入。
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();
}
在github上:https://github.com/andreev-artem/angular_experiments/tree/master/ui-datetime
据我了解 - 创建新组件时的最佳做法是使用隔离范围。
当我尝试使用隔离范围时 - 没有任何作用。 ngModel。$ viewValue === undefined。
当我尝试使用新范围时(我的例子,不是那么好的变体imho) - ngModel在新创建的范围上使用值。
当然,我可以使用隔离范围创建指令,并通过“= expression”(example)使用ngModel值。但我认为使用ngModelController是一种更好的做法。
我的问题:
答案 0 :(得分:18)
在您的第一个小提琴中用scope: true
替换scope: { datetime1: '=ngModel'}
似乎工作正常 - fiddle。不幸的是,你的“示例”小提琴的链接被打破了,所以我不确定你在那里尝试了什么。
因此,似乎ngModelController可以与隔离范围一起使用。
这是一个较小的小提琴,它在HTML /视图中使用ng-model,一个隔离范围,以及链接函数中的$ setViewValue:fiddle。
更新:我刚刚发现了一些相当有趣的东西:如果隔离范围属性被赋予了不同的名称 - 例如,说dt1而不是datetime1 - scope: { dt1: '=ngModel'}
- 它不再作品!我猜测当我们require: 'ngModel'
时,ngModelController使用HTML /视图中的名称(即ng-model属性值)在隔离范围上创建属性。因此,如果我们在对象哈希中指定相同的名称,那么一切都很好。但是如果我们指定一个不同的名称,那么新的范围属性(例如,dt1)就不会与我们需要的ngModelController相关联。
这是一个updated fiddle。
答案 1 :(得分:2)
使指令以比ngModel更高的优先级运行,并更正隔离范围的模型绑定。我选择优先级为'100'的级别与输入指令相同,经过高优先级模板操作(如ngRepeat)之后但默认值为0之前,这是ngModel使用的。
以下是示例代码:
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的Attributes对标准化值起作用。如果该属性存在,则将其替换为'model'(不是'ngModel'),这是绑定到隔离数据中的名称。但是,我们还必须创建一个属性,以便Angular可以为我们执行数据绑定。这两个属性都可以(根据您的选择)使用false
参数进行修改,使DOM保持不变。
答案 2 :(得分:1)
我认为我遇到了同样的问题,我找到了部分可用的解决方案。
所以,问题有几个部分:
所以,我的第一步是重写我的指令以使用scope:true
而不是scope:{...}
(实际上,这是一个要求,因为我想在我的指令的被转换内容中使用一些全局范围属性) :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
};
}
};
});
答案 3 :(得分:0)
试试这个版本:
.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 + ' />';
}
};
});