【问题标题】:Angular directive data binding not working properlyAngular 指令数据绑定无法正常工作
【发布时间】:2013-06-07 04:27:02
【问题描述】:

我一直在尝试为我的 Angular 应用程序编写一个共享指令,将输入字段转换为 jquery timepicker。

我有两个输入字段,fromTime 和 toTime,我想在 fromTime 更改时更新 toTime。

我的代码如下:

HTML

<input type="text"
       time-picker="{ timeFormat: 'H:i'}"
       class="input-block-level"
       name="fromTime"
       ng-model="filter.fromTime"
       ng-change="update()" 
/>

<input type="text"
       time-picker="{ timeFormat: 'H:i'}"
       class="input-block-level"
       name="toTime"
       ng-model="filter.toTime"
       ng-change="update()" 
/>

指令

sharedServices.directive('TimePicker', function () {
return {
    restrict: 'A',
    scope: {
        time: "=ngModel"
    },
    link: function(scope, element, attrs) {
                    //Initialize the timepicker
        $(element).timepicker(scope.$eval(attrs.TimePicker));
                    //watch for changes
        scope.$watch('time', function(stuff) {
            console.log('some change', stuff);
        });
    }
};
});

包括共享服务

var roomsApp = angular.module("roomsApp", ["ngResource",'ui', 'sharedServices','ui.bootstrap']).config(function($routeProvider) { ...

指令加载并且时间选择器被初始化并附加到元素,但是当我更改输入字段时没有任何反应,甚至没有附加到元素的 ng-change 事件。页面加载时输入字段也是空的,即使模型设置为在应用程序的控制器中包含某些值。谁能解释一下这个问题?

*更新 http://plnkr.co/edit/t3BzShezEdh29ZAlI8ZI?p=preview 这说明了我的问题。更改日期时没有控制台记录任何内容

【问题讨论】:

  • 向我们展示包含指令的代码。我看到了一些非常奇怪的事情。
  • @TheHippo ,完成。指令加载并且时间选择器被初始化。但是作用域和指令之间的绑定不能正常工作。
  • 请继续利用 ngModelController 但不要作为隔离范围的道具,将其设置为作为 linkibg 函数的第四个参数注入的必需控制器,使用其 $viewValue 重载其 $render 方法并在您的 UI 小部件更改上使用其 $setViewValue 并结合在范围上调用 $apply。

标签: javascript html angularjs angularjs-directive


【解决方案1】:

解决问题

您需要听取输入字段的更改并更新scope.time。

我所做的第一个更改是在 jQuery 之后包含 Angular,这样element 参数将是一个 jQuery 对象。

然后监听更改事件并更新scope.time。将调用封装在 scope.$apply 中,告诉 Angular 发生了一些变化。

element.on('change', function () {
    scope.$apply(function () {
        scope.time = element.datepicker('getDate');
    });
});

Plunker

替代方法

个人在编写应用于输入元素的指令时,我希望允许用户使用ngModel 指令来保持数据同步,因为这是一个已知的约定。通过使用 ngModel,您还可以(可选)使用它的控制器来添加自定义验证或其他逻辑,例如解析器/格式化程序(在这种情况下不需要)。

通过使用$parse 服务,我们可以将数据读/写回ngModel 字段。

我还进行了更改,以便在需要时可以传递日期时间选项。

最后,我将第一个 datetime() 调用移到了 compile 函数,因为这是应该进行 DOM 操作的地方。

.directive("datetime", ['$parse', function($parse) {
  return {
      require: '?ngModel',
      restrict: 'A',
      compile: function (element, attrs) {
        var options = $parse(attrs.datetime)() || {};
        console.log('Passed options:', options);

        element.datepicker(options);

        // Return the link function
        return function (scope, element, attrs, ngModel) {
          var getter, setter;

          // If the ngModel directive is used, then set the initial value and keep it in sync
          if (ngModel) {
            getter = $parse(attrs.ngModel);
            setter = getter.assign;

            console.log('ngModel directive used, setting initial value')
            element.datepicker("setDate", getter(scope));

            scope.$watch(attrs.ngModel, function(val, prev) {
              console.log('ngModel changed from', prev, 'to', val);
            });

            element.on('change', function () {
              console.log('change');
              var newValue = element.datepicker('getDate');
              scope.$apply(function () {
                setter(scope, newValue);
              });
            });
          }
        };
      }
  };
}])

并且指令是这样使用的:

<input type="text" datetime="{showButtonPanel: true}" ng-model="parentTime" />

Plunker

【讨论】:

  • 我不明白为什么 ng-model 指令首先停止工作。在我向元素添加自定义指令之前,我处于一种可以正常工作的情况(将输入值绑定到范围模型)。然后绑定不再发生。我只是希望它们堆叠起来。
【解决方案2】:

更新: 这是一个使用 jquery datepicker 的非常基本的实现 - http://plnkr.co/edit/4JZIWo6mJqg59F5o7xdh?p=preview

angular
.module("demo", [])
.directive("test", function() {
  return {
      restrict: 'A',
      scope: {
          time: "="
      },
      link: function(scope, element, attrs) {
        console.log("scope.time:" + scope.time);
        $(element).datepicker();
        $(element).datepicker("setDate", scope.time);
      }
  };
})
.controller("demoCtl", function($scope){
  $scope.parentTime = new Date();
});

看看angular-ui calendar module。如果你不想用,​​可以看看他们的代码来帮助你。


此外,您设置示波器的方式看起来很奇怪。我认为您想将指令的 time 属性绑定到由指令的父范围管理的属性,而不是 ng-model。

试试这个

return {
    restrict: 'A',
    scope: {
        time: "="
    },
    link: function(scope, element, attrs) {
                    //Initialize the timepicker
        $(element).timepicker(scope.$eval(attrs.TimePicker)); 
                    //watch for changes
        scope.$watch('time', function(stuff) {
            console.log('some change', stuff);
        });
    }
};

并使用它

<input type="text"
       time-picker="{ timeFormat: 'H:i'}"
       class="input-block-level"
       name="toTime"
       time="filter.toTime"
       ng-change="update()" 
/>

您也不需要将attrs.TimePicker 包装在$scope.eval 中。

【讨论】:

  • 只使用“=”会使“时间”模型未定义:/而不使用 $scope.eval 似乎忽略了选项
  • 看起来是错误的网址 - 试试这个:plnkr.co/edit/4JZIWo6mJqg59F5o7xdh?p=preview
  • ohhhhh,我明白是什么混乱了。您无法观看 scope.time 变量,因为日期选择器不会更改它。在我发布的示例中,您会看到我在 datepicker 的 onSelect 事件中明确设置了指令的 scope.time 变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-28
  • 1970-01-01
  • 1970-01-01
  • 2020-07-17
  • 2013-04-03
  • 1970-01-01
相关资源
最近更新 更多