2012-08-10 16 views
21

Tôi đang tạo chỉ thị ui-datetime đơn giản. Nó chia đối tượng javascript Date thành các phần _date, _hours và _minutes. _date sử dụng jquery ui datepicker, _hours và _minutes - số đầu vào.Tôi có thể sử dụng ng-model với phạm vi phân lập được khô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

On github: https://github.com/andreev-artem/angular_experiments/tree/master/ui-datetime

Theo như tôi hiểu - thực hành tốt nhất khi bạn tạo ra một thành phần mới là sử dụng phạm vi cô lập.

Khi tôi cố gắng sử dụng phạm vi phân lập - không có gì hoạt động. ngModel. $ viewValue === không xác định.

Khi tôi cố gắng sử dụng phạm vi mới (ví dụ của tôi, không phải là biến thể tốt như vậy imho) - ngModel sử dụng giá trị trên phạm vi mới được tạo.

Tất nhiên tôi có thể tạo chỉ thị với phạm vi phân lập và làm việc với giá trị ngModel thông qua "= expression" (example). Nhưng tôi nghĩ rằng làm việc với ngModelController là một thực hành tốt hơn.

Câu hỏi của tôi:

  1. Tôi có thể sử dụng ngModelController với phạm vi bị cô lập?
  2. Nếu không thể giải pháp nào tốt hơn cho việc tạo thành phần như vậy?

Trả lời

2

Làm cho chỉ thị của bạn chạy ở mức ưu tiên cao hơn ngModel và sửa mô hình ràng buộc cho phạm vi phân lập của bạn. Tôi đã chọn mức độ ưu tiên là '100' tương đương với chỉ thị đầu vào, sau khi thao tác mẫu có mức độ ưu tiên cao như ngRepeat nhưng trước mặc định là 0, đó là những gì ngModel sử dụng.

Dưới đây là mã ví dụ:

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: '=' 
    }, 
    }; 
} 

Trong biên soạn, kiểm tra chỉ thị cho dù thuộc tính ngModel có mặt. Kiểm tra này hoạt động trên giá trị chuẩn hóa bằng cách sử dụng Attributes của Angular. Nếu thuộc tính là hiện tại, nó được thay thế bằng 'model' (không phải 'ngModel'), đó là tên được gắn với dữ liệu của chúng ta. Tuy nhiên, chúng ta cũng phải tạo một thuộc tính để Angular có thể thực hiện ràng buộc dữ liệu cho chúng ta. Cả hai thuộc tính có thể được (tùy chọn của bạn) được sửa đổi với thông số false khiến DOM không thay đổi.

+2

Có vẻ như cách giải quyết. Nhưng đối với workaround tôi thích 'phạm vi: true' và' ng-model = "someObj.someProp" ' –

19

Thay thế scope: true bằng scope: { datetime1: '=ngModel'} trong fiddle đầu tiên của bạn có vẻ hoạt động tốt - fiddle. Thật không may, liên kết đến "fiddle" ví dụ của bạn bị hỏng, vì vậy tôi không chắc chắn những gì bạn đã thử ở đó.

Vì vậy, có vẻ như ngModelController có thể được sử dụng với phạm vi cách ly.

Đây là một fiddle nhỏ hơn sử dụng mô hình ng trong HTML/chế độ xem, phạm vi cách ly và $ setViewValue trong hàm liên kết: fiddle.

Cập nhật: Tôi chỉ phát hiện ra một cái gì đó khá thú vị: nếu tài sản phạm vi cô lập được đưa ra một cái tên khác - ví dụ, nói dt1 thay vì datetime1 - scope: { dt1: '=ngModel'} - nó không còn hoạt động! Tôi đoán rằng khi chúng tôi require: 'ngModel', ngModelController sử dụng tên trong HTML/view (tức là, giá trị thuộc tính ng-model) để tạo thuộc tính trên phạm vi cô lập.Vì vậy, nếu chúng ta chỉ định cùng một tên trong đối tượng băm, tất cả là tốt. Nhưng nếu chúng tôi chỉ định một tên khác, thuộc tính phạm vi mới (ví dụ: dt1) không được liên kết với ngModelController mà chúng tôi yêu cầu.

