Chỉ thị cách ly phạm vi với phạm vi ng-repeat trong AngularJS


95

Tôi có một chỉ thị với phạm vi cô lập (để tôi có thể sử dụng lại chỉ thị ở những nơi khác) và khi tôi sử dụng chỉ thị này với một ng-repeat, nó không hoạt động.

Tôi đã đọc tất cả tài liệu và câu trả lời về Stack Overflow về chủ đề này và hiểu các vấn đề. Tôi tin rằng tôi đã tránh được tất cả các lỗi thông thường.

Vì vậy, tôi hiểu rằng mã của tôi không thành công do phạm vi được tạo bởi ng-repeatchỉ thị. Chỉ thị của riêng tôi tạo một phạm vi cô lập và thực hiện liên kết dữ liệu hai chiều với một đối tượng trong phạm vi chính. Chỉ thị của tôi sẽ gán một đối tượng-giá trị mới cho biến bị ràng buộc này và điều này hoạt động hoàn hảo khi chỉ thị của tôi được sử dụng mà không có ng-repeat(biến cha được cập nhật chính xác). Tuy nhiên, với ng-repeat, phép gán tạo ra một biến mới trong ng-repeatphạm vi và biến cha không thấy sự thay đổi. Tất cả điều này là như mong đợi dựa trên những gì tôi đã đọc.

Tôi cũng đã đọc rằng khi có nhiều chỉ thị trên một phần tử nhất định, chỉ một phạm vi được tạo. Và a prioritycó thể được đặt trong mỗi chỉ thị để xác định thứ tự áp dụng các chỉ thị; các chỉ thị được sắp xếp theo mức độ ưu tiên và sau đó các hàm biên dịch của chúng được gọi (tìm kiếm từ ưu tiên tại http://docs.angularjs.org/guide/directive ).

Vì vậy, tôi hy vọng mình có thể sử dụng mức độ ưu tiên để đảm bảo rằng chỉ thị của tôi chạy trước và kết thúc việc tạo phạm vi cô lập và khi ng-repeatchạy, nó sử dụng lại phạm vi cô lập thay vì tạo phạm vi kế thừa nguyên mẫu từ phạm vi mẹ. Các ng-repeattiểu bang tài liệu mà chạy chỉ ở mức ưu tiên 1000. Không rõ 1là mức ưu tiên cao hơn hay mức ưu tiên thấp hơn. Khi tôi sử dụng mức độ ưu tiên 1trong chỉ thị của mình, nó không tạo ra sự khác biệt, vì vậy tôi đã thử 2000. Nhưng điều đó làm cho mọi thứ trở nên tồi tệ hơn: ràng buộc hai chiều undefinedcủa tôi trở nên và chỉ thị của tôi không hiển thị bất cứ điều gì.

Tôi đã tạo một trò chơi để hiển thị sự cố của mình . Tôi đã nhận xét về prioritycài đặt trong chỉ thị của tôi. Tôi có một danh sách các đối tượng tên và một lệnh được gọi name-rowđể hiển thị các trường họ và tên trong đối tượng tên. Khi một tên hiển thị được nhấp vào, tôi muốn nó đặt một selectedbiến trong phạm vi chính. Mảng tên, selectedbiến được chuyển tới name-rowchỉ thị bằng cách sử dụng liên kết dữ liệu hai chiều.

Tôi biết cách làm cho điều này hoạt động bằng cách gọi các hàm trong phạm vi chính. Tôi cũng biết rằng nếu selectedở bên trong một đối tượng khác, và tôi liên kết với đối tượng bên ngoài, mọi thứ sẽ hoạt động. Nhưng tôi không quan tâm đến các giải pháp đó vào lúc này.

Thay vào đó, những câu hỏi tôi có là:

  • Làm cách nào để ngăn ng-repeatviệc tạo một phạm vi kế thừa nguyên mẫu từ phạm vi chính và thay vào đó, nó sử dụng phạm vi cô lập của chỉ thị của tôi?
  • Tại sao mức độ ưu tiên 2000trong chỉ thị của tôi không hoạt động?
  • Sử dụng Batarang, có thể biết loại phạm vi đang sử dụng không?

1
Thông thường, bạn không muốn sử dụng một phạm vi cô lập nếu chỉ thị của bạn sẽ được sử dụng trên cùng một phần tử với các chỉ thị khác. Vì bạn đang tạo thuộc tính phạm vi của riêng mình và bạn cần làm việc với ng-repeat, tôi khuyên bạn nên sử dụng scope: truecho chỉ thị của mình. Xem thêm (nếu bạn chưa có) stackoverflow.com/questions/14914213/… Ngoài ra, chỉ vì một chỉ thị sẽ được sử dụng ở nhiều nơi không có nghĩa là chúng ta nên tự động sử dụng một phạm vi cô lập.
Mark Rajcok

Tôi đã đọc nhiều câu trả lời của bạn (chúng quá xuất sắc, cảm ơn bạn đã viết chúng), nhưng tôi chưa bao giờ nghĩ đến câu hỏi của bạn :-). Tôi đọc những gì bạn liên kết đến. Tôi thấy rằng không thể trộn các chỉ thị phạm vi cô lập với các chỉ thị khác. Tôi đồng ý với quan điểm rằng các chỉ thị như vậy là thành phần và do đó chúng không cần phải trộn lẫn với các chỉ thị khác. Một ngoại lệ (cho đến nay) đối với tôi sẽ là ng-repeat. Tôi nghĩ có thể kết hợp các chỉ thị độc lập với ng-repeat. Để được tiếp tục ...
Deepak Nulu

