การผูกข้อมูลทำงานใน AngularJS อย่างไร


1956

การผูกข้อมูลทำงานในAngularJSกรอบงานอย่างไร

ฉันยังไม่พบรายละเอียดทางเทคนิคบนเว็บไซต์ของพวกเขา มันชัดเจนมากขึ้นหรือน้อยลงว่ามันทำงานอย่างไรเมื่อมีการเผยแพร่ข้อมูลจากมุมมองไปยังโมเดล แต่ AngularJS ติดตามการเปลี่ยนแปลงคุณสมบัติของโมเดลโดยไม่มี setters และ getters อย่างไร

ฉันพบว่ามีผู้ดู JavaScriptที่สามารถใช้งานได้ แต่พวกเขายังไม่ได้รับการสนับสนุนในInternet Explorer 6และInternet Explorer 7 ดังนั้น AngularJS รู้ได้อย่างไรว่าฉันเปลี่ยนตัวอย่างต่อไปนี้และสะท้อนการเปลี่ยนแปลงนี้ในมุมมอง

myobject.myproperty="new value";

10
โปรดทราบว่าเนื่องจาก angular 1.0.0rc1 คุณต้องระบุ ng-model-instant ( docs-next.angularjs.org/api/… ) เพื่อให้ moder ของคุณอัพเดทอย่างบ้าคลั่ง มิฉะนั้นจะมีการอัปเดตเมื่อเหตุการณ์เบลอ
Sotomajor

8
เห็นได้ชัดว่าลิงก์ของ Marcello นั้นแตกหักดังนั้นจึงเป็นเช่นนี้อีกครั้ง: github.com/mhevery/angular.js/blob/master/docs/content/guide/?hl=th
riffraff

6
@orian ลิงก์นั้นไม่ดี อัปเดตเป็น (ฉันถือว่า) เหมือนกัน - docs.angularjs.org/guide/databinding
Kevin Meredith

11
สำหรับผู้ที่ยังอ่านคำถามนี้อยู่โปรดทราบว่า Angular 2.0 มีการเปลี่ยนแปลงอย่างมากเกี่ยวกับการจัดทำฐานข้อมูลตั้งแต่ Angular 1.x เพื่อทำงานกับส่วนประกอบของเว็บและแก้ไขปัญหามากมายในคำตอบด้านล่าง
สิงหาคม

คำตอบ:


2744

AngularJS จดจำค่าและเปรียบเทียบกับค่าก่อนหน้า นี่เป็นการตรวจสอบขั้นพื้นฐานที่สกปรก หากมีการเปลี่ยนแปลงค่ามันจะทำการเปลี่ยนแปลงเหตุการณ์

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

ในการเปรียบเทียบการตรวจสอบสกปรก (AngularJS) เทียบกับการเปลี่ยนฟัง ( KnockoutJSและBackbone.js ): ในขณะที่การตรวจสอบสกปรกอาจดูเรียบง่ายและไม่มีประสิทธิภาพ ในขณะที่ผู้ฟังการเปลี่ยนแปลงมีกรณีมุมแปลก ๆ มากมายและต้องการสิ่งต่าง ๆ เช่นการติดตามการพึ่งพาเพื่อให้ถูกต้องมากขึ้น การติดตามการพึ่งพา KnockoutJS เป็นคุณสมบัติที่ชาญฉลาดสำหรับปัญหาที่ AngularJS ไม่มี

ปัญหาเกี่ยวกับผู้ฟังการเปลี่ยนแปลง:

  • ไวยากรณ์ผิดพลาดเนื่องจากเบราว์เซอร์ไม่สนับสนุนอย่างเป็นทางการ ใช่มีพร็อกซี่ แต่ไม่ถูกต้องทางอรรถศาสตร์ในทุกกรณีและแน่นอนไม่มีพร็อกซีในเบราว์เซอร์เก่า บรรทัดล่างคือการตรวจสอบที่สกปรกช่วยให้คุณทำPOJOในขณะที่ KnockoutJS และ Backbone.js บังคับให้คุณรับมรดกจากคลาสของพวกเขาและเข้าถึงข้อมูลของคุณผ่าน accessors
  • เปลี่ยนการเชื่อมต่อกัน สมมติว่าคุณมีรายการมากมาย สมมติว่าคุณต้องการเพิ่มรายการลงในอาร์เรย์เนื่องจากคุณวนลูปที่จะเพิ่มทุกครั้งที่คุณเพิ่มคุณกำลังยิงเหตุการณ์ที่เกิดขึ้นซึ่งเป็นการแสดงผล UI สิ่งนี้แย่มากสำหรับประสิทธิภาพ สิ่งที่คุณต้องการคือการอัปเดต UI เพียงครั้งเดียวในตอนท้าย กิจกรรมการเปลี่ยนแปลงนั้นละเอียดเกินไป
  • ผู้ฟังการเปลี่ยนแปลงเริ่มทำงานใน setter ทันทีซึ่งเป็นปัญหาเนื่องจากผู้ฟังการเปลี่ยนแปลงสามารถเปลี่ยนแปลงข้อมูลได้เพิ่มเติมซึ่งทำให้เกิดเหตุการณ์การเปลี่ยนแปลงมากขึ้น สิ่งนี้ไม่ดีเนื่องจากในสแต็กของคุณคุณอาจมีเหตุการณ์การเปลี่ยนแปลงหลายอย่างเกิดขึ้นพร้อมกัน สมมติว่าคุณมีสองอาร์เรย์ที่จำเป็นต้องถูกซิงค์ด้วยเหตุผลใดก็ตาม คุณสามารถเพิ่มหนึ่งหรืออย่างอื่นเท่านั้น แต่ทุกครั้งที่คุณเพิ่มเหตุการณ์ไฟเปลี่ยนแปลงซึ่งขณะนี้มีมุมมองที่ไม่สอดคล้องกันของโลก นี่เป็นปัญหาที่คล้ายกันมากกับการล็อกเธรดซึ่ง JavaScript จะหลีกเลี่ยงเนื่องจากการเรียกกลับแต่ละครั้งจะดำเนินการโดยเฉพาะและเสร็จสิ้น เหตุการณ์การเปลี่ยนแปลงเกิดขึ้นเนื่องจากผู้ตั้งค่าสามารถมีผลที่ตามมาซึ่งไม่ได้ตั้งใจและไม่ชัดเจนซึ่งจะสร้างปัญหาเธรดอีกครั้ง ปรากฎว่าสิ่งที่คุณต้องการจะทำคือการประหารชีวิตผู้ฟังและรับประกัน

