2012-08-10 67 views
21

我正在创建简单的ui-datetime指令。它将javascript Date对象分成_date,_hours和_minutes部分。 _date使用jquery ui datepicker,_hours和_minutes - 数字输入。我可以在隔离范围内使用ng模型吗?

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

据我了解 - 最佳实践当你创建一个新的组件是用隔离范围。

当我试图使用隔离范围 - 没有任何工作。 ngModel。$ viewValue === undefined。

当我尝试使用新的作用域(我的例子,不是很好的变体imho) - ngModel在新创建的作用域上使用值。

当然,我可以通过“=表达式”(example)创建具有隔离范围的指令并使用ngModel值。但我认为使用ngModelController是一种更好的做法。

我的问题:

  1. 我可以使用ngModelController孤立范围是什么?
  2. 如果不可能创建这样的组件更好的解决方案?

回答

2

使您的指令以比ngModel更高的优先级运行并更正您的隔离范围的模型绑定。我选择了与输入指令相同级别的“100”的优先级,在像ngRepeat这样的高优先级模板操作之后,但在ngModel使用的缺省值0之前,它的优先级为100。

这里的示例代码:

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

似乎解决办法。但是,对于解决方法,我更喜欢'scope:true'和'ng-model =“someObj.someProp”' – 2012-12-12 12:30:58

19

更换scope: truescope: { datetime1: '=ngModel'}在你的第一个小提琴似乎工作正常 - fiddle。不幸的是,你的“示例”小提琴的链接被打破,所以我不确定你在那里试过了什么。

所以,看起来ngModelController可以与隔离范围一起使用。

下面是一个较小的小提琴,它在链接函数fiddle中使用HTML /视图中的ng-model,隔离范围和$ setViewValue。

更新:我刚刚发现了一些相当有趣的事情:如果隔离范围属性被赋予不同的名称 - 例如说dt1而不是datetime1 - 它不再有效!我猜测,当我们require: 'ngModel'时,ngModelController使用HTML /视图中的名称(即ng-model属性值)在隔离范围上创建一个属性。所以如果我们在对象散列中指定相同的名字,一切都很好。但是,如果我们指定一个不同的名称,则新的范围属性(例如,dt1)不会与我们需要的ngModelController相关联。

这是updated fiddle

+1

似乎ngModelController [使用](https://github.com/angular/angular.js/blob/v1.0.1 /src/ng/directive/input.js#L873)和[手表](https://github.com/angular/angular.js/blob/v1.0.1/src/ng/directive/input.js#L998)基于ngModel。所以我们也应该使用不同的解决方法。 – 2013-02-10 10:14:16

+0

这是一个错误@MarkRajcok? – finishingmove 2013-06-19 17:10:24

+0

@finishingmove,我不知道。如果我们使用相同的名字,我会猜测我们只是“幸运”(但我也不知道这个运气是否可能在指令中的某个地方崩溃)。没有一个Angular示例在需要'ngModel'时使用隔离范围,所以我会避开它。 – 2013-06-19 18:35:37

1

我想我有同样的问题,我发现部分但可用的解决方案。

所以,这个问题有几个部分组成:

  1. 您的自定义指令,希望一些私人性质,即分离范围
  2. DOM节点只能有一个范围,所有指令共享
  3. ngModel =”东西“绑定到”在“共享(孤立)范围内的东西”,这是实际问题

所以,我的第一步是重写我的指令,使用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 
      }; 
     } 
    }; 
}); 
+2

您可以简单地使用'scope:true'和'ng- model =“someObj.someProp”'而不是你的伎俩。 'ng-model'建议使用''someObj.someProp''。 – 2013-04-13 06:32:18

+1

是的,您的解决方案也适用,谢谢指出。虽然,我的代码修复了指令的所有实例的问题,并且您需要为每个实例单独调整代码。 我已经编译了一个演示,你可以看到我的代码在运行时没有someObj(即$ scope.someProp):http://jsbin.com/ejozow/1/edit。 顺便说一句,你可以发布一个链接阅读关于建议重新模型使用?一般官方文件似乎相当稀少。 – alx 2013-04-14 05:26:40

+0

https://plus.google.com/118090665492423851447/posts/KKiLKLCF4Xa - 请参阅MiškoHevery评论。这不是强制性的,但建议。 – 2013-04-14 09:26:19

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 + ' />'; 
        } 
    }; 
}); 
相关问题