自定义角度指令中的ng-switch打破双向绑定

时间:2016-04-16 13:49:11

标签: javascript angularjs datepicker

我创建了自定义指令来封装uib-datepicker-popup:

'use strict';
angular.module( 'frontendApp' )
.directive( 'inputDate', function(){

var controller = function(){

  var vm = this;

  function init() {
    vm.formats = [ 'dd.MMMM yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate' ];
    vm.format = vm.formats[ 0 ];
    vm.altInputFormats = [ 'M!/d!/yyyy' ];

    vm.dateOptions = {
      datepickerMode: 'day',
      formatYear: 'yy',
      maxDate: new Date(),
      minDate: new Date( 1900, 1, 1 ),
      startingDay: 1
    };

    vm.datepicker = {
      opened: false
    };
  };

  init();

  vm.showDatePicker = function(){
    vm.datepicker.opened = true;
  };
};

var template = '<div ng-switch on="readonly" >' +
    '<div ng-switch-when="true" class="form-control" readonly>' +
      '<div readonly name="readonlyText">{{ngModel | date : \'d.MMMM yyyy\'}}</div>' +
    '</div>' +
    '<div ng-switch-default class="input-group">' +
      '<input class="form-control" type="text" uib-datepicker-popup="{{vm.format}}" ng-model="ngModel" ng-model-options="{timezone:\'UTC\'}" is-open="vm.datepicker.opened" datepicker-options="vm.dateOptions" ng-required="true" show-button-bar="false" alt-input-formats="vm.altInputFormats" />' +
      '<span class="input-group-btn">' +
        '<button type="button" class="btn btn-default" ng-click="vm.showDatePicker()"><i class="glyphicon glyphicon-calendar"></i></button>' +
      '</span>' +
    '</div>' +
  '</div>';


return{
  controller: controller,
  controllerAs: 'vm',
  bindToController: true,
  template: template,
  restrict: 'EA',
  scope           :true,
  require:'ngModel',
  link: function( scope, element, attrs, ngModel ){
    // Bring in changes from outside:
    scope.$watch( 'ngModel', function(){
      if( ngModel ) {
        scope.$eval( attrs.ngModel + ' = ngModel' );
      }
    } );

    // Send out changes from inside:
    scope.$watch( attrs.ngModel, function( val ){
      if( val ) {
        scope.ngModel = val;
      }
    } );

    if( attrs.readonly === 'true' ) {
      scope.readonly = true;
    }
  }
};

});

html部分是:

<input-date ng-model="form.flight.date"></input-date>

问题:如果弹出窗口显示,则从attrs.ngModel正确初始化scope.ngModel。我在守望者里面写了一个日志,告诉我看着attrs.ngModel完美地工作,但是看着&#39; ngModel&#39;或scope.ngModel仅在我使用datepicker之前有效。只要未触发日期选择器,它就能完美运行。 刚刚发现,如果我重新启动它,它的效果非常好 &#34; NG-开关默认&#34 ;.用ng-show / ng-hide替换它使指令完全按预期工作。

任何人都可以解释原因吗?

1 个答案:

答案 0 :(得分:1)

您看到的行为绝对正确。当您使用ng-ifng-switchng-repeat等结构指令时,它会创建新范围并复制父范围的所有属性。您的模型是基元(字符串),因此它完全复制到新范围并在此范围内更改而不传播到父范围。

你能做的是:

  1. 使用object而不是string来传递ng-model,我个人觉得这里很尴尬
  2. 使用来自控制器对象的ng-model而不是来自范围
  3. 继续使用第二种方法:您已经使用bindToControllerscope: true的隔离范围,因此只需使用观察者跟踪模型,将其绑定到控制器:

    return {
      bindToController: true,
      scope: {
        ngModel: '='
      },
      ...
    

    理想情况下,您甚至不需要链接功能,而是在模板中而不是

    '<div readonly name="readonlyText">{{ngModel | date : \'d.MMMM yyyy\'}}</div>'
    

    使用

    '<div readonly name="readonlyText">{{vm.ngModel | date : \'d.MMMM yyyy\'}}</div>'
    

    为什么ng-hide仍有效?它不会创建新的范围。