Ember Komponenter En Deep Dive

Ember.js er et JavaScript MVC rammeverk som lar utviklere lage ambisiøse webapplikasjoner. Selv om ren MVC tillater en utvikler å skille bekymringer, gir den deg ikke alle verktøyene, og din søknad vil trenge andre konstruksjoner. I dag skal jeg snakke om en av disse konstruksjonene. Ember komponenter er i hovedsak sandboxed gjenbrukbare biter av brukergrensesnitt. Hvis du ikke er kjent med Ember, vennligst sjekk ut Komme i gang med Ember.js eller Let's Learn Ember Course. I denne opplæringen vil vi dekke spesifikasjonen for Webkomponenter, lære å skrive en komponent i Ember, snakk om sammensetning, forklar forskjellen mellom en Ember-visning og en Ember-komponent, og øve integrering av plugins med Ember-komponenter.


Et ord om webkomponenter

Ember-komponentene er basert på W3C Web Components-spesifikasjonen. Spesifikasjonen består av fire mindre spesifikasjoner; maler, dekoratører, skygge DOM og egendefinerte elementer. Av disse fire konseptene har bare tre av dem herdingspesifikasjoner, dekoratører er unntaket. Ved å ha spesifikasjonene på plass, har rammeutviklere kunnet polyfillere disse nye APIene før de blir implementert av nettleservirksomhetene.

Det er flere viktige konsepter å forstå når du snakker om komponenter:

  • Komponenter kjenner ingenting om omverdenen med mindre det er eksplisitt sendt inn
  • Komponenter bør ha et godt definert grensesnitt til omverdenen
  • Komponenter kan ikke manipulere noe JavaScript utenfor komponenten
  • Komponenter kan kringkaste hendelser
  • Egendefinerte elementer må være namespaced med en bindestrek
  • Utenfor JavaScript kan ikke manipulere komponenter

Webkomponenter gir sann innkapsling for UI-widgets. Nedenfor er et diagram over hvordan en komponent fungerer på det mest grunnleggende nivå.

Mens Ember har lykkes polyfilled mye av en spesifikasjon, har rammer som AngularJS, Dart, Polymer og Xtags lignende løsninger. Den eneste advarsel her er at Ember og Angular for øyeblikket ikke bruker rekkevidder til komponenten. Over tid vil disse polyfill løsningene falme bort, og rammer vil vedta nettleserens implementering. Dette er en fundamentalt forskjellig tilnærming til utvikling, da vi kan dra nytte av fremtidige spesifikasjoner uten å knytte oss til eksperimentelle funksjoner i nettlesere.


Den mest grunnleggende Ember-komponenten

Med vår kunnskap om webkomponenter, la oss implementere den svært grunnleggende my-name-komponenten ovenfra, men i Ember. La oss begynne med å laste ned Ember Starter Kit fra Ember nettsiden. På tidspunktet for denne opplæringen er versjonen av Ember 1.3.0. Når du har lastet ned, åpner du filene i favorittredigereren din, slett alle maler i index.html (betegnet med data-mal-navn) og alt i app.js.

Det første vi skal ønske å gjøre er å lage vår komponentsjema. For denne tutorials skyld skal vi bruke inline-maler. Du gjør dette ved å skrive følgende i din index.html fil. Vi må også opprette en ny Ember-applikasjon i JavaScript.

   var App = Ember.Application.create ();

Du vil legge merke til at data-mal-navnet har et stinavn i stedet for bare en vanlig streng. Grunnen til at vi prefikser vårt komponentnavn med "Komponenter /" er å fortelle Ember vi har å gjøre med en komponentsjema og ikke en vanlig applikasjonsmal. Du vil også legge merke til at komponentnavnet har bindestrek i det. Dette er navnetpacingen som jeg hadde nevnt i Web Components-spesifikasjonen. Namespacing er gjort slik at vi ikke har navnekollisjoner med eksisterende tagger.

Hvis vi åpner nettleseren, bør vi ikke se noe annet. Årsaken til dette at vi ennå ikke har plass til noe i vår my-name-mal. La oss ta vare på det.