แล้วประสิทธิภาพล่ะ

ดังนั้นอาจดูเหมือนว่าเราช้าเนื่องจากการตรวจสอบสกปรกไม่มีประสิทธิภาพ นี่คือที่ที่เราต้องดูจำนวนจริงมากกว่าแค่มีข้อโต้แย้งเชิงทฤษฎี แต่ก่อนอื่นมานิยามข้อ จำกัด กันก่อน

มนุษย์คือ:

  • ช้า - สิ่งใดที่เร็วกว่า 50 มิลลิวินาทีจะไม่สามารถมองเห็นได้โดยมนุษย์และถือได้ว่าเป็น "ทันที"

  • ถูก จำกัด - คุณไม่สามารถแสดงข้อมูลมากกว่า 2,000 ชิ้นต่อคนในหน้าเดียว อะไรที่มากกว่า UI ที่แย่จริงๆและมนุษย์ก็ไม่สามารถดำเนินการได้

ดังนั้นคำถามที่แท้จริงคือ: คุณสามารถเปรียบเทียบเบราว์เซอร์ใน 50 ms ได้กี่ครั้ง นี่เป็นคำถามที่ยากที่จะตอบคำถามเนื่องจากมีหลายปัจจัยที่เข้ามามีบทบาท แต่นี่เป็นกรณีทดสอบ: http://jsperf.com/angularjs-digest/6ซึ่งสร้างผู้ดู 10,000 คน บนเบราว์เซอร์รุ่นใหม่ใช้เวลาเพียง 6 มิลลิวินาที บนInternet Explorer 8ใช้เวลาประมาณ 40 ms อย่างที่คุณเห็นนี่ไม่ใช่ปัญหาแม้แต่เบราว์เซอร์ที่ทำงานช้าในปัจจุบัน มีข้อแม้: การเปรียบเทียบต้องง่ายเพื่อให้พอดีกับเวลาที่กำหนด ... น่าเสียดายที่มันง่ายเกินไปที่จะเพิ่มการเปรียบเทียบที่ช้าลงใน AngularJS ดังนั้นมันจึงง่ายต่อการสร้างแอปพลิเคชันที่ช้าเมื่อคุณไม่ทราบว่าคุณเป็นใคร กำลังทำ. แต่เราหวังว่าจะได้คำตอบโดยการจัดทำโมดูลเครื่องมือซึ่งจะแสดงให้คุณทราบว่าการเปรียบเทียบแบบช้านั้นเป็นอย่างไร

ปรากฎว่าวิดีโอเกมและ GPU ใช้วิธีการตรวจสอบที่สกปรกโดยเฉพาะเนื่องจากสอดคล้องกัน ตราบใดที่พวกเขาได้รับอัตราการรีเฟรชของจอภาพ (โดยทั่วไปคือ 50-60 Hz หรือทุกๆ 16.6-20 มิลลิวินาที) ประสิทธิภาพใด ๆ ก็ตามที่เป็นของเสียดังนั้นคุณจึงควรวาดภาพสิ่งต่างๆ


32
@ Mark - ใช่ใน KO คุณเพียงแค่เพิ่ม. extend ({throttle: 500}) เพื่อรอ 500 มิลลิวินาทีหลังจากเหตุการณ์การเปลี่ยนแปลงครั้งสุดท้ายก่อนที่จะลงมือทำ
Daniel Earwicker

158
คำตอบทั้งหมดนี้ยอดเยี่ยมมากนอกเหนือจาก "ตราบใดที่พวกเขาได้รับ 50 fps แล้วการแสดงใด ๆ ที่เป็นของเสียเนื่องจากสายตาของมนุษย์ไม่สามารถชื่นชมมันได้ดังนั้นคุณจึงควรวาดสิ่งต่างๆ คำสั่งนั้นไม่ถูกต้องขึ้นอยู่กับใบสมัครของคุณ สายตาสามารถชื่นชมมากกว่า 50 fps และแน่นอนจากปัญหาต่าง ๆ ของ VR show (อ่านล่าสุดจาก John Carmack หรือ Michael Abrash โดยเฉพาะ GDC 2013 VR talk หลัง) จริง ๆ แล้วช้า 50 fps นอกจากนั้นคำตอบของคุณก็ยอดเยี่ยม ฉันไม่ต้องการกระจายข้อมูลที่ผิด
Nate Bundy

10
@DavidRivers เราเป็นไมโครวินาทีเช่นเดียวกับในโปรแกรม uTorrent 1μs = 0.000001s
Thorgeir

33
คำสั่งนั้นสามารถพูดได้อย่างง่ายดายในทางกลับกันว่า "การตรวจสอบสกปรกเป็นคุณสมบัติที่ชาญฉลาดสำหรับปัญหาที่ไม่มีสิ่งที่น่าพิศวง" ES6 ใช้สิ่งที่สามารถสังเกตได้และเชิงมุมก็กำจัดการตรวจสอบที่สกปรก โลกแห่งความจริงจมอยู่กับคำตอบนี้และแสดงว่ามันเป็นเท็จ
รูปกรวย