Đây là số updated fiddle.

+1

Có vẻ như ngModelController [sử dụng] (https://github.com/angular/angular.js/blob/v1.0.1 /src/ng/directive/input.js#L873) và [đồng hồ] (https://github.com/angular/angular.js/blob/v1.0.1/src/ng/directive/input.js#L998) dựa trên ngModel. Vì vậy, chúng tôi cũng nên sử dụng cách giải quyết khác nhau. –

+0

Đó có phải là lỗi @MarkRajcok không? – finishingmove

+0

@finishingmove, tôi không biết. Tôi đoán rằng chúng tôi chỉ nhận được "may mắn" nếu chúng tôi sử dụng cùng một tên (nhưng tôi cũng không biết liệu may mắn đó có thể phá vỡ một nơi nào đó bên trong chỉ thị) hay không. Không có ví dụ Angular nào sử dụng một phạm vi cô lập khi chúng 'require: 'ngModel'', vì vậy tôi sẽ tránh xa nó. –

1

Tôi nghĩ rằng tôi đã có cùng một vấn đề, và tôi tìm thấy một phần giải pháp có thể sử dụng được.

Vì vậy, vấn đề có nhiều phần:

  1. thị tùy chỉnh của bạn muốn một số đặc tính riêng, tức là phạm vi cô lập
  2. DOM nút có thể chỉ có một phạm vi, tất cả các chỉ chia sẻ nó
  3. ngModel =" một cái gì đó "liên kết với" một cái gì đó "trong phạm vi chia sẻ (cô lập) đó, và đây là vấn đề thực tế

Vì vậy, bước đầu tiên của tôi là viết lại chỉ thị của tôi để sử dụng scope:true thay vì scope:{...} (thực ra, đó là yêu cầu, vì tôi muốn sử dụng một số thuộc tính phạm vi toàn cầu trong nội dung được nhúng của chỉ thị của tôi): những thứ như attrs.$observe(), $scope.$parent.$watch(), v.v. đã giúp.

Sau đó, trong compile() Tôi bị ràng buộc ngModel đối với thuộc tính của phạm vi cha mẹ: attrs.$set('ngModel', '$parent.' + attrs.ngModel, false). Và đó là tất cả.

Dưới đây là chỉ thị của tôi, với mã không cần thiết lột:

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

Bạn chỉ có thể sử dụng' scope: true' và 'ng- model = "someObj.someProp" 'thay vì mẹo của bạn. Sử dụng '" someObj.someProp "' được khuyến khích cho 'ng-model'. –

+1

Có, giải pháp của bạn cũng hoạt động, cảm ơn bạn đã chỉ ra. Mặc dù, mã của tôi sửa chữa vấn đề cho tất cả các trường hợp của một chỉ thị, và bạn yêu cầu điều chỉnh mã riêng biệt cho mỗi trường hợp. Tôi đã biên soạn bản trình diễn nơi bạn có thể thấy mã của mình đang hoạt động mà không có someObj (ví dụ: $ scope.someProp): http://jsbin.com/ejozow/1/edit. BTW, bạn có thể đăng liên kết để đọc về các đề xuất sử dụng lại mô hình ng? Các tài liệu chính thức dường như khá thưa thớt nói chung. – alx

+0

https://plus.google.com/118090665492423851447/posts/KKiLKLCF4Xa - xem nhận xét của Miško Hevery. Nó không phải là bắt buộc nhưng được khuyến khích. –

0

Hãy thử một phiên bản này:

.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 + ' />'; 
        } 
    }; 
}); 
Các vấn đề liên quan