İlk Ember.js Uygulamanızı Oluşturma Kılavuzu

Yayınlanan: 2022-03-11

Modern web uygulamaları istemci tarafında giderek daha fazlasını yaptığı için (artık onlara "web siteleri" yerine "web uygulamaları" dediğimiz gerçeği oldukça açıklayıcıdır), istemci tarafı çerçevelere artan ilgi olmuştur. . Bu alanda çok sayıda oyuncu var ancak çok sayıda işlevselliğe ve çok sayıda hareketli parçaya sahip uygulamalar için özellikle ikisi öne çıkıyor: Angular.js ve Ember.js.

Kapsamlı bir [Angular.js öğreticisi][https://www.toptal.com/angular-js/a-step-by-step-guide-to-your-first-angularjs-app] zaten yayınladık, bu yüzden biz' Müzik koleksiyonunuzu kataloglamak için basit bir Ember uygulaması oluşturacağımız bu gönderide Ember.js'ye odaklanacağız. Çerçevenin ana yapı taşlarıyla tanışacak ve tasarım ilkelerine bir göz atacaksınız. Kaynak kodunu okurken görmek isterseniz, Github'da rock and roll olarak mevcut.

Ne inşa edeceğiz?

Rock & Roll uygulamamız son sürümünde şöyle görünecek:

ember.js ile uygulamanın son sürümü

Solda, sanatçıların bir listesinin ve sağda seçilen sanatçının şarkılarının bir listesinin olduğunu göreceksiniz (müzik zevkimin iyi olduğunu da görebilirsiniz, ama konuyu dalıyorum). Metin kutusuna yazıp yandaki düğmeye basarak yeni sanatçılar ve şarkılar eklenebilir. Her şarkının yanındaki yıldızlar, bir iTunes olarak, onu derecelendirmeye hizmet eder.

Uygulamanın temel işlevselliğini aşağıdaki adımlara ayırabiliriz:

  1. 'Ekle'yi tıklamak, listeye 'Yeni Sanatçı' alanında belirtilen bir adla yeni bir sanatçı ekler (aynısı belirli bir sanatçı için şarkılar için de geçerlidir).
  2. 'Yeni Sanatçı' alanını boşaltmak, 'Ekle' düğmesini devre dışı bırakır (aynısı belirli bir sanatçı için şarkılar için de geçerlidir).
  3. Bir sanatçının adına tıklamak, şarkılarını sağda listeler.
  4. Yıldızlara tıklamak belirli bir şarkıyı derecelendirir.

Bunun işe yaraması için uzun bir yolumuz var, o yüzden başlayalım.

Rotalar: Ember.js uygulamasının anahtarı

Ember'in ayırt edici özelliklerinden biri, URL'lere yaptığı yoğun vurgudur. Diğer birçok çerçevede, ayrı ekranlar için ayrı URL'lere sahip olmak ya eksiktir ya da sonradan düşünülmüştür. Ember'de, url'leri ve bunlar arasındaki geçişleri yöneten bileşen olan yönlendirici, yapı taşları arasındaki işi koordine eden merkezi parçadır. Sonuç olarak, aynı zamanda Ember uygulamalarının iç işleyişini anlamanın anahtarıdır.

İşte uygulamamız için yollar:

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

Bir kaynak rotası, artists ve bunun içinde yuvalanmış bir songs rotası tanımlarız. Bu tanım bize şu yolları verecektir:

Rotalar

Oluşturulan yolları size kolayca okunabilir bir şekilde göstermek için harika Ember Inspector eklentisini (hem Chrome hem de Firefox için var) kullandım. Yukarıdaki tablonun yardımıyla özel durumumuz için doğrulayabileceğiniz Ember rotaları için temel kurallar şunlardır:

  1. Örtük bir application yolu vardır.

    Bu, tüm istekler (geçişler) için etkinleştirilir.

  2. Örtük bir index yolu var.

    Bu, kullanıcı uygulamanın kök dizinine gittiğinde girilir.

  3. Her kaynak yolu, aynı ada sahip bir yol oluşturur ve bunun altında dolaylı olarak bir dizin yolu oluşturur.

    Bu dizin rotası, kullanıcı rotaya gittiğinde etkinleştirilir. Bizim durumumuzda, /artists artists.index gittiğinde tetiklenir.

  4. Basit (kaynak olmayan), iç içe geçmiş bir rota, öneki olarak üst rota adına sahip olacaktır.

    this.route('songs', ...) olarak tanımladığımız rotanın adı artists.songs olacaktır. Kullanıcı /artists/pearl-jam veya /artists/radiohead gittiğinde tetiklenir.

  5. Yol verilmezse, rota adına eşit olduğu varsayılır.

  6. Yol bir : içeriyorsa , dinamik bir segment olarak kabul edilir .

    Kendisine atanan ad (bizim durumumuzda, slug ) url'nin uygun bölümündeki değerle eşleşecektir. Yukarıdaki bilgi segmenti, radiohead pearl-jam , slug veya URL'den çıkarılan diğer herhangi bir değere sahip olacaktır.

Sanatçıların listesini göster

İlk adım olarak, sol tarafta sanatçıların listesini gösteren ekranı oluşturacağız. /artists/ dizinine gittiklerinde kullanıcılara bu ekran gösterilmelidir:

sanatçılar

Bu ekranın nasıl işlendiğini anlamak için, başka bir kapsayıcı Ember tasarım ilkesini tanıtmanın zamanı geldi: konfigürasyon üzerinde konvansiyon . Yukarıdaki bölümde /artists artists rotasını etkinleştirdiğini gördük. Kural olarak, bu rota nesnesinin adı ArtistsRoute . Uygulamanın oluşturacağı verileri getirmek bu rota nesnesinin sorumluluğundadır. Bu, rotanın model kancasında olur:

 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; } });

