คู่มือการสร้างแอป Ember.js แรกของคุณ

เผยแพร่แล้ว: 2022-03-11

เนื่องจากเว็บแอปพลิเคชันสมัยใหม่ทำมากขึ้นเรื่อยๆ ในฝั่งไคลเอ็นต์ (ความจริงที่ว่าตอนนี้เราเรียกพวกเขาว่า "แอปพลิเคชันเว็บ" ซึ่งต่างจาก "เว็บไซต์" นั้นค่อนข้างจะชัดเจน) มีความสนใจในเฟรมเวิร์กฝั่งไคลเอ็นต์เพิ่มขึ้น . มีผู้เล่นหลายคนในสาขานี้ แต่สำหรับแอปพลิเคชันที่มีฟังก์ชันการทำงานมากมายและชิ้นส่วนที่เคลื่อนไหวได้มากมาย สองรายการมีความโดดเด่นเป็นพิเศษ: Angular.js และ Ember.js

เราได้เผยแพร่ [Angular.js บทช่วยสอน] ที่ครอบคลุมแล้ว[https://www.toptal.com/angular-js/a-step-by-step-guide-to-your-first-angularjs-app] ดังนั้นเราจึง กำลังจะมุ่งเน้นไปที่ Ember.js ในโพสต์นี้ ซึ่งเราจะสร้างแอปพลิเคชัน Ember ที่เรียบง่ายเพื่อจัดทำรายการคอลเลคชันเพลงของคุณ คุณจะได้รู้จักกับหน่วยการสร้างหลักของเฟรมเวิร์กและรับทราบหลักการออกแบบของเฟรมเวิร์ก หากคุณต้องการดูซอร์สโค้ดขณะอ่าน สามารถใช้ได้เป็นร็อกแอนด์โรลบน Github

เราจะสร้างอะไร?

นี่คือสิ่งที่แอป Rock & Roll ของเราจะมีหน้าตาในเวอร์ชันสุดท้าย:

แอปเวอร์ชันสุดท้ายที่มี ember.js

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

เราสามารถแบ่งฟังก์ชันพื้นฐานของแอปออกเป็นขั้นตอนต่อไปนี้:

  1. การคลิก 'เพิ่ม' จะเป็นการเพิ่มศิลปินใหม่ลงในรายการ โดยมีชื่อที่ระบุโดยฟิลด์ 'ศิลปินใหม่' (เช่นเดียวกับเพลงสำหรับศิลปินที่ระบุ)
  2. การล้างฟิลด์ 'ศิลปินใหม่' จะเป็นการปิดใช้งานปุ่ม 'เพิ่ม' (เช่นเดียวกับเพลงสำหรับศิลปินที่ระบุ)
  3. การคลิกชื่อศิลปินจะแสดงเพลงของพวกเขาทางด้านขวา
  4. การคลิกที่ดาวจะให้คะแนนเพลงที่กำหนด

เรามีหนทางอีกยาวไกลในการทำงานนี้ เรามาเริ่มกันเลยดีกว่า

เส้นทาง: กุญแจสู่แอป Ember.js

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

นี่คือเส้นทางสำหรับการสมัครของเรา:

 App.Router.map(function() { this.resource('artists', function() { this.route('songs', { path: ':slug' }); }); });

เรากำหนดเส้นทางทรัพยากร artists และเส้นทาง songs ที่ซ้อนกันอยู่ภายใน คำจำกัดความดังกล่าวจะให้เส้นทางต่อไปนี้แก่เรา:

เส้นทาง

ฉันใช้ปลั๊กอินตัวตรวจสอบ Ember ที่ยอดเยี่ยม (มีทั้งสำหรับ Chrome และ Firefox) เพื่อแสดงให้คุณเห็นเส้นทางที่สร้างขึ้นในลักษณะที่อ่านง่าย นี่คือกฎพื้นฐานสำหรับเส้นทาง Ember ซึ่งคุณสามารถตรวจสอบสำหรับกรณีของเราโดยเฉพาะโดยใช้ตารางด้านบน:

  1. มีเส้นทางการ application โดยนัย

    สิ่งนี้จะเปิดใช้งานสำหรับคำขอ ทั้งหมด (การเปลี่ยนผ่าน)

  2. มีเส้นทาง index โดยนัย

    สิ่งนี้จะถูกป้อนเมื่อผู้ใช้นำทางไปยังรูทของแอปพลิเคชัน

  3. เส้นทางทรัพยากรแต่ละเส้นทางสร้างเส้นทางที่มีชื่อเดียวกันและสร้างเส้นทางดัชนีโดยปริยายด้านล่าง

    เส้นทางดัชนีนี้จะเปิดใช้งานเมื่อผู้ใช้นำทางไปยังเส้นทาง ในกรณีของเรา artists.index จะถูกทริกเกอร์เมื่อผู้ใช้นำทางไปยัง /artists

  4. เส้นทางที่ซ้อนกันแบบธรรมดา (ไม่ใช่ทรัพยากร) จะมีชื่อเส้นทางหลักเป็นคำนำหน้า

    เส้นทางที่เรากำหนดเป็น this.route('songs', ...) จะมีชื่อ artists.songs มันจะถูกทริกเกอร์เมื่อผู้ใช้นำทางไปยัง /artists/pearl-jam หรือ /artists/radiohead

  5. หากไม่ได้ระบุเส้นทาง จะถือว่าเท่ากับชื่อเส้นทาง

  6. หากพาธมี : จะถือว่าเป็นเซ็กเมนต์ไดนามิก

    ชื่อที่กำหนดให้กับมัน (ในกรณีของเรา slug ) จะตรงกับค่าในส่วนที่เหมาะสมของ url ส่วน slug ด้านบนจะมีค่า pearl-jam radiohead ดิโอเฮด หรือค่าอื่นๆ ที่ดึงมาจาก URL

แสดงรายชื่อศิลปิน

ในขั้นแรก เราจะสร้างหน้าจอซึ่งแสดงรายการศิลปินทางด้านซ้าย หน้าจอนี้ควรแสดงให้ผู้ใช้เห็นเมื่อไปที่ /artists/ :

ศิลปิน

เพื่อให้เข้าใจถึงวิธีการแสดงหน้าจอนั้น ได้เวลาแนะนำหลักการออกแบบของ Ember ที่ครอบคลุมอีกประการหนึ่ง นั่นคือ หลักการ ที่มากกว่าการตั้งค่าคอนฟิก ในส่วนด้านบน เราพบว่า /artists เปิดใช้งานเส้นทางของ artists ตามแบบแผน ชื่อของ อ็อบเจ็กต์ เส้นทางนั้นคือ ArtistsRoute เป็นความรับผิดชอบของอ็อบเจ็กต์เส้นทางในการดึงข้อมูลเพื่อให้แอปแสดงผล ที่เกิดขึ้นในโมเดล hook ของเส้นทาง:

 App.ArtistsRoute = Ember.Route.extend({ model: function() { var artistObjects = []; Ember.$.getJSON('http://localhost:9393/artists', function(artists) { artists.forEach(function(data) { artistObjects.pushObject(App.Artist.createRecord(data)); }); }); return artistObjects; } });

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

อืม—ที่จริงแล้ว เราไม่จำเป็นต้องกำหนดตัวควบคุม ณ จุดนี้! Ember ฉลาดพอที่จะสร้างคอนโทรลเลอร์ เมื่อจำเป็น และตั้งค่าแอตทริบิวต์ M.odel ของคอนโทรลเลอร์ให้เป็นค่าส่งคืนของตัวเบ็ดโมเดล นั่นคือรายชื่อศิลปิน (อีกครั้งนี้เป็นผลมาจากกระบวนทัศน์ 'การประชุมผ่านการกำหนดค่า') เราสามารถลดระดับลงหนึ่งเลเยอร์และสร้างเทมเพลตเพื่อแสดงรายการ:

 <script type="text/x-handlebars" data-template-name="artists"> <div class="col-md-4"> <div class="list-group"> {{#each model}} {{#link-to "artists.songs" this class="list-group-item artist-link"}} {{name}} <span class="pointer glyphicon glyphicon-chevron-right"></span> {{/link-to}} {{/each}} </div> </div> <div class="col-md-8"> <div class="list-group"> {{outlet}} </div> </div> </script>

หากดูคุ้นเคย นั่นเป็นเพราะ Ember.js ใช้เทมเพลต Handlebars ซึ่งมีรูปแบบและตัวช่วยที่ง่ายมาก แต่ไม่อนุญาตให้ใช้ตรรกะที่ไม่สำคัญ (เช่น ORing หรือ ANDing เงื่อนไขในเงื่อนไข)

ในเทมเพลตข้างต้น เราทำซ้ำผ่านโมเดล (ตั้งค่าก่อนหน้านี้โดยเส้นทางไปยังอาร์เรย์ที่มีศิลปินทั้งหมด) และสำหรับแต่ละรายการในนั้น เราสร้างลิงก์ที่นำเราไปยังเส้นทาง artists.songs สำหรับศิลปินนั้น ลิงค์นี้มีชื่อศิลปิน ตัวช่วย #each ในแฮนด์บาร์จะเปลี่ยนขอบเขตภายในเป็นรายการปัจจุบัน ดังนั้น {{name}} จะอ้างอิงถึงชื่อศิลปินที่อยู่ภายใต้การทำซ้ำเสมอ

เส้นทางซ้อนสำหรับมุมมองที่ซ้อนกัน

อีกจุดที่น่าสนใจในตัวอย่างด้านบน: {{outlet}} ซึ่งระบุช่องในเทมเพลตที่สามารถแสดงเนื้อหาได้ เมื่อทำการซ้อนเส้นทาง เทมเพลตสำหรับเส้นทางทรัพยากรภายนอกจะถูกแสดงก่อน ตามด้วยเส้นทางภายใน ซึ่งแสดงเนื้อหาเทมเพลตใน {{outlet}} ที่กำหนดโดยเส้นทางภายนอก นี่คือสิ่งที่เกิดขึ้นที่นี่

ตามธรรมเนียมแล้ว เส้นทางทั้งหมดแสดงเนื้อหาลงในเทมเพลตที่มีชื่อเดียวกัน ด้านบน แอตทริบิวต์ data-template-name ของเทมเพลตด้านบนคือ artists ซึ่งหมายความว่าจะแสดงผลสำหรับเส้นทางภายนอก artists มันระบุช่องทางสำหรับเนื้อหาของแผงด้านขวาซึ่งเส้นทางภายใน, artists.index แสดงเนื้อหา:

 <script type="text/x-handlebars" data-template-name="artists/index"> <div class="list-group-item empty-list"> <div class="empty-message"> Select an artist. </div> </div> </script>

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

ที่เกี่ยวข้อง: 8 คำถามสัมภาษณ์ Ember.js ที่จำเป็น

สร้างศิลปิน

ส่วนที่ 1: การผูกข้อมูล

ต่อไป เราต้องการสร้างศิลปิน ไม่ใช่แค่ดูรายชื่อที่น่าเบื่อ

เมื่อฉันแสดงเทมเพลตของ artists ที่แสดงรายชื่อศิลปิน ฉันโกงเล็กน้อย ฉันตัดส่วนบนออกเพื่อเน้นสิ่งที่สำคัญ ตอนนี้ฉันจะเพิ่มกลับ:

 <script type="text/x-handlebars" data-template-name="artists"> <div class="col-md-4"> <div class="list-group"> <div class="list-group-item"> {{input type="text" class="new-artist" placeholder="New Artist" value=newName}} <button class="btn btn-primary btn-sm new-artist-button" {{action "createArtist"}} {{bind-attr disabled=disabled}}>Add</button> </div> < this is where the list of artists is rendered > ... </script>

เราใช้ Ember helper, input , พร้อมพิมพ์ข้อความเพื่อแสดงการป้อนข้อความอย่างง่าย ในนั้น เรา ผูก ค่าของการป้อนข้อความเข้ากับคุณสมบัติ newName ของคอนโทรลเลอร์ที่สำรองเทมเพลตนี้ ArtistsController ด้วยเหตุนี้ เมื่อคุณสมบัติของค่าของอินพุตเปลี่ยนแปลง (กล่าวอีกนัยหนึ่ง เมื่อผู้ใช้พิมพ์ข้อความลงไป) คุณสมบัติ newName บนคอนโทรลเลอร์จะซิงค์กัน

เรายังทำให้ทราบด้วยว่าการกระทำ createArtist ควรเริ่มทำงานเมื่อมีการคลิกปุ่ม สุดท้าย เราผูกคุณสมบัติที่ถูกปิดใช้งานของปุ่มเข้ากับคุณสมบัติที่ disabled ของคอนโทรลเลอร์ แล้วคอนโทรลเลอร์มีหน้าตาเป็นอย่างไร?

 App.ArtistsController = Ember.ArrayController.extend({ newName: '', disabled: function() { return Ember.isEmpty(this.get('newName')); }.property('newName') });

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

ถูกใช้ disabled โดยที่เมื่อไม่มีข้อความในกล่องอินพุต ข้อความนั้นจะกลับมา true ดังนั้นปุ่มจะถูกปิดการใช้งาน การเรียก .property ในตอนท้ายทำให้สิ่งนี้เป็น "คุณสมบัติที่คำนวณได้" ซึ่งเป็นอีกชิ้นที่น่ารับประทานของเค้ก Ember

คุณสมบัติที่คำนวณได้คือคุณสมบัติที่ขึ้นอยู่กับคุณสมบัติอื่น ๆ ซึ่งสามารถ "ปกติ" หรือคำนวณได้ Ember จะเก็บค่าของสิ่งเหล่านี้ไว้จนกว่าคุณสมบัติที่ขึ้นต่อกันตัวใดตัวหนึ่งจะเปลี่ยนไป จากนั้นจะคำนวณค่าของคุณสมบัติที่คำนวณใหม่และแคชอีกครั้ง

นี่คือการแสดงภาพของกระบวนการข้างต้น เพื่อสรุป: เมื่อผู้ใช้ป้อนชื่อศิลปิน คุณสมบัติ newName อัปเดต ตามด้วยคุณสมบัติที่ disabled และสุดท้าย ชื่อของศิลปินจะถูกเพิ่มลงในรายการ

ทางเบี่ยง: แหล่งความจริงเพียงแหล่งเดียว

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

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

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

หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับหัวข้อนี้ เราขอแนะนำให้คุณอ่านโพสต์บล็อกของ eviltrout สำหรับเวอร์ชันที่สั้นกว่าหรือคำถาม Quora นี้สำหรับการสนทนาที่ยาวขึ้นซึ่งนักพัฒนาหลักจากทั้งสองฝ่ายเข้ามามีส่วนร่วม

ส่วนที่ 2: ตัวจัดการการดำเนินการ

กลับไปดูว่าการกระทำ createArtist ถูกสร้างขึ้นหลังจากเริ่มทำงานอย่างไร (หลังจากกดปุ่ม):

 App.ArtistsRoute = Ember.Route.extend({ ... actions: { createArtist: function() { var name = this.get('controller').get('newName'); Ember.$.ajax('http://localhost:9393/artists', { type: 'POST', dataType: 'json', data: { name: name }, context: this, success: function(data) { var artist = App.Artist.createRecord(data); this.modelFor('artists').pushObject(artist); this.get('controller').set('newName', ''); this.transitionTo('artists.songs', artist); }, error: function() { alert('Failed to save artist'); } }); } } });

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

ไม่มีอะไรแฟนซีเกิดขึ้นที่นี่ หลังจากที่แบ็กเอนด์แจ้งให้เราทราบว่าการดำเนินการบันทึกเสร็จสมบูรณ์ เราทำสามสิ่งตามลำดับ:

  1. เพิ่มศิลปินใหม่ลงในโมเดลของเทมเพลต (ศิลปินทั้งหมด) เพื่อให้ได้รับการแสดงผลใหม่ และศิลปินใหม่จะปรากฏเป็นรายการสุดท้ายของรายการ
  2. ล้างช่องป้อนข้อมูลผ่านการผูก newName ซึ่งช่วยให้เราไม่ต้องจัดการ DOM โดยตรง
  3. การเปลี่ยนผ่านสู่เส้นทางใหม่ ( artists.songs ) ผ่านศิลปินที่สร้างขึ้นใหม่มาเป็นต้นแบบสำหรับเส้นทางนั้น transitionTo เป็นวิธีการย้ายระหว่างเส้นทางภายใน (ตัวช่วย link-to ยังดำเนินการผ่านการกระทำของผู้ใช้)

แสดงเพลงสำหรับศิลปิน

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

เส้นทางที่ใช้งานที่นี่คือ artists.songs ดังนั้นคอนโทรลเลอร์และเทมเพลตจะเป็น ArtistsSongsController และ artist artists/songs ตามลำดับ เราได้เห็นแล้วว่าเทมเพลตได้รับการเรนเดอร์ในร้านค้าที่จัดทำโดยเทมเพลตของ artists เพื่อให้เราสามารถมุ่งเน้นไปที่เทมเพลตที่อยู่ในมือ:

 <script type="text/x-handlebars" data-template-name="artists/songs"> (...) {{#each songs}} <div class="list-group-item"> {{title}} {{view App.StarRating maxRating=5}} </div> {{/each}} </script>

โปรดทราบว่าฉันได้ลอกโค้ดออกเพื่อสร้างเพลงใหม่ ซึ่งเหมือนกับโค้ดสำหรับสร้างศิลปินใหม่ทุกประการ

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

ชื่อจะแสดงโดยตรงในเทมเพลตและการให้คะแนนจะแสดงด้วยดาวผ่านมุมมอง StarRating มาดูกันว่าตอนนี้

วิดเจ็ตการให้คะแนนดาว

คะแนนของเพลงอยู่ระหว่าง 1 ถึง 5 และแสดงให้ผู้ใช้เห็นผ่านมุมมอง App.StarRating มุมมองสามารถเข้าถึงบริบท (ในกรณีนี้คือเพลง) และผู้ควบคุม ซึ่งหมายความว่าพวกเขาสามารถอ่านและแก้ไขคุณสมบัติของมันได้ สิ่งนี้ตรงกันข้ามกับส่วนประกอบอื่นๆ ของ Ember ซึ่งเป็นส่วนประกอบที่แยกออกมา การควบคุมที่นำกลับมาใช้ใหม่ได้ โดยเข้าถึงได้เฉพาะสิ่งที่ส่งผ่านเข้ามาเท่านั้น (เราสามารถใช้องค์ประกอบการให้คะแนนดาวในตัวอย่างนี้ได้เช่นกัน)

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

 App.StarRating = Ember.View.extend({ classNames: ['rating-panel'], templateName: 'star-rating', rating: Ember.computed.alias('context.rating'), fullStars: Ember.computed.alias('rating'), numStars: Ember.computed.alias('maxRating'), stars: function() { var ratings = []; var fullStars = this.starRange(1, this.get('fullStars'), 'full'); var emptyStars = this.starRange(this.get('fullStars') + 1, this.get('numStars'), 'empty'); Array.prototype.push.apply(ratings, fullStars); Array.prototype.push.apply(ratings, emptyStars); return ratings; }.property('fullStars', 'numStars'), starRange: function(start, end, type) { var starsData = []; for (i = start; i <= end; i++) { starsData.push({ rating: i, full: type === 'full' }); }; return starsData; }, (...) });

rating fullStars และ numStars เป็นคุณสมบัติที่คำนวณได้ ซึ่งเราเคยพูดถึงก่อนหน้านี้เกี่ยวกับคุณสมบัติที่ disabled ของ ArtistsController ด้านบน ฉันใช้มาโครคุณสมบัติการคำนวณที่เรียกว่า ประมาณโหลซึ่งถูกกำหนดไว้ใน Ember พวกเขาทำให้คุณสมบัติการคำนวณทั่วไปกระชับมากขึ้นและเกิดข้อผิดพลาดน้อยลง (ในการเขียน) ฉันตั้งค่า rating เป็นการให้คะแนนของบริบท (และด้วยเหตุนี้เพลง) ในขณะที่ฉันกำหนดทั้งคุณสมบัติ fullStars และ numStars เพื่อให้พวกเขาอ่านได้ดีขึ้นในบริบทของวิดเจ็ตการให้คะแนนดาว

วิธี stars เป็นแหล่งท่องเที่ยวหลัก ส่งคืนอาร์เรย์ข้อมูลสำหรับดาวที่แต่ละรายการมีคุณสมบัติ rating (ตั้งแต่ 1 ถึง 5) และแฟล็ก ( full ) เพื่อระบุว่าดาวเต็มหรือไม่ วิธีนี้ทำให้ดูข้อมูลเหล่านี้ในเทมเพลตได้ง่ายมาก:

 <script type="text/x-handlebars" data-template-name="star-rating"> {{#each view.stars}} <span {{bind-attr data-rating=rating}} {{bind-attr class=":star-rating :glyphicon full:glyphicon-star:glyphicon-star-empty"}} {{action "setRating" target=view}}> </span> {{/each}} </script>

ตัวอย่างนี้มีข้อสังเกตหลายประการ:

  1. อันดับแรก ผู้ช่วย each กำหนดว่าใช้คุณสมบัติการดู (ตรงข้ามกับคุณสมบัติบนตัวควบคุม) โดยนำหน้าชื่อคุณสมบัติด้วย view
  2. ประการที่สอง แอตทริบิวต์ class ของแท็ก span มีการกำหนดคลาสไดนามิกและสแตติกแบบผสม สิ่งใดก็ตามที่นำหน้าด้วย : จะกลายเป็นคลาสสแตติก ในขณะที่ full:glyphicon-star:glyphicon-star-empty สัญกรณ์จะเหมือนกับตัวดำเนินการ ternary ใน JavaScript: หากคุณสมบัติทั้งหมดเป็นจริง ควรกำหนดคลาสเฟิร์สคลาส ถ้าไม่ใช่ครั้งที่สอง
  3. สุดท้าย เมื่อคลิกแท็ก การดำเนินการ setRating ควรเริ่มทำงาน—แต่ Ember จะค้นหาในมุมมอง ไม่ใช่เส้นทางหรือตัวควบคุม เช่นเดียวกับในกรณีสร้างศิลปินใหม่

การดำเนินการจึงถูกกำหนดไว้ในมุมมอง:

 App.StarRating = Ember.View.extend({ (...) actions: { setRating: function() { var newRating = $(event.target).data('rating'); this.set('rating', newRating); } } });

เราได้รับ rating จากแอตทริบิวต์ข้อมูลการจัดประเภทที่เรากำหนดในเทมเพลต แล้วตั้งค่านั้นเป็น rating สำหรับเพลง โปรดทราบว่าการให้คะแนนใหม่จะไม่คงอยู่ในส่วนหลัง การใช้งานฟังก์ชันนี้ตามวิธีที่เราสร้างศิลปินไม่ใช่เรื่องยาก และเหลือไว้เป็นแบบฝึกหัดสำหรับผู้อ่านที่มีแรงบันดาลใจ

หมดทุกอย่างแล้ว

เราได้ลิ้มลองส่วนผสมหลายอย่างของเค้ก Ember ดังกล่าว:

  • เราได้เห็นแล้วว่าเส้นทางเป็นจุดสำคัญของแอปพลิเคชัน Ember อย่างไรและใช้เป็นพื้นฐานในการตั้งชื่ออย่างไร
  • เราได้เห็นแล้วว่าการเชื่อมโยงข้อมูลแบบสองทางและคุณสมบัติที่คำนวณได้ทำให้ข้อมูลแบบจำลองของเราเป็นแหล่งความจริงเพียงแหล่งเดียว และช่วยให้เราหลีกเลี่ยงการจัดการ DOM โดยตรงได้
  • และเราได้เห็นวิธีการเริ่มทำงานและจัดการกับการกระทำต่างๆ ในรูปแบบต่างๆ และสร้างมุมมองที่กำหนดเองเพื่อสร้างการควบคุมที่ ไม่ได้ เป็นส่วนหนึ่งของ HTML ของเรา

สวยใช่มั้ยล่ะ

อ่านเพิ่มเติม (และดู)

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

ฉันหวังว่าฉันจะกระตุ้นความอยากอาหารของคุณเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ Ember.js และคุณทำได้ดีกว่าแอปพลิเคชันตัวอย่างที่ฉันใช้ในโพสต์นี้ ในขณะที่คุณเรียนรู้เกี่ยวกับ Ember.js ต่อไป โปรดอ่านบทความของเราเกี่ยวกับ Ember Data เพื่อเรียนรู้วิธีใช้ไลบรารี่ ember-data ขอให้สนุกกับการสร้าง!

ที่เกี่ยวข้อง: Ember.js และ 8 ข้อผิดพลาดทั่วไปที่นักพัฒนาสร้างขึ้น