2013-12-18 17 views
13

Tôi đã có ý tưởng bao bọc đầu vào vào các chỉ thị tùy chỉnh để đảm bảo giao diện và hành vi nhất quán thông qua trang web của tôi. Tôi cũng muốn bọc datepicker và trình đơn thả xuống của bootstrap ui. Ngoài ra, chỉ thị sẽ xử lý xác thực và hiển thị chú giải công cụ.đầu vào gói trong chỉ thị trong góc

HTML nên tìm một cái gì đó như thế này:

<my-input required max-length='5' model='text' placeholder='text' name='text'/> 

hoặc

<my-datepicker required model='start' placeholder='start' name='start'/> 

trong các chỉ thị tôi muốn tạo ra một cấu trúc dom như:

<div> 
<div>..</div> //display validation in here 
<div>..</div> //add button to toggle datepicker (or other stuff) in here 
<div>..</div> //add input field in here 
</div> 

tôi đã cố gắng nhiều cách khác nhau để đạt được điều này nhưng luôn gặp phải một số sự cân bằng:

  1. sử dụng transclude và thay thế để chèn đầu vào vào cấu trúc chỉ thị dom (trong trường hợp này chỉ thị sẽ bị giới hạn thành 'A' không 'E' như trong ví dụ trên). Vấn đề ở đây là, không có cách nào dễ dàng để truy cập vào phần tử transcluded như tôi muốn thêm các thuộc tính tùy chỉnh trong trường hợp của datepicker. Tôi có thể sử dụng chức năng transclude và sau đó biên dịch lại các mẫu trong chức năng liên kết, nhưng điều này có vẻ hơi phức tạp cho nhiệm vụ này. Điều này cũng dẫn đến các vấn đề với phạm vi transcluded và trạng thái chuyển đổi cho datepicker (một trong phạm vi chỉ thị, cái còn lại trong phạm vi transcluded).

  2. chỉ sử dụng thay thế. Trong trường hợp này, tất cả các thuộc tính được áp dụng cho div ngoài cùng (ngay cả khi tôi tạo cấu trúc dom mẫu trong hàm biên dịch). Nếu tôi chỉ sử dụng đầu vào làm mẫu, thì các thuộc tính nằm trên đầu vào, nhưng tôi cần tạo mẫu trong hàm liên kết rồi biên dịch lại nó. Theo như tôi hiểu mô hình pha của góc, tôi muốn tránh biên dịch lại và thay đổi mẫu dom trong chức năng liên kết (mặc dù tôi đã thấy nhiều người làm điều này).

Hiện tại tôi đang làm việc với phương pháp thứ hai và tạo mẫu trong hàm liên kết, nhưng tôi tự hỏi có ai đó có ý tưởng hay hơn không!

+0

Bạn muốn thêm loại thuộc tính tùy chỉnh nào? Một ví dụ đầy đủ về HTML mà bạn muốn chỉ thị để hiển thị (tùy chỉnh và tất cả) sẽ hữu ích ở đây, tôi nghĩ vậy. –

+0

Trong trường hợp của datepicker tôi muốn đặt giá trị ứng dụng rộng tiêu chuẩn. Thẻ đầu vào html kết quả sẽ trông giống như: Dữ liệu 'dữ liệu-đang-mở =" mở "dữ liệu-hiện-open =" mở "dữ liệu-hiện-văn bản =" {{hiện tại}} " -close-text = "{{text}}" dữ liệu-clear-text = "{{clear}}" dữ liệu-show-tuần = "{{showweeks}}" data-starting-day = "{{startingday}} "placeholder =" {{placeholder}} "data-ng-model =" bắt đầu "name =" bắt đầu "bắt buộc =" bắt buộc "class =" ng-isolate-phạm vi ng-pristine ng-hợp lệ ng-hợp lệ-yêu cầu "> ' – roemer

Trả lời

2

Tại sao không thực hiện chỉ thị như vậy?

myApp.directive('wrapForm', function(){ 
    return { 
     restrict: 'AC', 
     link: function(scope, inputElement, attributes){      
      var overallWrap = angular.element('<div />'); 
      var validation = angular.element('<div />').appendTo(overallWrap); 
      var button = angular.element('<div />').appendTo(overallWrap); 
      var inputWrap = angular.element('<div />').appendTo(overallWrap); 

      overallWrap.insertBefore(inputElement); 
      inputElement.appendTo(inputWrap); 

      inputElement.on('keyup', function(){ 
       if (inputElement.val()) { 
        validation.text('Just empty fields are valid!'); 
       } else { 
        validation.text(''); 
       } 
      });    
     } 
    } 
}); 

Fiddle: http://jsfiddle.net/bZ6WL/

Về cơ bản bạn có lĩnh vực đầu vào gốc (đó là, bằng cách này,) và xây dựng bao bì riêng rẽ. Trong ví dụ này, tôi chỉ cần xây dựng các DIV theo cách thủ công. Đối với những thứ phức tạp hơn, bạn cũng có thể sử dụng một mẫu có được $compile (d) bởi angularjs.

Lợi thế sử dụng thuộc tính lớp hoặc html "wrapForm": Bạn có thể sử dụng cùng một chỉ thị cho một số loại đầu vào biểu mẫu.

+0

Tôi đã nhìn thấy điều này trong fiddle nhưng điều này không làm việc cho tôi, không biết tại sao.Tôi có jquery-1.10.2 và angular.js trong ứng dụng của tôi và vẫn không làm việc này !! –

+1

Cẩn thận - nếu bạn sử dụng điều này với ng-nếu nó sẽ không xóa các trình bao bọc! – jantimon

+0