17
"สิ่งใดที่เร็วกว่า 50 มิลลิวินาทีที่มนุษย์ไม่อาจมองเห็นได้" ไม่เป็นความจริง ในการทดสอบเราพบว่าลูกค้าของเราสามารถแยกแยะระหว่างเวลาแฝงการอัปเดต 50ms (20fps) และ 16.6ms update latency (60fps) ได้อย่างง่ายดาย ฉากที่วิ่งด้วยความเร็วเดิมจะทำให้คะแนนโดยรวมแย่ลง "รู้สึกอย่างไร" เรตติ้งแม้ว่าผู้คนจะไม่ได้ลงทะเบียนเฟรมก็ตาม
Crashworks

323

Misko ให้คำอธิบายที่ยอดเยี่ยมเกี่ยวกับวิธีการผูกข้อมูล แต่ฉันต้องการเพิ่มมุมมองของฉันเกี่ยวกับปัญหาด้านประสิทธิภาพด้วยการผูกข้อมูล

ตามที่ Misko กล่าวไว้การผูกประมาณ 2,000 ครั้งเป็นจุดที่คุณเริ่มเห็นปัญหา แต่คุณไม่ควรมีข้อมูลมากกว่า 2000 ชิ้นในหน้าเว็บ สิ่งนี้อาจเป็นจริง แต่ผู้ใช้จะมองไม่เห็นการเชื่อมโยงข้อมูลทุกครั้ง เมื่อคุณเริ่มต้นสร้างการเรียงลำดับของวิดเจ็ตหรือตารางข้อมูลใด ๆ ที่มีสองทางที่มีผลผูกพันคุณสามารถได้อย่างง่ายดายตี 2000 ผูก, โดยไม่ต้องมี UX ที่ไม่ดี

ตัวอย่างเช่นลองใช้กล่องคำสั่งผสมที่คุณสามารถพิมพ์ข้อความเพื่อกรองตัวเลือกที่มี การควบคุมประเภทนี้อาจมีประมาณ 150 รายการและยังคงสามารถใช้งานได้สูง ถ้ามันมีคุณสมบัติพิเศษบางอย่าง (เช่นคลาสที่เฉพาะเจาะจงในตัวเลือกที่เลือกในปัจจุบัน) คุณเริ่มได้รับการผูก 3-5 ตัวต่อตัวเลือก วางวิดเจ็ตสามรายการนี้ไว้ในหน้าหนึ่ง (เช่นอันหนึ่งเพื่อเลือกประเทศอีกอันหนึ่งเพื่อเลือกเมืองในประเทศดังกล่าวและอีกอันเพื่อเลือกโรงแรม) และคุณอยู่ระหว่าง 1,000 ถึง 2,000 ครั้งแล้ว

หรือพิจารณาตารางข้อมูลในเว็บแอปพลิเคชันองค์กร 50 แถวต่อหน้าไม่มีเหตุผลซึ่งแต่ละแถวอาจมี 10-20 คอลัมน์ หากคุณสร้างสิ่งนี้ด้วย ng-repeats และ / หรือมีข้อมูลในบางเซลล์ที่ใช้การเชื่อมคุณสามารถเข้าใกล้การเชื่อม 2000 รายการด้วยตารางนี้เพียงอย่างเดียว

ฉันพบว่านี่เป็นปัญหาใหญ่เมื่อทำงานกับ AngularJS และวิธีแก้ปัญหาเดียวที่ฉันพบคือการสร้างวิดเจ็ตโดยไม่ต้องใช้การผูกสองทางแทนที่จะใช้ ngOnce การลงทะเบียนนักดูและกลอุบายที่คล้ายกันหรือสร้าง คำสั่งที่สร้าง DOM ด้วยการจัดการ jQuery และ DOM ฉันรู้สึกว่านี่เป็นการเอาชนะจุดประสงค์ของการใช้ Angular ตั้งแต่แรก

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

TL; DR
การเชื่อมโยงข้อมูลอาจทำให้เกิดปัญหาประสิทธิภาพการทำงานในหน้าเว็บที่ซับซ้อน


26
ใช่ฉันที่สองนี้ ความรับผิดชอบหลักของแอพของเราคือการแสดงการเชื่อมต่อระหว่างเอนทิตีที่แตกต่างกัน เพจที่ระบุอาจมี 10 ส่วน แต่ละส่วนมีตาราง แต่ละตารางมีฟิลเตอร์หัวพิมพ์ 2-5 แต่ละตารางมี 2-5 คอลัมน์แต่ละแถวมี 10 แถว เร็วมากที่เราพบเจอกับปัญหาที่สมบูรณ์และไปกับตัวเลือก "เทคนิคที่คล้ายคลึงกัน"
Scott Silvi

10
เป็นธรรมหรือไม่ที่จะบอกว่า Angular ไม่เพียง แต่เกี่ยวกับการผูกข้อมูลและแอพบางตัวอาจไม่ต้องการใช้คุณสมบัตินี้ด้วยเหตุผลที่คนอื่นอ้างถึงอย่างแน่นอน ฉันคิดว่าแนวทางของ DI และต้นแบบนั้นคุ้มค่ามาก การผูกอัตโนมัติวิเศษนั้นเป็นสิ่งที่ดี แต่ในทุกการใช้งานที่มีอยู่จะมีการเสียประสิทธิภาพ วิธีการของ Angular นั้นเหนือกว่าเนื้อหาส่วนใหญ่ของเว็บแอพพลิเคชั่น CRUD และผู้คนต่างก็ชนกำแพงกันด้วยการพยายามทำให้สุดขั้ว มันจะดีถ้ามีวิธีอื่นในการรองรับการฟังเหตุการณ์ แต่บางทีมันอาจซับซ้อนเกินไปสำหรับเฟรมเวิร์กเดียว?
Jason Boyd

8
ตอนนี้ Angular มีทางเดียวและผูกฐานข้อมูลหนึ่งครั้งเพื่อช่วยแก้ไขปัญหานี้ นอกจากนี้ตอนนี้มีดัชนีสำหรับแหล่งทวนของคุณซึ่งช่วยให้คุณสามารถแก้ไขรายการโดยไม่ต้องสร้าง dom ใหม่สำหรับเนื้อหาทั้งหมด
Gaute Løken