Bu snippet'te, veriler arka uçtan bir XHR çağrısı yoluyla alınır ve - bir model nesnesine dönüştürüldükten sonra - daha sonra görüntüleyebileceğimiz bir diziye itilir. Ancak, rotanın sorumlulukları, denetleyici tarafından işlenen görüntüleme mantığı sağlamaya kadar uzanmaz. Hadi bir bakalım.

Hmmm—aslında, bu noktada denetleyiciyi tanımlamamıza gerek yok! Ember, gerektiğinde denetleyiciyi oluşturacak ve denetleyicinin M.odel özniteliğini model kancasının kendisinin dönüş değerine, yani sanatçılar listesine ayarlayacak kadar akıllıdır. (Yine, bu 'konfigürasyon üzerindeki konvansiyon' paradigmasının bir sonucudur.) Bir katman aşağı inebilir ve listeyi görüntülemek için bir şablon oluşturabiliriz:

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

Bu size tanıdık geliyorsa, bunun nedeni Ember.js'nin çok basit bir sözdizimine ve yardımcılara sahip olan, ancak önemsiz olmayan mantığa (örneğin, bir koşulda ORing veya ANDing terimleri) izin vermeyen Gidon şablonları kullanmasıdır.

Yukarıdaki şablonda, modeli yineleriz (daha önce tüm sanatçıları içeren bir diziye giden yol tarafından ayarlanır) ve içindeki her öğe için, bizi o sanatçının artists.songs yoluna götüren bir bağlantı oluştururuz. Bağlantı, sanatçı adını içerir. #each helper, içindeki kapsamı geçerli öğeye değiştirir, bu nedenle {{name}} her zaman o anda yineleme altında olan sanatçının adına atıfta bulunur.

İç içe görünümler için iç içe rotalar

Yukarıdaki snippet'te başka bir ilgi çekici nokta: Şablonda içeriğin oluşturulabileceği alanları belirten {{outlet}} . Rotaları iç içe yerleştirirken, önce dış, kaynak rotası için şablon oluşturulur, ardından şablon içeriğini dış rota tarafından tanımlanan {{outlet}} içine işleyen iç rota gelir. Burada tam olarak bu oluyor.

Kural olarak, tüm yollar içeriklerini aynı adlı şablona dönüştürür. Yukarıda, yukarıdaki şablonun data-template-name özniteliği artists , yani dış rota, artists için oluşturulacaktır. Sağ panelin içeriği için, iç rotanın, artists.index içeriğini oluşturduğu bir çıkış belirtir:

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

