Directive แยกขอบเขตด้วยขอบเขต ng-repeat ใน AngularJS


95

ฉันมีคำสั่งที่มีขอบเขตแยก (เพื่อให้ฉันสามารถใช้คำสั่งซ้ำในที่อื่น ๆ ได้) และเมื่อฉันใช้คำสั่งนี้กับ an ng-repeatมันก็ไม่ทำงาน

ฉันได้อ่านเอกสารประกอบและคำตอบของ Stack Overflow ทั้งหมดในหัวข้อนี้และเข้าใจปัญหา ฉันเชื่อว่าฉันได้หลีกเลี่ยง gotcha ปกติทั้งหมด

ดังนั้นฉันจึงเข้าใจว่ารหัสของฉันล้มเหลวเนื่องจากขอบเขตที่สร้างขึ้นโดยng-repeatคำสั่ง คำสั่งของฉันสร้างขอบเขตแยกและทำการผูกข้อมูลสองทางกับวัตถุในขอบเขตหลัก คำสั่งของฉันจะกำหนดค่าออบเจ็กต์ใหม่ให้กับตัวแปรที่ถูกผูกไว้นี้และจะทำงานได้อย่างสมบูรณ์เมื่อใช้คำสั่งของฉันโดยไม่มีng-repeat(ตัวแปรหลักได้รับการอัปเดตอย่างถูกต้อง) อย่างไรก็ตามng-repeatการกำหนดจะสร้างตัวแปรใหม่ในng-repeatขอบเขตและตัวแปรหลักไม่เห็นการเปลี่ยนแปลง ทั้งหมดนี้เป็นไปตามที่คาดไว้ตามสิ่งที่ฉันได้อ่าน

ฉันยังได้อ่านว่าเมื่อมีคำสั่งหลายคำสั่งในองค์ประกอบหนึ่ง ๆ จะมีการสร้างขอบเขตเดียว และpriorityสามารถตั้งค่าa ในแต่ละคำสั่งเพื่อกำหนดลำดับที่จะนำคำสั่งไปใช้ คำสั่งจะเรียงลำดับตามลำดับความสำคัญจากนั้นจึงเรียกฟังก์ชันคอมไพล์ (ค้นหาลำดับความสำคัญของคำที่http://docs.angularjs.org/guide/directive )

ดังนั้นฉันหวังว่าฉันจะสามารถใช้ลำดับความสำคัญเพื่อให้แน่ใจว่าคำสั่งของฉันทำงานก่อนและจบลงด้วยการสร้างขอบเขตแยกและเมื่อng-repeatรันมันจะใช้ขอบเขตแยกซ้ำแทนการสร้างขอบเขตที่สืบทอดต้นแบบมาจากขอบเขตหลัก เอกสารระบุว่าที่วิ่งสั่งที่ระดับความสำคัญng-repeat 1000ไม่ชัดเจนว่า1เป็นระดับความสำคัญสูงกว่าหรือระดับความสำคัญต่ำกว่า เมื่อผมใช้ระดับความสำคัญในคำสั่งของฉันมันไม่ได้สร้างความแตกต่างดังนั้นฉันพยายาม1 2000แต่นั่นทำให้สิ่งต่าง ๆ แย่ลง: การผูกสองทางundefinedของฉันกลายเป็นและคำสั่งของฉันไม่แสดงอะไรเลย

ผมได้สร้างไวโอลินเพื่อแสดงให้เห็นปัญหาของฉัน ฉันได้แสดงความคิดเห็นเกี่ยวกับpriorityการตั้งค่าในคำสั่งของฉัน ฉันมีรายการชื่อออบเจ็กต์และคำสั่งที่เรียกname-rowซึ่งแสดงฟิลด์ชื่อและนามสกุลในออบเจ็กต์ชื่อ เมื่อชื่อที่ปรากฏถูกคลิกฉันต้องการให้ตั้งค่าselectedตัวแปรในขอบเขตหลัก อาร์เรย์ของชื่อselectedตัวแปรจะถูกส่งผ่านไปยังname-rowคำสั่งโดยใช้การผูกข้อมูลสองทาง

ฉันรู้วิธีทำให้สิ่งนี้ทำงานได้โดยเรียกใช้ฟังก์ชันในขอบเขตหลัก ฉันยังรู้ด้วยว่าถ้าselectedอยู่ในวัตถุอื่นและฉันผูกกับวัตถุภายนอกสิ่งต่างๆก็จะใช้ได้ แต่ฉันไม่สนใจวิธีแก้ปัญหาเหล่านั้นในขณะนี้

แต่คำถามที่ฉันมีคือ:

  • ฉันจะป้องกันไม่ให้ng-repeatสร้างขอบเขตที่สืบทอดต้นแบบจากขอบเขตหลักและใช้ขอบเขตแยกของคำสั่งแทนได้อย่างไร
  • เหตุใดระดับความสำคัญ2000ในคำสั่งของฉันจึงไม่ทำงาน
  • การใช้ Batarang สามารถทราบได้หรือไม่ว่าใช้ขอบเขตประเภทใด?

1
โดยปกติคุณไม่ต้องการใช้ขอบเขตแยกหากคำสั่งของคุณจะใช้กับองค์ประกอบเดียวกันกับคำสั่งอื่น ๆ เนื่องจากคุณกำลังสร้างคุณสมบัติขอบเขตของคุณเองและคุณต้องทำงานกับ ng-repeat ฉันขอแนะนำให้ใช้scope: trueสำหรับคำสั่งของคุณ ดูเพิ่มเติม (ถ้าคุณยังไม่ได้ทำ) stackoverflow.com/questions/14914213/… นอกจากนี้การที่คำสั่งจะถูกใช้ในหลาย ๆ ที่ไม่ได้หมายความว่าเราควรใช้ขอบเขตการแยกโดยอัตโนมัติ
Mark Rajcok

ฉันได้อ่านคำตอบของคุณมากมาย (มันยอดเยี่ยมมากขอบคุณที่เขียนให้) แต่ฉันไม่เคยอ่านคำถามของคุณ :-) ฉันอ่านสิ่งที่คุณเชื่อมโยงถึง สำหรับฉันแล้วดูเหมือนว่าคำสั่งขอบเขตแยกไม่สามารถผสมกับคำสั่งอื่น ๆ ได้ ฉันเห็นด้วยกับความเชื่อมั่นว่าคำสั่งดังกล่าวเป็นส่วนประกอบดังนั้นจึงไม่จำเป็นต้องผสมกับคำสั่งอื่น ๆ ข้อยกเว้นหนึ่ง (จนถึงตอนนี้) สำหรับฉันng-repeatคือ ng-repeatฉันคิดว่ามันเป็นสิ่งมีค่าที่จะสามารถที่จะผสมกับคำสั่งแบบสแตนด์อโลน To be
Continue