6
@MW สุจริตฉันคิดว่าการผูกครั้งเดียวคือในแกน แต่ดูเหมือนว่ามันจะไม่ มันเป็นเพียงสิ่งที่คุณสามารถทำได้เมื่อเขียนคำสั่งของคุณเอง อย่างไรก็ตามมี ux mod สำหรับมัน: github.com/pasvaz/bindonce
Gaute Løken

9
เสียงตะโกนจากอนาคตสำหรับทุกคนที่อ่านสิ่งนี้: การผูกพันครั้งเดียวตอนนี้กลายเป็นคุณสมบัติหลักใน Angular v1.3 อ่านเพิ่มเติมได้ที่นี่: docs.angularjs.org/guide/expression
โนบิตะ

158

โดยการตรวจสอบ$scopeวัตถุที่สกปรก

Angular รักษาความเรียบง่ายarrayของนักดูใน$scopeวัตถุ หากคุณตรวจสอบใด ๆ$scopeคุณจะพบว่ามันมีที่เรียกว่าarray$$watchers

ผู้เฝ้าดูแต่ละคนนั้นเป็นobjectสิ่งที่มีอยู่เหนือสิ่งอื่นใด

  1. นิพจน์ที่ผู้เฝ้าดูกำลังมอนิเตอร์ นี่อาจเป็นattributeชื่อหรือสิ่งที่ซับซ้อนกว่านี้
  2. ค่าที่ทราบล่าสุดของการแสดงออก สามารถตรวจสอบกับค่าที่คำนวณได้ในปัจจุบันของการแสดงออก หากค่าต่างกันผู้เฝ้าสังเกตจะเรียกใช้ฟังก์ชันและทำเครื่องหมาย$scopeว่าสกปรก
  3. ฟังก์ชั่นที่จะใช้งานถ้าตัวเฝ้าดูสกปรก

วิธีกำหนดผู้ดู

มีหลายวิธีในการกำหนดผู้เฝ้าดูใน AngularJS

  • คุณอย่างชัดเจนสามารถบน$watchattribute$scope

    $scope.$watch('person.username', validateUnique);
  • คุณสามารถวางการ{{}}แก้ไขในแม่แบบของคุณ (ผู้เฝ้าดูจะถูกสร้างขึ้นสำหรับคุณในปัจจุบัน$scope)

    <p>username: {{person.username}}</p>
  • คุณสามารถขอคำสั่งเช่นng-modelกำหนดผู้เฝ้าดูคุณ

    <input ng-model="person.username" />

$digestวงจรตรวจสอบดูทั้งหมดกับค่าสุดท้ายของพวกเขา

เมื่อเราโต้ตอบกับ AngularJS ผ่านช่องทางปกติ (ng-model, ng-repeat และอื่น ๆ ) วงจรย่อยจะถูกเรียกใช้โดยคำสั่ง

แยกยวงจรคือการข้ามผ่านความลึกแรกของ$scopeและเด็กทั้งหมด สำหรับแต่ละรายการ$scope objectเราวนซ้ำ$$watchers arrayและประเมินค่านิพจน์ทั้งหมด หากค่านิพจน์ใหม่นั้นแตกต่างจากค่าที่ทราบล่าสุดฟังก์ชันของผู้สังเกตการณ์จะถูกเรียกใช้ ฟังก์ชั่นนี้อาจคอมไพล์ซ้ำส่วนหนึ่งของ DOM, คำนวณค่าอีกครั้ง$scope, เรียกAJAX requestสิ่งที่คุณต้องการให้ทำ

ทุกขอบเขตถูกสำรวจและทุกการแสดงออกของนาฬิกาจะถูกประเมินและตรวจสอบกับค่าสุดท้าย

หากทริกเกอร์ตัว$scopeเตือนทำงานสัญญาณสกปรก

หากทริกเกอร์เฝ้าดูแอพจะรู้ว่ามีบางอย่างเปลี่ยนไปและ$scopeจะมีการทำเครื่องหมายว่าสกปรก

ฟังก์ชั่น Watcher สามารถเปลี่ยนคุณลักษณะอื่น ๆ บนหรือผู้ปกครอง$scope $scopeหาก$watcherมีการเรียกใช้ฟังก์ชันหนึ่งเราไม่สามารถรับประกันได้ว่าฟังก์ชันอื่น ๆ ของเรา$scopeยังคงสะอาดอยู่ดังนั้นเราจึงดำเนินการรอบการย่อยทั้งหมดอีกครั้ง

นี่เป็นเพราะ AngularJS มีการเชื่อมโยงสองทางดังนั้นข้อมูลสามารถถูกส่งกลับไปที่$scopeต้นไม้ เราอาจเปลี่ยนค่าที่สูงกว่า$scopeที่ถูกย่อยแล้ว $rootScopeบางทีเราเปลี่ยนค่าใน

หาก$digestสกปรกเราดำเนินการ$digestรอบทั้งหมดอีกครั้ง

เราวนรอบอย่างต่อเนื่อง$digestจนกว่าวงจรย่อยจะทำความสะอาด ( $watchนิพจน์ทั้งหมดมีค่าเหมือนกันกับรอบก่อนหน้า) หรือถึงขีด จำกัด การแยกย่อย โดยค่าเริ่มต้นขีด จำกัด นี้ถูกตั้งไว้ที่ 10

หากเราถึงขีด จำกัด การแยกย่อย AngularJS จะทำให้เกิดข้อผิดพลาดในคอนโซล:

10 $digest() iterations reached. Aborting!

การแยกย่อยทำได้ยากบนเครื่อง แต่ง่ายสำหรับผู้พัฒนา

อย่างที่คุณเห็นทุกครั้งที่มีการเปลี่ยนแปลงในแอป AngularJS AngularJS จะตรวจสอบผู้เฝ้าดูทุกคนใน$scopeลำดับชั้นเพื่อดูวิธีการตอบสนอง สำหรับนักพัฒนาซอฟต์แวร์นี่เป็นประโยชน์อย่างมากเนื่องจากคุณต้องเขียนรหัสการเดินสายเกือบไม่ได้เลย AngularJS จะสังเกตเห็นว่ามีการเปลี่ยนแปลงค่าและทำให้แอพที่เหลือสอดคล้องกับการเปลี่ยนแปลง

