Javascript 自定义角度指令中的ng开关断开双向绑定

Javascript 自定义角度指令中的ng开关断开双向绑定,javascript,angularjs,datepicker,Javascript,Angularjs,Datepicker,我创建了自定义指令来封装uib日期选择器弹出窗口: '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', 'shortDat

我创建了自定义指令来封装uib日期选择器弹出窗口:

'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;
    }
  }
};
“严格使用”;
角度.模块('frontendApp')
.指令('inputDate',函数(){
变量控制器=函数(){
var vm=这个;
函数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:'天',
年份:“yy”,
maxDate:新日期(),
minDate:新日期(1900年1月1日),
开始日期:1
};
vm.datepicker={
开放:假
};
};
init();
vm.showDatePicker=函数(){
vm.datepicker.opened=true;
};
};
变量模板=“”+
'' +
“{ngModel}日期:\'d.MMMM yyyy\'}”+
'' +
'' +
'' +
'' +
'' +
'' +
'' +
'';
返回{
控制器:控制器,
controllerAs:'vm',
bindToController:对,
模板:模板,
限制:“EA”,
范围:正确,
要求:'ngModel',
链接:功能(范围、元素、属性、模型){
//从外部引入更改:
范围.$watch('ngModel',function(){
if(ngModel){
范围.$eval(attrs.ngModel+'=ngModel');
}
} );
//从内部发送更改:
范围$watch(attrs.ngModel,function(val){
if(val){
scope.ngModel=val;
}
} );
如果(attrs.readonly==='true'){
scope.readonly=true;
}
}
};
})

html部分是:

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

问题:如果弹出窗口出现,则scope.ngModel已从attrs.ngModel正确初始化。我在watcher中有一个日志,它告诉我监视attrs.ngModel效果很好,但是监视“ngModel”或scope.ngModel只在我使用日期选择器之前有效。只要不触发日期选择器,它就可以正常工作。 我刚刚发现如果我记得 “ng开关默认值”。将其替换为ng show/ng hide可使指令完全按预期工作


有人能解释为什么吗?

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

您可以做的是:

  • 使用object而不是string来传递
    ng模型
    ,我个人觉得这里非常尴尬
  • 从控制器对象而不是范围中使用ng模型
  • 继续第二种方法:您已经使用了
    bindToController
    scope:true
    的隔离作用域,因此只需使用watcher将模型绑定到控制器而不是跟踪模型:

    return {
      bindToController: true,
      scope: {
        ngModel: '='
      },
      ...
    
    因此,理想情况下,您甚至不需要在模板中使用链接函数而不是

    '<div readonly name="readonlyText">{{ngModel | date : \'d.MMMM yyyy\'}}</div>'
    
    “{ngModel}日期:\'d.MMMM yyyy\'}”
    
    使用

    “{vm.ngModel}日期:\'d.MMMM yyyy\'}”
    

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

    谢谢,第二种方法非常有效,并且很好地缩小了我的链接功能:)
    '<div readonly name="readonlyText">{{vm.ngModel | date : \'d.MMMM yyyy\'}}</div>'