Đây là một gợi ý tốt nhưng góc cạnh không có appendTo hoặc insertBefore. Nối và sau (tương ứng) có thể được sử dụng thay vì mặc dù để đạt được một kết quả tương tự. – Theo

8

Đây là những gì tôi tin là cách thích hợp để thực hiện việc này. Giống như OP tôi muốn có thể sử dụng một chỉ thị thuộc tính để bao bọc một input. Nhưng tôi cũng muốn nó hoạt động với ng-if và như vậy mà không bị rò rỉ bất kỳ yếu tố nào. Như @ jantimon đã chỉ ra, nếu bạn không dọn dẹp các phần tử bao bọc của bạn, chúng sẽ nán lại sau khi ng-nếu phá hủy phần tử gốc.

app.directive("checkboxWrapper", [function() { 
    return { 
     restrict: "A", 
     link: function(scope, element, attrs, ctrl, transclude) { 
     var wrapper = angular.element('<div class="wrapper">This input is wrappered</div>'); 

     element.after(wrapper); 
     wrapper.prepend(element); 

     scope.$on("$destroy", function() { 
      wrapper.after(element); 
      wrapper.remove(); 
     }); 
     } 
    }; 
    } 
]); 

here's a plunker bạn có thể chơi cùng.

QUAN TRỌNG: scope vs element $ hủy. Bạn phải đặt dọn dẹp của bạn trong scope.$on("$destroy") và không phải trong element.on("$destroy") (đó là những gì tôi đã cố gắng ban đầu). Nếu bạn làm điều đó trong phần tử sau (phần tử) thì thẻ nhận xét "ngIf end" sẽ bị rò rỉ. Điều này là do làm thế nào để ngif của Angular đi về làm sạch thẻ bình luận cuối cùng của nó khi nó hiện logic falsey của nó. Bằng cách đặt mã dọn dẹp của chỉ thị trong phạm vi $ destroy, bạn có thể đặt DOM trở lại giống như trước khi bạn bọc đầu vào và vì vậy mã dọn dẹp của ng-if rất vui. Bởi thời gian element.on ("$ phá hủy") được gọi là, nó là quá muộn trong dòng ng-nếu falsey để unwrap các yếu tố ban đầu mà không gây ra một bình luận thẻ bị rò rỉ.

+1

[Angular's jqLite có 'wrap'] (https://docs.angularjs.org/api/ng/function/angular.element#angular-s-jqlite). Tại sao không sử dụng điều đó so với 'after' và' prepend'? – TheSharpieOne

+0

Thử 'wrap' khiến tôi gặp khó khăn. Để chắc chắn nó sẽ là cú pháp sạch hơn/gọn hơn, nhưng vì lý do nào đó, việc dọn dẹp không hoạt động khi sử dụng 'wrap'. Hãy thử 'wrap' trong Plunker sau đó bật hộp kiểm Show (bật tắt điều kiện ngIf). Tôi thừa nhận rằng tôi đã không điều tra thêm, nhưng tôi quan tâm đến chi tiết nếu có ai muốn nhìn sâu hơn vào nó. – BrandonLWhite

2

Tại sao không bọc đầu vào trong chức năng biên dịch? Lợi thế là bạn sẽ không phải sao chép các thuộc tính và sẽ không phải dọn dẹp trong phạm vi phá hủy chức năng. Lưu ý rằng bạn phải loại bỏ thuộc tính chỉ thị mặc dù để tránh thực hiện vòng tròn.

(http://jsfiddle.net/oscott9/8er3fu0r/)

angular.module('directives').directive('wrappedWithDiv', [ 
    function() { 
     var definition = { 
      restrict: 'A', 
      compile: function(element, attrs) { 
       element.removeAttr("wrapped-with-div"); 
       element.replaceWith("<div style='border:2px solid blue'>" + 
        element[0].outerHTML + "</div>") 
      } 
     } 
     return definition; 
    } 
]); 
0

Dựa trên điều này: http://angular-tips.com/blog/2014/03/transclusion-and-scopes/

Chỉ thị này không nhúng, nhưng những thứ nhúng sử dụng phạm vi phụ huynh, vì vậy tất cả các ràng buộc làm việc như nếu nội dung nhúng là trong bản gốc phạm vi nơi trình bao bọc được sử dụng. Điều này tất nhiên bao gồm ng-model, cũng min/max và các chỉ thị/thuộc tính xác nhận khác. Nên làm việc cho bất kỳ nội dung nào. Tôi không sử dụng chỉ thị ng-transclude bởi vì tôi đang nhân bản các phần tử và cung cấp phạm vi của cha mẹ (bộ điều khiển) cho chúng. "my-transclude" được sử dụng thay cho ng-transclude để chỉ định vị trí chèn nội dung được nhúng vào.

Quá xấu ng-transclude không có cài đặt để kiểm soát phạm vi. Nó sẽ làm cho tất cả những điều này không cần thiết. Và có vẻ như họ sẽ không sửa lỗi: https://github.com/angular/angular.js/issues/5489

controlsModule.directive('myWrapper', function() { 
     return { 
      restrict: 'E', 
      transclude: true, 
      scope: { 
       label: '@', 
       labelClass: '@', 
       hint: '@' 
      }, 

      link: link, 
      template: 
       '<div class="form-group" title="{{hint}}"> \ 
        <label class="{{labelClass}} control-label">{{label}}</label> \ 
        <my-transclude></my-transclude> \ 
       </div>' 
     }; 

     function link(scope, iElement, iAttrs, ctrl, transclude) { 

      transclude(scope.$parent, 
       function (clone, scope) { 

        iElement.find("my-transclude").replaceWith(clone); 

        scope.$on("$destroy", function() { 
         clone.remove(); 
        }); 
       }); 
     } 
    }); 
Các vấn đề liên quan