จากมุมมองของตัวเครื่องแม้ว่าจะไม่มีประสิทธิภาพมากและจะทำให้แอปของเราช้าลงหากเราสร้างนักดูมากเกินไป Misko ได้เสนอตัวเลขของนักดูประมาณ 4,000 คนก่อนที่แอปของคุณจะรู้สึกช้าในเบราว์เซอร์รุ่นเก่า

ขีด จำกัด นี้เข้าถึงได้ง่ายหากคุณng-repeatมีขนาดใหญ่JSON arrayเกินไป คุณสามารถลดปัญหานี้ได้โดยใช้คุณสมบัติเช่นการเชื่อมโยงครั้งเดียวเพื่อรวบรวมแม่แบบโดยไม่ต้องสร้างนักดู

วิธีหลีกเลี่ยงการสร้างนักดูมากเกินไป

ทุกครั้งที่ผู้ใช้โต้ตอบกับแอพของคุณผู้ตรวจสอบแต่ละคนในแอปจะได้รับการประเมินอย่างน้อยหนึ่งครั้ง ส่วนใหญ่ของการเพิ่มประสิทธิภาพแอป AngularJS คือการลดจำนวนผู้ดูใน$scopeต้นไม้ของคุณ วิธีง่ายๆในการทำเช่นนี้คือมีผลผูกพันครั้งเดียว

หากคุณมีข้อมูลที่จะไม่เปลี่ยนแปลงบ่อยครั้งคุณสามารถผูกมันได้เพียงครั้งเดียวโดยใช้ไวยากรณ์ :: เช่น:

<p>{{::person.username}}</p>

หรือ

<p ng-bind="::person.username"></p>

การผูกจะถูกทริกเกอร์เมื่อมีการแสดงเท็มเพลตที่มีอยู่และข้อมูลถูกโหลดเข้า$scopeมา

นี่เป็นสิ่งสำคัญอย่างยิ่งเมื่อคุณมีng-repeatสิ่งของหลายอย่าง

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

ขอบคุณ @ user2864740 - ถึงจะเป็นคำตอบที่ดีที่สุดของ Misko เขารู้กรอบดีกว่าใคร ๆ และมันก็ค่อนข้างดีที่เขาเข้าร่วมกับ Stack Overflow ..
superluminary

4
ฉันไม่เห็นด้วยที่คำตอบดังกล่าวควรอยู่ด้านบน มีความแตกต่างระหว่างการรู้บางสิ่งและการเขียนคำตอบที่เกี่ยวข้อง / รายละเอียดสำหรับคำถามที่เฉพาะเจาะจง มีวิธีที่ดีกว่าในการรับรางวัล อย่างไรก็ตาม ..
2864740

1
ฉันไม่สงสัยเช่นนั้นจริง แต่คำถามคำถามและคำตอบคำตอบ :)
user2864740

3
คำตอบที่ดีครอบคลุมถึงพฤติกรรมที่สกปรกและการประเมินผลจริงสิ่งหนึ่งที่ไม่ชัดเจนเกินไปในคำตอบของ Misko
คนที่

3
คำตอบที่ยอดเยี่ยมและมีรายละเอียด @superluminary ขอบคุณสำหรับคำตอบนั้น ยิ่งกว่านั้นหลังจากอ่านคำตอบนี้ฉันมาถึงจุดที่เราต้องไม่เพิ่มการแสดงออกที่ไม่ใช่ idempotent เป็นการแสดงออกที่ดูอยู่
Mangu Singh Rajpurohit

81

นี่คือความเข้าใจพื้นฐานของฉัน มันอาจจะผิด!

  1. รายการจะถูกดูโดยผ่านฟังก์ชั่น (คืนสิ่งที่จะดู) ไปยัง$watchวิธีการ
  2. การเปลี่ยนแปลงรายการที่จับต้องจะต้องทำภายในบล็อคของโค้ดที่ห่อด้วย$applyวิธีการ
  3. ในตอนท้ายของวิธีการเรียกที่ผ่านไปแต่ละนาฬิกาและตรวจสอบเพื่อดูว่าพวกเขามีการเปลี่ยนแปลงตั้งแต่ครั้งสุดท้ายที่วิ่ง$apply$digest$digest
  4. หากพบการเปลี่ยนแปลงใด ๆ ระบบจะเรียกใช้การแยกย่อยอีกครั้งจนกว่าการเปลี่ยนแปลงทั้งหมดจะเสถียร

ในการพัฒนาปกติ, การผูกข้อมูลไวยากรณ์ใน HTML บอกคอมไพเลอร์ AngularJS เพื่อสร้างนาฬิกาสำหรับคุณและวิธีการควบคุมจะดำเนินการภายใน$applyแล้ว ดังนั้นสำหรับนักพัฒนาแอปพลิเคชันมันจึงโปร่งใสทั้งหมด


4
วิธีการสมัครจะเริ่มขึ้นเมื่อใด
numan salati

3
@EliseuMonar การวนลูปย่อยทำงานเป็นผลมาจากเหตุการณ์บางอย่างหรือการเรียกใช้ $ () จะไม่ถูกเรียกเป็นระยะ ๆ ตามตัวจับเวลา ดูฟังก์ชัน $ watch ของ AngularJS ทำงานอย่างไร และการรวมและการย่อยทำงานใน AngularJS อย่างไร
adl

1
@remi ฉันไม่กังวลเกี่ยวกับ AngularJS เวอร์ชันล่าสุด พวกเขาใช้พร็อกซีหรือ Object.observe อยู่แล้ว ถ้าไม่พวกเขายังอยู่ในยุคการตรวจสอบที่สกปรกซึ่งจะสร้างลูปแบบกำหนดเวลาเพื่อดูว่ามีการเปลี่ยนแปลงแอตทริบิวต์ของโมเดลหรือไม่
Eliseu Monar dos Santos