Özetle, bir rota ( artists ) içeriğini sol kenar çubuğunda işler, modeli sanatçıların listesidir. Başka bir yol olan artists.index , kendi içeriğini artists şablonu tarafından sağlanan yuvaya işler. Modeli olarak hizmet etmesi için bazı veriler getirebilir, ancak bu durumda görüntülemek istediğimiz tek şey statik metindir, dolayısıyla buna ihtiyacımız yok.

İlgili: 8 Temel Ember.js Mülakat Sorusu

Sanatçı oluştur

Bölüm 1: Veri bağlama

Ardından, sadece sıkıcı bir listeye bakmakla kalmayıp sanatçılar yaratabilmek istiyoruz.

Sanatçı listesini oluşturan artists şablonunu gösterdiğimde biraz hile yaptım. Önemli olana odaklanmak için üst kısmı kestim. Şimdi bunu tekrar ekleyeyim:

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

Basit bir metin input oluşturmak için text türünde bir Ember yardımcısı kullanıyoruz. İçinde, bu şablonu, ArtistsController yedekleyen denetleyicinin newName özelliğine metin girişinin değerini bağlarız . Sonuç olarak, girişin value özelliği değiştiğinde (başka bir deyişle, kullanıcı içine metin girdiğinde), denetleyicideki newName özelliği senkronize tutulacaktır.

Ayrıca, butona tıklandığında createArtist eyleminin başlatılması gerektiğini bildiririz. Son olarak, butonun disabled özelliğini controller’ın disable özelliğine bağlıyoruz. Peki kontrolör neye benziyor?

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

newName başlangıçta boş olarak ayarlanmıştır, bu da metin girişinin boş olacağı anlamına gelir. (Bağlamalar hakkında ne söylediğimi hatırlıyor musunuz? newName değiştirmeyi deneyin ve giriş alanındaki metin olarak yansıtıldığını görün.)

disabled , giriş kutusunda metin olmadığında true dönecek ve böylece düğme devre dışı bırakılacak şekilde uygulanır. .property çağrısı, bunu Ember pastasının bir başka şahane dilimi olan "hesaplanmış bir özellik" yapar.

Hesaplanan özellikler, kendileri "normal" veya hesaplanmış olabilen diğer özelliklere bağlı olan özelliklerdir. Ember, bağımlı özelliklerden biri değişene kadar bunların değerini önbelleğe alır. Daha sonra hesaplanan özelliğin değerini yeniden hesaplar ve yeniden önbelleğe alır.

İşte yukarıdaki sürecin görsel bir temsili. Özetlemek gerekirse: kullanıcı bir sanatçının adını girdiğinde, newName özelliği güncellenir, ardından disabled özellik ve son olarak sanatçının adı listeye eklenir.

Sapma: Tek bir hakikat kaynağı

Bir an için bunu düşünün. Bağlamaların ve hesaplanan özelliklerin yardımıyla, verileri tek bir doğruluk kaynağı olarak kurabiliriz (model) . Yukarıda, yeni sanatçının adındaki bir değişiklik, controller özelliğinde bir değişikliği tetikler ve bu da, devre dışı bırakılmış özellikte bir değişikliği tetikler. Kullanıcı yeni sanatçının adını yazmaya başladığında, düğme sanki sihirli bir şekilde etkinleştirilir.

Sistem ne kadar büyük olursa, 'tek gerçek kaynağı' ilkesinden o kadar fazla kaldıraç elde ederiz. Kodumuzu temiz ve sağlam tutar ve özellik tanımlarımızı daha bildirimsel tutar.

Diğer bazı çerçeveler de model verilerinin tek gerçeğin kaynağı olmasına vurgu yapar, ancak ya Ember kadar ileri gitmez ya da böyle kapsamlı bir iş yapmaz. Örneğin Angular, iki yönlü bağlamalara sahiptir, ancak hesaplanmış özelliklere sahip değildir. Basit işlevler aracılığıyla hesaplanan özellikleri "taklit edebilir"; Buradaki sorun, "bilgisayarlı bir özelliğin" ne zaman yenileneceğini bilmenin bir yolunun olmaması ve dolayısıyla kirli kontrole başvurması ve bunun sonucunda özellikle daha büyük uygulamalarda dikkate değer bir performans kaybına yol açmasıdır.