ต่อจากด้านบน ... ดังนั้นหากควรมีเพียงคำสั่งเดียวที่มีขอบเขตสำหรับองค์ประกอบก็ng-repeatไม่ควรมีขอบเขต ng-repeatการมีขอบเขตเหมาะสมกับกรณีการใช้งานทั่วไปดังนั้นฉันจึงไม่แนะนำให้เปลี่ยน แต่เช่นเดียวกับที่ฉันแสดงความคิดเห็นในคำตอบของ Alex Osborn ฉันคิดว่าฉันจะสร้างคำสั่งซ้ำตามng-repeatที่ไม่ได้สร้างขอบเขตของตัวเอง จากนั้นสามารถใช้สำหรับการทำซ้ำคำสั่งที่มีขอบเขตแยกของตนเอง จะดำเนินต่อไป ...
Deepak Nulu

โค้ดที่ทำซ้ำคำสั่งในขณะนี้จำเป็นต้องทราบว่าจะใช้ng-repeatหรือคำสั่งการทำซ้ำแบบกำหนดขอบเขตเอง ฉันคิดว่ามันก็โอเคสำหรับ "ผู้โทร" ที่จะรู้เรื่องนี้ แต่มันไม่โอเคสำหรับ "callee" (คำสั่งถูกพูดซ้ำ) ที่จะรู้ว่ามีการพูดซ้ำหรือไม่ To be
Continue

เริ่มบ้าคลั่งกับความคิดเห็นที่นี่ ... :-) ngRepeat ต้องสร้างขอบเขตของตัวเอง ทำไมคุณรู้สึกว่าคุณต้องการขอบเขตแยกที่นี่?
Josh David Miller

คำตอบ:


203

เอาล่ะจากความคิดเห็นมากมายด้านบนฉันได้ค้นพบความสับสน ก่อนอื่นให้ความกระจ่างสองสามประเด็น:

  • ngRepeat ไม่มีผลกับขอบเขตการแยกที่คุณเลือก
  • พารามิเตอร์ที่ส่งผ่านไปยัง ngRepeat เพื่อใช้กับแอตทริบิวต์ของคำสั่งของคุณใช้ขอบเขตที่สืบทอดมาจากต้นแบบ
  • เหตุผลที่คำสั่งของคุณใช้ไม่ได้นั้นไม่เกี่ยวข้องกับขอบเขตการแยก