1
ฉันได้อ่านแล้วว่าไดเจสต์จะทำงานได้สูงสุดสิบครั้งsitepoint.com/understanding-angulars-apply-digest
user137717

62

ฉันสงสัยว่าตัวเองในขณะนี้ หากไม่มีตัวตั้งค่าจะAngularJSสังเกตเห็นการเปลี่ยนแปลงของ$scopeวัตถุได้อย่างไร มันสำรวจความคิดเห็นของพวกเขา?

ที่จริงแล้วสิ่งนี้คืออะไร: สถานที่ "ปกติ" ใด ๆ ที่คุณแก้ไขโมเดลนั้นถูกเรียกใช้แล้วจากความกล้าของAngularJSดังนั้นมันจะโทร$applyหาคุณโดยอัตโนมัติหลังจากที่โค้ดของคุณทำงาน สมมติว่าคอนโทรลเลอร์ของคุณมีวิธีการที่เชื่อมโยงng-clickกับองค์ประกอบบางอย่าง เนื่องจากAngularJSเชื่อมโยงการโทรของวิธีการนั้นเข้าด้วยกันสำหรับคุณจึงมีโอกาสที่จะทำ$applyในสถานที่ที่เหมาะสม ในทำนองเดียวกันสำหรับนิพจน์ที่ปรากฏในมุมมองจะถูกดำเนินการโดยAngularJSดังนั้นจึงเป็นเช่นนั้น$applyดังนั้นจึงไม่

เมื่อเอกสารอธิบายเกี่ยวกับการโทร$applyด้วยตนเองเพื่อขอรหัสนอกAngularJSมันพูดถึงรหัสที่เมื่อทำงานไม่ได้เกิดจากAngularJSตัวเองในการโทรสแต็ค


32

การอธิบายด้วยรูปภาพ:

การเชื่อมโยงข้อมูลต้องการการแมป

การอ้างอิงในขอบเขตไม่ใช่การอ้างอิงในแม่แบบ เมื่อคุณผูกข้อมูลวัตถุสองชิ้นคุณต้องมีวัตถุที่สามที่รับฟังวัตถุแรกและปรับเปลี่ยนวัตถุอื่น

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

ที่นี่เมื่อคุณปรับเปลี่ยน<input>คุณสัมผัสข้อมูล ref3 และคลาสสิก mecanism ข้อมูลผูกจะเปลี่ยนข้อมูล ref4 ดังนั้นการ{{data}}แสดงออกอื่น ๆจะย้ายอย่างไร

เหตุการณ์นำไปสู่การสรุป $ ()

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

เชิงมุมรักษาoldValueและnewValueผูกพันทุก และหลังจากเหตุการณ์เชิงมุมทุก$digest()วงวงที่มีชื่อเสียงจะตรวจสอบ WatchList เพื่อดูว่ามีอะไรเปลี่ยนแปลงหรือไม่ เหล่านี้เหตุการณ์เชิงมุมกำลังng-click, ng-change, $httpเสร็จ ... ความ$digest()ประสงค์ห่วงตราบเท่าที่ใด ๆ ที่oldValueแตกต่างจากnewValueแตกต่างจาก

ในภาพก่อนหน้านี้จะสังเกตว่า data-ref1 และ data-ref2 มีการเปลี่ยนแปลง

สรุปผลการวิจัย

มันเหมือนไข่กับไก่นิดหน่อย คุณไม่มีทางรู้ว่าใครจะเริ่ม แต่หวังว่ามันจะได้ผลมากที่สุดตามที่คาดไว้

อีกจุดหนึ่งคือคุณสามารถเข้าใจผลกระทบที่เกิดจากการรวมหน่วยความจำและ CPU ได้ง่าย หวังว่าเดสก์ท็อปจะอ้วนพอที่จะจัดการเรื่องนี้ได้ โทรศัพท์มือถือไม่ค่อยแรง


22

เห็นได้ชัดว่าไม่มีการตรวจสอบเป็นระยะScopeว่ามีการเปลี่ยนแปลงใด ๆ ในวัตถุที่แนบมากับมัน มีการเฝ้าดูวัตถุทั้งหมดที่อยู่ในขอบเขตเท่านั้น ขอบเขต prototypically รักษา$$ ดู Scopeทำซ้ำผ่านสิ่งนี้$$watchersเมื่อ$digestถูกเรียก

Angular เพิ่มตัวเฝ้าดูไปที่ตัวเฝ้าดู $$ สำหรับแต่ละตัวเหล่านี้

  1. {{expression}} - ในเทมเพลตของคุณ (และที่อื่น ๆ ที่มีนิพจน์) หรือเมื่อเรากำหนด ng-model
  2. $ scope $ watch ('expression / function') - ใน JavaScript ของคุณเราสามารถแนบวัตถุขอบเขตสำหรับเชิงมุมเพื่อดู

ฟังก์ชั่น$ watchใช้เวลาในสามพารามิเตอร์:

  1. อย่างแรกคือฟังก์ชั่นเฝ้าดูซึ่งเพิ่งส่งคืนวัตถุหรือเราแค่เพิ่มการแสดงออก

  2. สองฟังก์ชั่นฟังซึ่งจะถูกเรียกเมื่อมีการเปลี่ยนแปลงในวัตถุ ทุกสิ่งเช่นการเปลี่ยนแปลง DOM จะถูกนำมาใช้ในฟังก์ชั่นนี้

  3. ที่สามเป็นพารามิเตอร์ทางเลือกซึ่งจะใช้ในแบบบูล ถ้ามันเป็นเรื่องจริงเชิงมุมลึกก็จะมองวัตถุ & ถ้ามันเป็นมุมที่ผิดพลาดก็แค่อ้างอิงถึงการมองวัตถุนั้น การใช้งาน $ watch อย่างหยาบดูเหมือนว่าจะเป็นเช่นนี้

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