Konu hakkında daha fazla bilgi edinmek isterseniz, daha kısa bir sürüm için kötüböcek'in blog gönderisini veya her iki taraftan çekirdek geliştiricilerin ağırlık verdiği daha uzun bir tartışma için bu Quora sorusunu okumanızı tavsiye ederim.

Bölüm 2: Eylem işleyicileri

Şimdi createArtist eyleminin tetiklendikten sonra nasıl oluşturulduğunu görmek için geri dönelim (düğmeye basıldıktan sonra):

 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'); } }); } } });

Eylem işleyicilerin bir actions nesnesine sarılması gerekir ve rota, denetleyici veya görünüm üzerinde tanımlanabilir. Eylemin sonucu controller ile sınırlı olmadığı için “global” olduğu için burada rota üzerinde tanımlamayı seçtim.

Burada süslü bir şey yok. Arka uç, kaydetme işleminin başarıyla tamamlandığını bize bildirdikten sonra, sırayla üç şey yaparız:

  1. Yeni sanatçıyı şablonun modeline ekleyin (tüm sanatçılar), böylece yeniden işlenir ve yeni sanatçı listenin son öğesi olarak görünür.
  2. Giriş alanını newName bağlaması aracılığıyla temizleyin, bu da bizi DOM'yi doğrudan değiştirmek zorunda kalmaktan kurtarır.
  3. Yeni bir rotaya ( artists.songs ) geçiş, bu rota için model olarak yeni yaratılmış sanatçıdan geçmek. transitionTo , rotalar arasında dahili olarak hareket etmenin yoludur. (Yardımcıya link-to , bunu kullanıcı eylemi aracılığıyla yapmaya yarar.)

Bir sanatçının şarkılarını göster

Bir sanatçının şarkılarını sanatçının adına tıklayarak da görüntüleyebiliriz. Yeni rotanın modeli olacak sanatçıyı da aktarıyoruz. Model nesnesi bu şekilde iletilirse, modeli çözmeye gerek olmadığından rotanın model kancası çağrılmayacaktır.

Buradaki aktif rota ArtistsSongsController artists.songs artists/songs olacak. artists şablonu tarafından sağlanan çıkışta şablonun nasıl işlendiğini zaten gördük, böylece yalnızca elimizdeki şablona odaklanabiliriz:

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

Yeni bir sanatçı yaratmakla tamamen aynı olacağından, yeni bir şarkı oluşturmak için kodu çıkardığımı unutmayın.

songs özelliği, sunucu tarafından döndürülen verilerden tüm sanatçı nesnelerinde kurulur. Bunun yapıldığı kesin mekanizma, mevcut tartışma için çok az ilgi çekicidir. Şimdilik her şarkının bir başlığı ve bir puanı olduğunu bilmemiz yeterli.

Başlık, doğrudan şablonda görüntülenir ve derecelendirme, StarRating görünümü aracılığıyla yıldızlarla temsil edilir. Şimdi bunu görelim.

Yıldız derecelendirme widget'ı

Bir şarkının derecelendirmesi 1 ile 5 arasında düşer ve kullanıcıya App.StarRating görünümüyle gösterilir. Görünümlerin bağlamlarına (bu durumda şarkı) ve denetleyicilerine erişimi vardır. Bu, özelliklerini okuyabilecekleri ve değiştirebilecekleri anlamına gelir. Bu, başka bir Ember yapı taşının, izole edilmiş bileşenlerin, yalnızca kendilerine aktarılanlara erişimi olan yeniden kullanılabilir kontrollerin aksine. (Bu örnekte de bir yıldız derecelendirme bileşeni kullanabiliriz.)