... 

Nå i nettleseren bør du se noe som bildet over. Vi er fortsatt ikke ferdige som du kan se vi faktisk ikke skriver ut et navn. Som jeg nevnte i første del, bør komponenter vise et godt definert grensesnitt til omverdenen. I dette tilfellet er vi opptatt av navnet. Så la oss passere i navnet ved å plassere et navnattributt på komponenten my-name.

... 

Når du oppdaterer siden du bør se "Hei, jeg heter Chad". Alt dette med å skrive en linje med JavaScript. Nå som vi har en følelse av å skrive en grunnleggende komponent, la oss snakke om forskjellen mellom Ember-komponenter og Ember-visninger.


Ember Components vs Ember Views

Ember er en MVC, så noen kan tenke, "Hvorfor ikke bare bruke en visning for dette?" Dette er et legitimt spørsmål. Komponenter er faktisk en underklasse av Ember. Vis, den største forskjellen er at visningene vanligvis finnes i sammenheng med en kontroller. Ta eksemplet nedenfor.

 App.IndexController = Ember.Controller.extend (myState: 'on'); App.IndexView = Ember.View.extend (klikk: funksjon () var controller = this.get ('controller'), myState = controller.get ('myState'); console.log (controller) // Kontrolleren instans console.log (myState) // Strengen "på");
 

Visninger sitter vanligvis bak en mal og snu råinngang (klikk, mouseEnter, mouseMove, etc.) i en semantisk handling (openMenu, editName, hideModal, etc.) i en kontroller eller rute. En annen ting å påpeke er at maler trenger en kontekst også. Så hva som skjer, er at Ember gir inn konteksten gjennom navnekonvensjoner og nettadressen. Se diagrammet under.

Som du kan se er det et nivå av hierarki basert på nettadressen, og hvert nivå av det hierarkiet har sin egen kontekst som er avledet gjennom navngivningskonvensjoner.

Ember-komponenter har ikke en kontekst, de vet bare om grensesnittet de definerer. Dette gjør det mulig for en komponent å bli gjengitt i en hvilken som helst sammenheng, slik at den løsnes og gjenbrukes. Hvis komponenten utgjør et grensesnitt, er det jobben i konteksten for å oppfylle dette grensesnittet. Med andre ord, hvis du vil at komponenten skal gjengis riktig, må du forsyne den med data som den forventer. Det er viktig å merke seg at disse passerte verdiene kan være både strenger eller bundet egenskaper.

Når bundet egenskaper manipuleres inne i en komponent, endres disse endringene fortsatt hvor de er referert i søknaden din. Dette gjør komponentene ekstremt kraftige. Nå som vi har en god forståelse for hvordan komponenter er forskjellige fra visninger, la oss se på et mer komplisert eksempel som illustrerer hvordan en utvikler kan komponere flere komponenter.


Sammensetning av komponenter

En veldig fin ting om Ember er at den er bygget på konsepter av UI-hierarki, og dette er meget tydelig med sammensetning av komponenter. Nedenfor er et eksempel på hva vi skal gjøre. Det er en enkel gruppe chat-brukergrensesnitt. Åpenbart vil jeg ikke skrive en hel chat-tjeneste for å drive brukergrensesnittet, men vi kan se hvordan vi kan bryte brukergrensesnittet ned til gjenbruk og komponere mulige komponenter.

La oss først se hvordan vi skal bryte opp brukergrensesnittet i mindre og mer fordøyelige deler. Alt vi kan tegne en boks rundt, er en komponent, med unntak av tekst- og knappinngangene nederst på brukergrensesnittet. Målet vårt er å kunne kun konfigurere komponenten på ytre lag, alt annet skal bare fungere.

La oss starte med å opprette en ny html-fil kalt chat.html og sette opp alle avhengighetene til Ember. Deretter opprett alle maler.

       

Du vil se at komponenter kan nestes inne i andre komponenter. Dette gjør komponenter som legos som vi kan sette sammen som vi vil. Vi trenger bare å skrive til komponentens grensesnitt.

Hvis vi nå ser i nettleseren, bør vi ikke se mye fordi vi ikke har noen data som strømmer inn i komponenten. Du vil også legge merke til at selv om det ikke finnes data, kaster komponentene ikke en feil. Det eneste som faktisk blir gjengitt her er inntastingsområdet og send-knappen. Dette skyldes at de ikke er avhengige av hva som er sendt inn.

Ta litt nærmere på malene du vil legge merke til at vi tildelte et par ting på gruppe-chat-komponenten.

 

I dette tilfellet passerer vi modellen fra konteksten til IndexRoute som "meldinger" og vi har satt streng av sende melding som handlingen på komponenten. Handlingen vil bli brukt til å kringkaste når brukeren ønsker å sende en ny melding. Vi vil dekke dette senere i opplæringen. Den andre tingen du vil legge merke til er at vi setter opp strenge grensesnitt til de nestede komponentene som alle bruker dataene som sendes inn fra gruppe-chat-grensesnittet.

... 
    #each melding i meldinger
  • chat-message brukernavn = message.twitterUserName message = message.text time = message.timeStamp
  • /Hver
...

Som nevnt før kan du overføre strenger eller bundet egenskaper til komponenter. Tommelfingerregel er, bruk anførselstegn når du sender en streng, ikke bruk anførselstegn når du overfører en fast eiendom. Nå som vi har våre maler på plass, la oss kaste noen mock data på den.

 App = Ember.Application.create (); App.IndexRoute = Ember.Route.extend (model: function () return [id: 1, firstName: 'Tom', lastName: 'Dale', twitterUserName: 'tomdale', tekst: 'Jeg tror vi burde tilbake gammel, Tomster. Han var fantastisk. ', timeStamp: Date.now () - 400000,, id: 2, firstName:' Yehuda ', etternavn:' Katz ', twitterUserName:' wycats ' en god ide. ', timeStamp: Date.now () - 300000,];);

Hvis vi går ser dette i nettleseren nå, bør vi se litt fremgang. Men det er fortsatt noe arbeid å gjøre, hovedsakelig å få bildene til å dukke opp, formatere datoen og å kunne sende en ny melding. La oss ta vare på det.

Med vår bruker-avatar komponent, vil vi bruke en tjeneste som heter Avatars.io for å hente en bruker Twitter avatar basert på deres Twitter brukernavn. La oss se på hvordan brukerbildekomponenten brukes i malen.

  

Det er en ganske enkel komponent, men du vil merke at vi har en bundet eiendom som heter avatarUrl. Vi kommer til å trenge å opprette denne eiendommen innenfor JavaScript for denne komponenten. En annen ting du vil merke er at vi spesifiserer tjenesten vi ønsker å hente avataren fra. Avatars.io lar deg hente sosiale avatarer fra Twitter, Facebook og Instagram. Vi kan gjøre denne komponenten ekstremt fleksibel. La oss skrive komponenten.

 App.UserAvatarComponent = Ember.Component.extend (avatarUrl: function () var brukernavn = this.get ('brukernavn'), service = this.get ('tjeneste'), tilgjengeligTjenester = ['twitter', 'facebook' , 'instagram']; hvis (availableServices.indexOf (service)> -1) return 'http://avatars.io/' + service + '/' + brukernavn; returnere 'images / cat.png'; .property ('brukernavn', 'tjeneste'));

Som du kan se, for å lage en ny komponent følger vi bare navngivningskonvensjonen for NAMEOFCOMPONENTComponent og utvide Ember.Component. Nå, hvis vi går tilbake til nettleseren, bør vi nå se våre avatarer.

For å ta vare på datoformatering la oss bruke moment.js og skrive en håndteringshjelp for å formatere datoen for oss.

 Ember.Handlebars.helper ('format-date', funksjon (dato) retur øyeblikk (dato) .fraNow (););

Nå er alt vi trenger å gjøre, å søke hjelperen til tidsstempelkomponenten.

 

Vi bør nå ha en komponent som formaterer datoer i stedet for Unix epok tidsstempler.

Vi kan gjøre en bedre skjønt. Disse tidsstemplene skal automatisk oppdateres i grov tid, så la oss få vår tidsstempelkomponent å gjøre nettopp det.

 App.TimeStampComponent = Ember.Component.extend (startTimer: funksjon () var currentTime = this.get ('time'); this.set ('time', currentTime - 6000); this.scheduleStartTimer (); scheduleStartTimer: function () this._timer = Ember.run.later (dette, startTimer, 6000); .on ('didInsertElement'), killTimer: function () Ember.run.cancel (this.timer) ; .on ('willDestroyElement'));

Et par poeng å merke seg her. En er den på() declarative event handler syntaks. Dette ble introdusert i Ember før 1,0 utgivelsen. Det gjør akkurat hva du synes det gjør, når tidsstempelkomponenten er satt inn i DOM, scheduleStartTime er kalt. Når elementet er i ferd med å bli ødelagt og ryddet opp killTimer Metoden vil bli kalt. Resten av komponenten forteller bare tiden for å oppdatere hvert minutt.

Det andre du vil legge merke til er at det er flere samtaler til Ember.run. I Ember er det et køsystem, vanligvis referert til som løpsløyfen, som blir skyllet når data endres. Dette er gjort for å i utgangspunktet forveksle endringer og gjøre endringen en gang. I vårt eksempel skal vi bruke Ember.run.later å kjøre startTimer metode hvert minutt. Vi vil også bruke Ember.run.cancel å dequeue timeren. Dette er i hovedsak Embers egne start- og stoppintervallmetoder. De trengs for å holde køesystemet synkronisert. For mer på løpesløyfen foreslår jeg å lese Alex Matchneers artikkel "Alt du aldri ønsket å vite om Ember Run Loop".

Den neste tingen vi trenger å gjøre er å sette opp handlingen, slik at når brukeren treffer, sendes en ny melding. Vår komponent bør ikke bryr seg om hvordan dataene opprettes, det skal bare kringkaste at brukeren har forsøkt å sende en melding. Våre IndexRoute vil være ansvarlig for å ta denne handlingen og bli til noe meningsfylt.

 App.GroupChatComponent = Ember.Component.extend (message: ", actions: submit: function () var melding = this.get ('message') .trim (), samtale = dette. $ ('Ul') [0]; // Fetches verdien av 'action' // og sender handlingen med meldingen this.sendAction ('action', melding); // Når Ember Run loop er ferdig // bla til nederst Ember. run.schedule ('afterRender', funksjon () conversation.scrollTop = conversation.scrollHeight;; // Tilbakestill tekstmeldingsfeltet this.set ('message', '););
 
input type = "text" placeholder = "Send ny melding" value = message input type = "submit" value = "Send"

Siden gruppeklattkomponenten eier inntastings- og sendeknappen, må vi reagere på at brukeren klikker send på dette abstraksjonsnivået. Når brukeren klikker på innleveringsknappen, skal den gjennomføre innsendingshandlingen i komponentimplementasjonen. Innenfor handlingshandleren skal vi få verdien av meldingen, som angis av tekstinngangen. Vi sender da handlingen sammen med meldingen. Endelig vil vi tilbakestille meldingen til en tom streng.

Den andre merkelige tingen du ser her er Ember.run.schedule metode blir kalt. Igjen er dette Embers løpebro i handling. Du vil legge merke til at tidsplanen tar en streng som det første argumentet, i dette tilfellet "afterRender". Ember har faktisk flere forskjellige køer som den forvalter, gjør at den er en av dem. Så i vårt tilfelle sier vi når sendingen av meldingen er ferdig med å gjøre noen manipulasjoner, og etter at gjengningskøen er spylt, ring vår tilbakeringing. Dette vil rulle vår ul nederst, slik at brukeren kan se den nye meldingen etter noen manipulasjoner. For mer på løpesløyfen foreslår jeg å lese Alex Matchneers artikkel "Alt du aldri ønsket å vite om Ember Run Loop".

Hvis vi går over til nettleseren, og vi klikker på send-knappen, får vi en veldig fin feil fra Ember som sier "Uncaught Error: ingenting håndtert hendelsen" sendMessage ". Dette er hva vi forventer fordi vi ikke har fortalt vår søknad om hvordan til reaksjon på disse typer hendelser. La oss fikse det.

 App.IndexRoute = Ember.Route.extend (/ * ... * / handlinger: sendMessage: funksjon (melding) if (message! == ") console.log (melding);));

Nå, hvis vi går tilbake til nettleseren, skriver du noe i meldingsinngangen og klikker send, bør vi se meldingen i konsollen. Så på dette punktet er komponenten løst koblet og snakker til resten vår søknad. La oss gjøre noe mer interessant med dette. Først la oss lage en ny Ember.Object å jobbe som modell for en ny melding.

 App.Message = Ember.Object.extend (id: 3, firstName: 'Chad', LastName: 'Hietala', twitterUserName: 'chadhietala', tekst: null, timeStamp: null);

Så da sende melding handling skjer, vi skal ønske å fylle ut teksten og Tidsstempel feltet i vår Meldingsmodell, opprett en ny forekomst av den, og trykk deretter den forekomsten i den eksisterende samlingen av meldinger.

 App.IndexRoute = Ember.Route.extend (/ * ... * / handlinger: sendMessage: funksjon (melding) var bruker, meldinger, newMessage; if (message! == ") messages = this.modelFor '), newMessage = App.Message.create (tekst: melding, timeStamp: Date.now ()) messages.pushObject (newMessage);));

Når vi går tilbake til nettleseren, bør vi nå kunne opprette nye meldinger.

Vi har nå flere forskjellige gjenbrukbare biter av brukergrensesnitt som vi kan plassere hvor som helst. For eksempel, hvis du trenger å bruke en avatar et annet sted i ditt Ember-program, kan vi gjenbruke bruker-avatar komponenten.

 

Innpakning jQuery Plugins

På dette tidspunktet lurer du sikkert på "Hva om jeg vil bruke noe jQuery-plugin i komponenten min?" Ikke noe problem. For korthet, la oss endre vår bruker-avatar komponent for å vise et verktøy tips når vi svinger over avataren. Jeg har valgt å bruke jQuery plugin tooltipster til å håndtere verktøytipset. La oss endre den eksisterende koden for å utnytte verktøytipster.

Først la vi legge til riktige filer til vår chat.html og endre den eksisterende brukerens avatar-komponent.

... ...  ... 

Og så vår JavaScript:

 App.UserAvatarComponent = Ember.Component.extend (/ * ... * / setupTooltip: funksjon () this. $ ('.Avatar') .tooltipster (animasjon: 'fade';; .on ('didInsertElement' ), destroyTooltip: function () this. $ ('.avatar') .tooltipster ('destroy'); .on ('willDestroyElement'));

Igjen ser vi deklarative hendelseslytterens syntaks, men for første gang ser vi dette. $. Hvis du er kjent med jQuery, ville du forvente at vi ville spørre alle elementene med klassen "avatar". Dette er ikke tilfellet i Ember fordi kontekst er brukt. I vårt tilfelle søker vi bare etter elementer med klassen "avatar" i bruker-avatar komponenten. Det er sammenlignbart med jQuery's søkemetode. Ved ødeleggelse av elementet, bør vi knytte sveverhendelsen på avataren og rydde opp noen funksjonalitet, dette gjøres ved å sende "ødelegge" til verktøyet tipster. Hvis vi går til nettleseren, oppdaterer og svever et bilde, bør vi se brukerens brukernavn.


Konklusjon

I denne opplæringen tok vi et dypt dykk inn i Ember-komponenter og viste hvordan du kan ta gjenbrukbare biter av brukergrensesnitt for å generere større kompositter og integrere jQuery-plugins. Vi så på hvordan komponenter er forskjellige fra visninger i Ember. Vi dekket også ideen om grensesnittbasert programmering når det gjelder komponenter. Forhåpentligvis var jeg i stand til å skinne litt lys på ikke bare Ember Components, men Web Components og hvor nettet er ledet.