นี่คือตัวอย่างของรหัสเดียวกัน แต่ลบคำสั่ง:

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

นี่คือJSFiddle ที่แสดงให้เห็นว่ามันใช้ไม่ได้ คุณจะได้ผลลัพธ์เดียวกันกับในคำสั่งของคุณ

ทำไมใช้ไม่ได้ เนื่องจากขอบเขตใน AngularJS ใช้การสืบทอดต้นแบบ ค่าselectedขอบเขตผู้ปกครองของคุณเป็นแบบดั้งเดิม ใน JavaScript หมายความว่าจะถูกเขียนทับเมื่อเด็กกำหนดค่าเดียวกัน มีกฎทองใน AngularJS ขอบเขตคือค่ารูปแบบควรเสมอมี.ในพวกเขา นั่นคือพวกเขาไม่ควรเป็นของดั้งเดิม ดูคำตอบ SO นี้สำหรับข้อมูลเพิ่มเติม


นี่คือภาพของขอบเขตในตอนแรก

ป้อนคำอธิบายภาพที่นี่

หลังจากคลิกรายการแรกขอบเขตจะมีลักษณะดังนี้:

ป้อนคำอธิบายภาพที่นี่

สังเกตว่าselectedคุณสมบัติใหม่ถูกสร้างขึ้นบนขอบเขต ngRepeat ขอบเขตของคอนโทรลเลอร์ 003 ไม่ได้รับการเปลี่ยนแปลง

คุณอาจเดาได้ว่าจะเกิดอะไรขึ้นเมื่อเราคลิกที่รายการที่สอง:

ป้อนคำอธิบายภาพที่นี่


ดังนั้นปัญหาของคุณไม่ได้เกิดจาก ngRepeat เลย แต่เกิดจากการฝ่าฝืนกฎทองใน AngularJS วิธีแก้ไขก็แค่ใช้คุณสมบัติของวัตถุ:

$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>

นี่คือJSFiddle ที่สองที่แสดงผลงานนี้ด้วย

นี่คือลักษณะของขอบเขตในตอนแรก:

ป้อนคำอธิบายภาพที่นี่

หลังจากคลิกรายการแรก:

ป้อนคำอธิบายภาพที่นี่

ที่นี่ขอบเขตของคอนโทรลเลอร์จะได้รับผลกระทบตามต้องการ

นอกจากนี้เพื่อพิสูจน์ว่าสิ่งนี้จะยังคงใช้ได้กับคำสั่งของคุณที่มีขอบเขตแยก (เพราะอีกครั้งสิ่งนี้ไม่เกี่ยวข้องกับปัญหาของคุณ) นี่คือJSFiddleสำหรับสิ่งนั้นเช่นกันมุมมองต้องสะท้อนถึงวัตถุ คุณจะได้ทราบว่าการเปลี่ยนแปลงที่จำเป็นเท่านั้นคือการใช้วัตถุแทนการดั้งเดิม

ขอบเขตเริ่มแรก:

ป้อนคำอธิบายภาพที่นี่

ขอบเขตหลังจากคลิกที่รายการแรก:

ป้อนคำอธิบายภาพที่นี่

สรุป: อีกครั้งปัญหาของคุณไม่ได้อยู่ที่ขอบเขตการแยกและไม่ได้ขึ้นอยู่กับวิธีการทำงานของ ngRepeat ปัญหาของคุณคือคุณทำผิดกฎซึ่งเป็นที่ทราบกันดีว่านำไปสู่ปัญหานี้ โมเดลใน AngularJS ควรมีนามสกุล..


การทดลองของฉันกับคำสั่งที่มีขอบเขตแบบแยกและการผูกข้อมูลแบบ 2 ทางทำให้ฉันเชื่อว่า.กฎทองจำเป็นสำหรับขอบเขตที่มีการสืบทอดต้นแบบเท่านั้น ด้วยขอบเขตการแยกตัวแปรที่คุณสนใจจะถูกกำหนดไว้ในscope: {}คำจำกัดความในคำสั่งดังนั้นตัวแปรเหล่านั้นจะมีอยู่ในขอบเขตการแยกตั้งแต่เริ่มต้น นอกจากนี้ยังไม่ปิดบังตัวแปรพาเรนต์เนื่องจากขอบเขตการแยกไม่ได้รับมรดกจากขอบเขตหลัก กล่าวคือไม่มีขอบเขต "หลัก" ที่จะปิดบัง
Deepak Nulu