Tiếp theo từ trên ... Vì vậy, nếu chỉ nên có một chỉ thị với phạm vi cho một phần tử, thì ng-repeatkhông nên có phạm vi. ng-repeatcó một phạm vi có ý nghĩa đối với trường hợp sử dụng điển hình, vì vậy tôi không đề nghị nó được thay đổi. Thay vào đó, giống như tôi đã nhận xét trong câu trả lời của Alex Osborn, tôi nghĩ rằng tôi sẽ tạo một chỉ thị lặp lại dựa trên ng-repeatđiều đó không tạo ra phạm vi riêng của nó. Điều này sau đó có thể được sử dụng cho các chỉ thị lặp lại có phạm vi cô lập riêng của chúng. Để được tiếp tục ...
Deepak Nulu

Mã lặp lại một chỉ thị bây giờ cần biết nên sử dụng ng-repeathay chỉ thị lặp lại tùy chỉnh ít phạm vi hơn. Tôi nghĩ rằng "người gọi" biết điều này thì không sao, nhưng không ổn nếu "người gọi" (lệnh được lặp lại) để biết liệu nó có được lặp lại hay không. Để được tiếp tục ...
Deepak Nulu

Phát điên một chút với các nhận xét ở đây ... :-) ngRepeat phải tạo phạm vi riêng của nó. Tại sao bạn cảm thấy bạn cần một phạm vi cô lập ở đây?
Josh David Miller

Câu trả lời:


203

Được rồi, thông qua rất nhiều ý kiến ​​ở trên, tôi đã phát hiện ra sự nhầm lẫn. Đầu tiên, một số điểm cần làm rõ:

  • ngRepeat không ảnh hưởng đến phạm vi cô lập đã chọn của bạn
  • các tham số được truyền vào ngRepeat để sử dụng trên các thuộc tính của chỉ thị của bạn sử dụng phạm vi được kế thừa theo nguyên mẫu
  • lý do khiến chỉ thị của bạn không hoạt động không liên quan gì đến phạm vi cô lập

Dưới đây là một ví dụ về cùng một mã nhưng đã loại bỏ chỉ thị:

<li ng-repeat="name in names"
    ng-class="{ active: $index == selected }"
    ng-click="selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

Đây là một JSFiddle chứng minh rằng nó sẽ không hoạt động. Bạn nhận được kết quả chính xác giống như trong chỉ thị của bạn.

Tại sao nó không hoạt động? Vì phạm vi trong AngularJS sử dụng kế thừa nguyên mẫu. Giá trị selectedtrên phạm vi cha của bạn là giá trị nguyên thủy . Trong JavaScript, điều này có nghĩa là nó sẽ bị ghi đè khi một phần tử con đặt cùng một giá trị. Có một quy tắc vàng trong phạm vi AngularJS: các giá trị mô hình phải luôn có một .trong đó. Đó là, chúng không bao giờ nên là nguyên thủy. Xem câu trả lời SO này để biết thêm thông tin.


Đây là hình ảnh về những phạm vi ban đầu trông như thế nào.

nhập mô tả hình ảnh ở đây

Sau khi nhấp vào mục đầu tiên, các phạm vi bây giờ trông như thế này:

nhập mô tả hình ảnh ở đây

Lưu ý rằng một thuộc tính mới selectedđã được tạo trên phạm vi ngRepeat. Phạm vi bộ điều khiển 003 không được thay đổi.

Bạn có thể đoán điều gì sẽ xảy ra khi chúng ta nhấp vào mục thứ hai:

nhập mô tả hình ảnh ở đây


Vì vậy, vấn đề của bạn thực sự không phải do ngRepeat gây ra - mà là do vi phạm quy tắc vàng trong AngularJS. Cách để khắc phục nó là chỉ cần sử dụng một thuộc tính đối tượng:

$scope.state = { selected: undefined };
<li ng-repeat="name in names"
    ng-class="{ active: $index == state.selected }"
    ng-click="state.selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

Đây là JSFiddle thứ hai cho thấy điều này cũng hoạt động.

Dưới đây là những gì phạm vi trông giống như ban đầu:

nhập mô tả hình ảnh ở đây

Sau khi nhấp vào mục đầu tiên:

nhập mô tả hình ảnh ở đây

Ở đây, phạm vi bộ điều khiển đang bị ảnh hưởng, như mong muốn.

Ngoài ra, để chứng minh rằng điều này sẽ vẫn hoạt động với chỉ thị của bạn với một phạm vi cô lập (bởi vì, một lần nữa, điều này không liên quan đến vấn đề của bạn), đây cũng là một JSFiddle cho điều đó, chế độ xem phải phản ánh đối tượng. Bạn sẽ lưu ý rằng thay đổi cần thiết duy nhất là sử dụng một đối tượng thay vì một nguyên thủy .

Phạm vi ban đầu:

nhập mô tả hình ảnh ở đây

Phạm vi sau khi nhấp vào mục đầu tiên:

nhập mô tả hình ảnh ở đây

Để kết luận: một lần nữa, vấn đề của bạn không phải với phạm vi cô lập và không phải với cách hoạt động của ngRepeat. Vấn đề của bạn là bạn đang vi phạm một quy tắc được cho là dẫn đến vấn đề này. Các mô hình trong AngularJS phải luôn có một ..


Các thử nghiệm của tôi với các chỉ thị có phạm vi cô lập và liên kết dữ liệu 2 chiều khiến tôi tin rằng .quy tắc vàng chỉ bắt buộc đối với các phạm vi có tính kế thừa nguyên mẫu. Với phạm vi cô lập, các biến mà bạn quan tâm được xác định trong scope: {}định nghĩa trong chỉ thị, và do đó các biến đó sẽ tồn tại trong phạm vi cô lập ngay từ đầu. Ngoài ra, chúng không che các biến cha vì phạm vi cô lập không kế thừa nguyên mẫu từ phạm vi cha. tức là không có phạm vi "cha" để che.
Deepak Nulu