มีสิ่งที่น่าสนใจใน Angular เรียกว่า Digest Cycle $ แยกวงจรเริ่มต้นเป็นผลมาจากการเรียกไปยังขอบเขต $ $ แยกย่อย () สมมติว่าคุณเปลี่ยนรูปแบบ $ scope ในฟังก์ชันตัวจัดการผ่านคำสั่ง ng-click ในกรณีนั้น AngularJS จะทริกเกอร์วงจร $ แยกย่อยโดยอัตโนมัติโดยเรียก $ digest () นอกจาก ng-click ยังมีคำสั่ง / บริการอื่น ๆ ในตัวที่ให้คุณเปลี่ยนรูปแบบ (เช่น ng-model, $ หมดเวลา ฯลฯ ) และทริกเกอร์วงจร $ แยกย่อยโดยอัตโนมัติ การดำเนินการที่คร่าวๆของ $ digest มีลักษณะเช่นนี้

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

หากเราใช้ฟังก์ชันsetTimeout ()ของ JavaScript เพื่ออัปเดตโมเดลขอบเขต Angular จะไม่มีทางรู้ว่าคุณจะเปลี่ยนแปลงอะไร ในกรณีนี้มันเป็นความรับผิดชอบของเราที่จะเรียกใช้ $ ใช้ด้วยตนเอง () ด้วยตนเองซึ่งทำให้เกิดวงจรย่อย $ ในทำนองเดียวกันถ้าคุณมีคำสั่งที่ตั้งค่าฟังเหตุการณ์ DOM และเปลี่ยนบางรุ่นในฟังก์ชั่นการจัดการคุณต้องโทร $ Apply () เพื่อให้การเปลี่ยนแปลงมีผล แนวคิดหลักของการใช้ $ คือเราสามารถรันโค้ดบางอย่างที่ไม่ทราบถึง Angular โค้ดนั้นอาจยังเปลี่ยนแปลงสิ่งต่าง ๆ ในขอบเขต หากเราใส่รหัสนั้นเป็น $ Apply จะดูแลการเรียก $ digest () การใช้งานแบบหยาบของ $ Apply ()

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};

15

AngularJS จัดการกลไกการเชื่อมโยงข้อมูลด้วยความช่วยเหลือของฟังก์ชั่นที่ทรงพลังสามอย่าง: $ watch () , $ digest ()และ$ Apply ()() เวลาส่วนใหญ่ AngularJS จะเรียกใช้ $ scope $ watch () และ $ scope $ digest () แต่ในบางกรณีคุณอาจต้องเรียกใช้ฟังก์ชันเหล่านี้ด้วยตนเองเพื่ออัปเดตด้วยค่าใหม่

$ watch () : -

ฟังก์ชั่นนี้ใช้เพื่อสังเกตการเปลี่ยนแปลงของตัวแปรบน $ scope ยอมรับสามพารามิเตอร์: expression, listener และ object equality โดยที่ listener และ object เท่าเทียมกันเป็นพารามิเตอร์ที่เป็นทางเลือก

$ แยกย่อย () -

ฟังก์ชันนี้วนซ้ำผ่านนาฬิกาทั้งหมดในวัตถุ $ scope และวัตถุลูก $ ขอบเขต
(ถ้ามี) เมื่อ $ digest () ทำซ้ำมากกว่านาฬิกามันจะตรวจสอบว่าค่าของนิพจน์มีการเปลี่ยนแปลงหรือไม่ หากค่ามีการเปลี่ยนแปลง AngularJS จะเรียกผู้ฟังด้วยค่าใหม่และค่าเก่า ฟังก์ชัน $ digest () ถูกเรียกใช้เมื่อใดก็ตามที่ AngularJS คิดว่าจำเป็น ตัวอย่างเช่นหลังจากคลิกปุ่มหรือหลังจากการโทร AJAX คุณอาจมีบางกรณีที่ AngularJS ไม่เรียกใช้ฟังก์ชัน $ digest () สำหรับคุณ ในกรณีนี้คุณต้องโทรหาด้วยตัวเอง

$ ใช้ () -

Angular ทำการอัปเดตอัตโนมัติอย่างน่าอัศจรรย์เฉพาะการเปลี่ยนแปลงโมเดลเหล่านั้นซึ่งอยู่ในบริบท AngularJS เมื่อคุณทำการเปลี่ยนแปลงในโมเดลใด ๆ นอกบริบท Angular (เช่นเหตุการณ์เบราว์เซอร์ DOM, setTimeout, XHR หรือไลบรารีของบุคคลที่สาม) จากนั้นคุณต้องแจ้ง Angular เกี่ยวกับการเปลี่ยนแปลงโดยการเรียก $ Apply () ด้วยตนเอง เมื่อการเรียกใช้ฟังก์ชั่น $ ใช้ () เสร็จสิ้นการเรียก AngularJS $ แยกย่อย () ภายในดังนั้นการเชื่อมโยงข้อมูลทั้งหมดจะได้รับการอัพเดต


7

มันเกิดขึ้นที่ฉันต้องการเชื่อมโยงโมเดลข้อมูลของบุคคลกับฟอร์มสิ่งที่ฉันทำคือการแม็พข้อมูลโดยตรงกับฟอร์ม

ตัวอย่างเช่นถ้าแบบจำลองมีลักษณะดังนี้:

$scope.model.people.name

อินพุตควบคุมของแบบฟอร์ม:

<input type="text" name="namePeople" model="model.people.name">

ด้วยวิธีนี้หากคุณปรับเปลี่ยนค่าของตัวควบคุมวัตถุสิ่งนี้จะถูกสะท้อนโดยอัตโนมัติในมุมมอง

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


14
ฉันอ่านคำตอบนี้ 5 ครั้ง แต่ฉันก็ยังไม่เข้าใจว่ามันหมายถึงอะไร
sbedulin

1
คำตอบดูเหมือนจะเป็นปริศนาสำหรับฉัน
Aman