Kullanıcı yıldızlardan birine tıkladığında görünümün yıldız sayısını nasıl gösterdiğini ve şarkının derecelendirmesini nasıl belirlediğini görelim:

 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 ve numStars , ArtistsController disabled özelliği ile daha önce tartıştığımız hesaplanmış özelliklerdir . Yukarıda, yaklaşık bir düzinesi Ember'de tanımlanmış olan, hesaplanmış bir özellik makrosu kullandım. Tipik hesaplanmış özellikleri daha kısa ve daha az hataya açık hale getirirler (yazmak için). Derecelendirmeyi bağlamın (ve dolayısıyla şarkının) rating olacak şekilde ayarlarken, hem fullStars hem de numStars özelliklerini yıldız derecelendirme widget'ı bağlamında daha iyi okumaları için tanımladım.

stars yöntemi ana cazibe merkezidir. Her öğenin bir rating özelliği (1'den 5'e kadar) ve yıldızın dolu olup olmadığını belirtmek için bir bayrak ( full ) içerdiği yıldızlar için bir veri dizisi döndürür. Bu, şablonda bunları gözden geçirmeyi son derece basit hale getirir:

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

Bu pasaj birkaç not içerir:

  1. İlk olarak, each yardımcı, özellik adının view ekleyerek (denetleyicideki bir özelliğin aksine) bir görünüm özelliği kullandığını belirtir.
  2. İkincisi, span etiketinin class özniteliği, atanmış karışık dinamik ve statik sınıflara sahiptir. : ile ön eklenmiş herhangi bir şey statik bir sınıf haline gelirken full : full:glyphicon-star:glyphicon-star-empty gösterimi JavaScript'teki üçlü bir operatör gibidir: eğer tam özellik truey ise, birinci sınıf atanmalıdır; değilse, ikincisi.
  3. Son olarak, etiket tıklandığında, setRating eylemi tetiklenmelidir - ancak Ember, yeni bir sanatçı oluşturma durumunda olduğu gibi rotada veya denetleyicide değil görünümde arar.

Eylem böylece görünümde tanımlanır:

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

Derecelendirmeyi, şablonda atadığımız rating verisi özelliğinden alıyoruz ve ardından bunu şarkının rating olarak ayarlıyoruz. Yeni derecelendirmenin arka uçta kalıcı olmadığını unutmayın. Bir sanatçıyı nasıl yarattığımıza bağlı olarak bu işlevi uygulamak zor olmaz ve motive okuyucu için bir alıştırma olarak bırakılır.

Her şeyi sarmak

Yukarıda bahsedilen Ember pastasının birkaç malzemesini tattık:

  • Rotaların Ember uygulamalarının en önemli noktası olduğunu ve adlandırma kurallarının temelini nasıl oluşturduklarını gördük.
  • İki yönlü veri bağlamalarının ve hesaplanan özelliklerin, model verilerimizi nasıl tek gerçek kaynak haline getirdiğini ve doğrudan DOM manipülasyonundan kaçınmamıza izin verdiğini gördük.
  • Eylemlerin nasıl tetikleneceğini ve çeşitli şekillerde ele alınacağını ve HTML'mizin parçası olmayan bir kontrol oluşturmak için özel bir görünüm oluşturmayı gördük.

Güzel, değil mi?

Daha fazla okuma (ve izleme)

Ember'da bu yazıya tek başıma sığdırabileceğimden çok daha fazlası var. Yukarıdaki uygulamanın biraz daha gelişmiş bir versiyonunu nasıl oluşturduğuma dair bir dizi ekran görüntüsü görmek ve/veya Ember hakkında daha fazla bilgi edinmek isterseniz, haftalık olarak makaleler veya ipuçları almak için posta listeme kaydolabilirsiniz.

Umarım Ember.js hakkında daha fazla bilgi edinme isteğinizi uyandırmışımdır ve bu yazıda kullandığım örnek uygulamanın ötesine geçmişsinizdir. Ember.js hakkında bilgi edinmeye devam ederken, ember-data kitaplığının nasıl kullanılacağını öğrenmek için Ember Data hakkındaki makalemize göz atmayı unutmayın. İyi eğlenceler inşa edin!

İlgili: Ember.js ve Geliştiricilerin Yaptığı En Yaygın 8 Hata