1
ควรกล่าวด้วยว่าคำสั่งของคุณไม่ได้สร้างขอบเขตลูก แต่สามารถใช้ในบริบทที่ต้องใช้ขอบเขตลูกได้อย่างง่ายดาย ngRepeat เป็นกรณีหนึ่ง ก็คือการเปลี่ยนแปลง เชื่อฉัน - ใช้ไฟล์..
Josh David Miller

1
การสนทนาเกี่ยวกับ AngularJS Google Groupซึ่งในที่สุด @JoshDavidMiller ก็สามารถเคลียร์ความสับสนของฉันได้!
Deepak Nulu

2
พวกคุณเอาไดอะแกรมเจ๋ง ๆ เหล่านี้มาจากไหน? ฉันเคยเห็นผู้ใช้รายอื่นโพสต์สิ่งเหล่านี้เช่นกัน
Asad Saeeduddin

3
@Asad ผมก็เพิ่งวางเครื่องมือขึ้นไปบน GitHub ก็เรียกว่าPeri $ ขอบเขต
Mark Rajcok

6

โดยไม่พยายามหลีกเลี่ยงการตอบคำถามของคุณโดยตรงให้ดูที่ซอต่อไปนี้:

http://jsfiddle.net/dVPLM/

ประเด็นสำคัญคือแทนที่จะพยายามต่อสู้และเปลี่ยนแปลงพฤติกรรมเดิม ๆ ของ Angular คุณสามารถจัดโครงสร้างคำสั่งของคุณให้ทำงานได้ng-repeatแทนที่จะพยายามแทนที่มัน

ในเทมเพลตของคุณ:

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

ในคำสั่งของคุณ:

    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>'

เพื่อตอบคำถามของคุณ:

  • ng-repeat จะสร้างขอบเขตคุณไม่ควรพยายามเปลี่ยนแปลงสิ่งนี้
  • ลำดับความสำคัญในคำสั่งไม่ได้เป็นเพียงแค่คำสั่งการดำเนินการเท่านั้นดู: AngularJS: คอมไพเลอร์ HTML จัดเรียงลำดับการคอมไพล์อย่างไร
  • ใน Batarang หากคุณตรวจสอบแท็บประสิทธิภาพคุณจะเห็นนิพจน์ที่ผูกไว้สำหรับแต่ละขอบเขตและตรวจสอบว่าตรงกับความคาดหวังของคุณหรือไม่

ขอบคุณสำหรับการตอบกลับอย่างรวดเร็ว หากสิ่งที่ฉันพยายามขัดกับเมล็ดพืชฉันก็เสียใจเล็กน้อย (AngularJS เป็นเฟรมเวิร์กที่ยอดเยี่ยม) คำสั่งหมายถึงส่วนประกอบที่ใช้ซ้ำได้ เมื่อเขียนแล้วผู้เขียนไม่ควรกังวลว่าจะใช้กับไฟล์ng-repeatหรือไม่ ng-repeatบางทีเมื่อมันเป็นครั้งแรกที่เขียนก็ไม่เคยนำมาใช้กับ และบางครั้งในอนาคตมันอาจจะชินng-repeatและเมื่อถึงเวลานั้นมันควรจะใช้งานได้โดยไม่ต้องเขียนซ้ำ หวังว่าการเปิดตัว AngularJS ในอนาคตจะทำให้เป็นไปได้
Deepak Nulu

ฉันอยากจะชี้แจงความคิดเห็นของฉันด้านบน ฉันคิดว่ามันเป็นไปได้ที่จะเขียนคำสั่งที่ไม่ต้องกังวลว่าจะถูกใช้ด้วยng-repeatหรือไม่ แต่ดูเหมือนว่าฉันจะต้องส่งผ่านฟังก์ชันไปยังคำสั่งเพื่อให้สามารถแก้ไขตัวแปรในขอบเขตพาเรนต์แทนที่จะสามารถกลายพันธุ์การผูกแบบสองทางในขอบเขตของคำสั่งเอง คำสั่งการผูกแบบสองทางและใช้ซ้ำได้คือสองสิ่งที่ฉันชอบมากที่สุดเกี่ยวกับ AngularJS และng-repeatพิสูจน์ได้ว่าเป็นเรื่องที่น่าสนใจในครีม บางทีฉันสามารถเขียนสิ่งng-repeatเทียบเท่าที่ไม่ได้สร้างขอบเขตของมันเอง
Deepak Nulu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.