6
  1. การเชื่อมโยงข้อมูลทางเดียวเป็นวิธีการที่ค่าถูกนำมาจากตัวแบบข้อมูลและแทรกลงในองค์ประกอบ HTML ไม่มีวิธีอัปเดตโมเดลจากมุมมอง มันถูกใช้ในระบบแม่แบบคลาสสิก ระบบเหล่านี้ผูกข้อมูลในทิศทางเดียวเท่านั้น

  2. การเชื่อมโยงข้อมูลในแอพพลิเคชั่น Angular เป็นการซิงโครไนซ์ข้อมูลอัตโนมัติระหว่างโมเดลและส่วนประกอบมุมมอง

การผูกข้อมูลช่วยให้คุณปฏิบัติต่อโมเดลเป็นแหล่งข้อมูลเดียวในแอปพลิเคชันของคุณ มุมมองเป็นภาพของโมเดลทุกครั้ง หากโมเดลมีการเปลี่ยนแปลงมุมมองจะแสดงถึงการเปลี่ยนแปลงและในทางกลับกัน


5

นี่คือตัวอย่างของการโยงข้อมูลกับ AngularJS โดยใช้ฟิลด์อินพุต ฉันจะอธิบายในภายหลัง

รหัส HTML

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

รหัส AngularJS

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

ดังที่คุณเห็นในตัวอย่างด้านบนAngularJSใช้ng-modelฟังและดูสิ่งที่เกิดขึ้นกับองค์ประกอบ HTML โดยเฉพาะในinputฟิลด์ เมื่อมีอะไรเกิดขึ้นทำอะไรสักอย่าง ในกรณีของเราng-modelผูกกับมุมมองของเราโดยใช้สัญลักษณ์หนวด{{}}มีการเชื่อมโยงกับมุมมองของเราโดยใช้สัญกรณ์หนวดสิ่งที่พิมพ์ภายในช่องป้อนข้อมูลจะปรากฏบนหน้าจอทันที และนั่นคือความงามของการเชื่อมโยงข้อมูลโดยใช้ AngularJS ในรูปแบบที่ง่ายที่สุด

หวังว่านี่จะช่วยได้

ดูตัวอย่างการทำงานได้ที่นี่ใน Codepen


5

AngularJs สนับสนุนสองวิธีการผูกข้อมูล
หมายความว่าคุณสามารถเข้าถึงมุมมองข้อมูล-> ตัวควบคุมและตัวควบคุม -> มุมมอง

สำหรับอดีต

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

O / P

Peter

คุณสามารถผูกข้อมูลในng-modelLike: -
2)

<input ng-model="name" />

<div> {{ name }} </div>

ในตัวอย่างด้านบนสิ่งที่ผู้ใช้ป้อนให้มันจะปรากฏใน<div>แท็ก

ถ้าต้องการผูกอินพุตจาก html ไปยังคอนโทรลเลอร์: -
3)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

ที่นี่ถ้าคุณต้องการใช้อินพุตnameในคอนโทรลเลอร์จากนั้น

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-model{{ }}ผูกมุมมองของเราและทำให้มันในการแสดงออก
ng-modelคือข้อมูลที่แสดงต่อผู้ใช้ในมุมมองและข้อมูลที่ผู้ใช้โต้ตอบ
ดังนั้นจึงง่ายต่อการผูกข้อมูลใน AngularJs


4

Angular.js สร้างผู้เฝ้าดูสำหรับทุกรุ่นที่เราสร้างในมุมมอง เมื่อใดก็ตามที่มีการเปลี่ยนแปลงโมเดลคลาส "ng-dirty" จะถูกผนวกเข้ากับโมเดลดังนั้น watcher จะสังเกตเห็นโมเดลทั้งหมดที่มีคลาส "ng-dirty" และอัปเดตค่าในคอนโทรลเลอร์และในทางกลับกัน


3

การผูกข้อมูล:

การผูกข้อมูลคืออะไร

เมื่อใดก็ตามที่ผู้ใช้เปลี่ยนแปลงข้อมูลในมุมมองจะมีการอัพเดตการเปลี่ยนแปลงในโมเดลขอบเขตและ viceversa

มันเป็นไปได้ยังไงกัน?

คำตอบสั้น ๆ : ด้วยความช่วยเหลือของวงจรย่อย

คำอธิบาย: Angular js ตั้งค่าตัวเฝ้าดูในขอบเขตแบบจำลองซึ่งยิงฟังก์ชันผู้ฟังหากมีการเปลี่ยนแปลงรูปแบบ

$scope.$watch('modelVar' , function(newValue,oldValue){

// Dom อัพเดทโค้ดด้วยค่าใหม่

});

ดังนั้นเมื่อไรและอย่างไรฟังก์ชั่นการเฝ้าดูเรียกว่า

ฟังก์ชั่น Watcher เรียกว่าเป็นส่วนหนึ่งของวงจรย่อย

วงจรย่อยจะถูกเรียกใช้โดยอัตโนมัติซึ่งเป็นส่วนหนึ่งของ js เชิงมุมที่สร้างขึ้นในคำสั่ง / บริการเช่น ng-model, ng-bind, $ timeout, ng-click และอื่น ๆ .. ที่ให้คุณกระตุ้นวงจรย่อย

ฟังก์ชั่นวงจรย่อย:

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

กล่าวคือ$rootScope.$apply()

หมายเหตุ: $ Apply () เท่ากับ $ rootScope $ digest () ซึ่งหมายความว่าการตรวจสอบที่สกปรกเริ่มต้นจากรูทหรือด้านบน

คุณสมบัติข้างต้นทำงานในเบราว์เซอร์ IE สำหรับรุ่นที่กล่าวถึงเพียงแค่ทำให้แน่ใจว่าแอปพลิเคชันของคุณเป็นแอปพลิเคชัน angular js ซึ่งหมายความว่าคุณกำลังใช้ไฟล์สคริปต์เฟรมเวิร์ก angularjs ที่อ้างอิงในแท็กสคริปต์

ขอบคุณ.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.