1
Cũng nên nói: chỉ thị của bạn không tạo phạm vi con, nhưng nó có thể dễ dàng được sử dụng trong ngữ cảnh yêu cầu phạm vi con. ngRepeat là một trường hợp. Việc ghép tạng cũng vậy. Tin tưởng tôi - sử dụng ..
Josh David Miller

1
Thảo luận trên AngularJS Google Group , nơi @JoshDavidMiller cuối cùng đã có thể giải tỏa sự nhầm lẫn của tôi!
Deepak Nulu

2
Các bạn lấy tất cả các sơ đồ thú vị này từ đâu? Tôi đã thấy một người dùng khác đăng những điều này.
Asad Saeeduddin

3
@ Xin chào, tôi vừa mới đưa công cụ này lên GitHub, nó có tên là Peri $ scope .
Mark Rajcok

6

Nếu không trực tiếp cố gắng tránh trả lời các câu hỏi của bạn, thay vào đó hãy xem các câu hỏi khó sau:

http://jsfiddle.net/dVPLM/

Điểm mấu chốt là thay vì cố gắng chống lại và thay đổi hành vi thông thường của Angular, bạn có thể cấu trúc chỉ thị của mình để làm việc thay ng-repeatvì cố gắng ghi đè nó.

Trong mẫu của bạn:

    <name-row 
        in-names-list="names"
        io-selected="selected">
    </name-row>

Trong chỉ thị của bạn:

    template:
'        <ul>' +      
'            <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >' +
'                <a ng-click="setSelected($index)">' +
'                    {{$index}} - {{name.first}} {{name.last}}' +
'                </a>' +
'            </li>' +
'        </ul>'

Để trả lời câu hỏi của bạn:

  • ng-repeat sẽ tạo một phạm vi, bạn thực sự không nên cố gắng thay đổi điều này.
  • Ưu tiên trong các chỉ thị không chỉ là thứ tự thực thi - hãy xem: AngularJS: Trình biên dịch HTML sắp xếp thứ tự biên dịch như thế nào?
  • Trong Batarang, nếu bạn kiểm tra tab hiệu suất, bạn có thể thấy các biểu thức được ràng buộc cho từng phạm vi và kiểm tra xem điều này có phù hợp với mong đợi của bạn không.

Cảm ơn bạn đã phản hồi nhanh chóng. Nếu những gì tôi đang cố gắng đi ngược lại vấn đề, tôi hơi buồn (AngularJS dù sao cũng là một khung công tác tuyệt vời). Một chỉ thị có nghĩa là một thành phần có thể sử dụng lại. Khi nó được viết, tác giả không cần phải lo lắng về việc nó có được sử dụng với dấu ng-repeathay không. Có thể khi nó được viết lần đầu tiên, nó không bao giờ được sử dụng với ng-repeat. Và một lúc nào đó trong tương lai, nó có thể quen dần ng-repeatvà tại thời điểm đó, nó sẽ hoạt động mà không cần viết lại. Hy vọng rằng bản phát hành AngularJS trong tương lai sẽ làm được điều này.
Deepak Nulu

Tôi muốn làm rõ nhận xét của tôi ở trên. Tôi nghĩ rằng có thể viết một chỉ thị mà không cần lo lắng về việc liệu nó có đang được sử dụng với ng-repeathay không. Nhưng có vẻ như tôi sẽ phải chuyển một hàm vào chỉ thị để nó có thể sửa đổi các biến trong phạm vi cha, thay vì có thể thay đổi ràng buộc hai chiều trong phạm vi riêng của chỉ thị. Hai chiều ràng buộc và chỉ thị có thể tái sử dụng là hai điều yêu thích hàng đầu của tôi về AngularJS và ng-repeatđang chứng tỏ mình là một con ruồi trong thuốc mỡ. Có lẽ tôi có thể viết một cái ng-repeattương đương mà không tạo ra phạm vi riêng của nó.
Deepak Nulu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.