Un tutorial pas cu pas pentru prima ta aplicație AngularJS

Publicat: 2022-03-11
AngularJS a evoluat și a devenit și mai bun. Acum numit Angular, a fost rescris în întregime către un nou flux de lucru de dezvoltare. Consultați noul nostru tutorial Angular 5 și chiar mai nou tutorial Angular 6 full-stack, cu Material și Firebase.

Ce este AngularJS?

AngularJS este un cadru JavaScript MVC dezvoltat de Google care vă permite să construiți aplicații front-end bine structurate, ușor de testat și care pot fi întreținute.

Și de ce ar trebui să-l folosesc?

Dacă nu ați încercat încă AngularJS, ați ratat. Cadrul constă dintr-un set de instrumente strâns integrat care vă va ajuta să construiți aplicații bine structurate și bogate la nivelul clientului într-un mod modular - cu mai puțin cod și mai multă flexibilitate.

AngularJS extinde HTML prin furnizarea de directive care adaugă funcționalitate markupului și vă permit să creați șabloane dinamice puternice. De asemenea, vă puteți crea propriile directive, creând componente reutilizabile care vă satisfac nevoile și abstragând toată logica de manipulare a DOM.

De asemenea, implementează legarea bidirecțională a datelor, conectând HTML (viziunile) cu obiectele (modele) JavaScript. În termeni simpli, aceasta înseamnă că orice actualizare a modelului dvs. va fi reflectată imediat în vizualizarea dvs. fără a fi nevoie de vreo manipulare DOM sau de gestionare a evenimentelor (de exemplu, cu jQuery).

Angular oferă servicii pe lângă XHR care vă simplifică dramatic codul și vă permit să abstractizați apelurile API în servicii reutilizabile. Cu aceasta, vă puteți muta modelul și logica de afaceri în front-end și puteți construi aplicații web agnostice pentru back-end.

În cele din urmă, îmi place Angular datorită flexibilității sale în ceea ce privește comunicarea cu serverul. La fel ca majoritatea framework-urilor JavaScript MVC, vă permite să lucrați cu orice tehnologie de pe partea serverului, atâta timp cât vă poate servi aplicația printr-un API web RESTful. Dar Angular oferă și servicii pe lângă XHR, care vă simplifică dramatic codul și vă permit să abstrageți apelurile API în servicii reutilizabile. Ca rezultat, vă puteți muta modelul și logica de afaceri în front-end și puteți construi aplicații web agnostice pentru back-end. În această postare, vom face exact asta, pas câte un pas.

Deci, de unde să încep?

Mai întâi, să decidem natura aplicației pe care dorim să o construim. În acest ghid, am prefera să nu petrecem prea mult timp pe back-end, așa că vom scrie ceva bazat pe date care sunt ușor de atins pe Internet, cum ar fi o aplicație de feed pentru sport!

Deoarece se întâmplă să fiu un mare fan al curselor cu motor și al Formulei 1, voi folosi un serviciu API autosport pentru a acționa ca back-end. Din fericire, băieții de la Ergast sunt destul de amabili să ofere un API gratuit pentru motorsport, care va fi perfect pentru noi.

Pentru o prezentare a ceea ce vom construi, aruncați o privire la demonstrația live. Pentru a înfrumuseța demonstrația și pentru a arăta niște șabloane Angular, am aplicat o temă Bootstrap din WrapBootstrap, dar având în vedere că acest articol nu este despre CSS, îl voi abstrage din exemple și îl voi lăsa deoparte.

Tutorial Noțiuni introductive

Să lansăm aplicația noastră exemplu cu niște boilerplate. Recomand proiectul de semințe unghiulare, deoarece nu numai că vă oferă un schelet grozav pentru bootstrapping, dar oferă și terenul pentru testarea unitară cu Karma și Jasmine (nu vom face nicio testare în acest demo, așa că vom face doar lăsați lucrurile deoparte deocamdată; vedeți partea 2 a acestui tutorial pentru mai multe informații despre configurarea proiectului pentru testarea unitară și end-to-end).

EDITARE (mai 2014): De când am scris acest tutorial, proiectul unghiular-seed a trecut prin câteva schimbări grele (inclusiv adăugarea lui Bower ca manager de pachete). Dacă aveți îndoieli cu privire la modul de implementare a proiectului, aruncați o privire rapidă la prima secțiune a ghidului lor de referință. În partea 2 a acestui tutorial, Bower, printre alte instrumente, este tratat mai în detaliu.

OK, acum că am clonat depozitul și am instalat dependențele, scheletul aplicației noastre va arăta astfel:

tutorial angularjs - începeți cu scheletul

Acum putem începe să codificăm. În timp ce încercăm să construim un flux sportiv pentru un campionat de curse, să începem cu cea mai relevantă vedere: tabelul de campionat .

masa de campionat

Având în vedere că avem deja o listă de drivere definită în domeniul nostru (stați cu mine – vom ajunge acolo) și ignorând orice CSS (pentru lizibilitate), HTML-ul nostru ar putea arăta astfel:

 <body ng-app="F1FeederApp" ng-controller="driversController"> <table> <thead> <tr><th colspan="4">Drivers Championship Standings</th></tr> </thead> <tbody> <tr ng-repeat="driver in driversList"> <td>{{$index + 1}}</td> <td> <img src="img/flags/{{driver.Driver.nationality}}.png" /> {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}} </td> <td>{{driver.Constructors[0].name}}</td> <td>{{driver.points}}</td> </tr> </tbody> </table> </body>

Primul lucru pe care îl veți observa în acest șablon este utilizarea expresiilor („{{“ și „}}”) pentru a returna valori variabile. În dezvoltarea AngularJS, expresiile vă permit să executați unele calcule pentru a returna o valoare dorită. Câteva expresii valide ar fi:

  • {{ 1 + 1 }}
  • {{ 946757880 | date }}
  • {{ user.name }}

Efectiv, expresiile sunt fragmente asemănătoare JavaScript. Dar, în ciuda faptului că este foarte puternic, nu ar trebui să utilizați expresii pentru a implementa vreo logică de nivel superior. Pentru asta, folosim directive.

Înțelegerea directivelor de bază

Al doilea lucru pe care îl veți observa este prezența ng-attributes , pe care nu le-ați vedea în marcajul tipic. Astea sunt directive.

La un nivel înalt, directivele sunt markeri (cum ar fi atribute, etichete și nume de clase) care îi spun lui AngularJS să atașeze un anumit comportament unui element DOM (sau să îl transforme, să îl înlocuiască etc.). Să aruncăm o privire la cele pe care le-am văzut deja:

  • Directiva ng-app este responsabilă pentru bootstrap-ul aplicației dvs., definindu-și domeniul de aplicare. În AngularJS, puteți avea mai multe aplicații în aceeași pagină, astfel încât această directivă definește unde începe și se termină fiecare aplicație distinctă.

  • Directiva ng-controller definește controlorul care va fi responsabil de vizualizarea dvs. În acest caz, notăm driversController , care va furniza lista noastră de drivere ( driversList ).

  • Directiva ng-repeat este una dintre cele mai frecvent utilizate și servește la definirea domeniului de aplicare a șablonului atunci când parcurgeți colecțiile. În exemplul de mai sus, reproduce o linie din tabel pentru fiecare driver din driversList .

Adăugarea de controlere

Desigur, nu este de nici un folos pentru vederea noastră fără un controler. Să adăugăm driversController la controllers.js nostru:

 angular.module('F1FeederApp.controllers', []). controller('driversController', function($scope) { $scope.driversList = [ { Driver: { givenName: 'Sebastian', familyName: 'Vettel' }, points: 322, nationality: "German", Constructors: [ {name: "Red Bull"} ] }, { Driver: { givenName: 'Fernando', familyName: 'Alonso' }, points: 207, nationality: "Spanish", Constructors: [ {name: "Ferrari"} ] } ]; });

Poate ați observat variabila $scope pe care o transmitem ca parametru controlerului. Variabila $scope ar trebui să vă conecteze controlerul și vizualizările. În special, deține toate datele care vor fi utilizate în șablonul dvs. Orice adăugați la acesta (cum ar fi lista de driversList din exemplul de mai sus) va fi direct accesibil în vizualizările dvs. Deocamdată, să lucrăm doar cu o matrice de date simulată (statică), pe care o vom înlocui ulterior cu serviciul nostru API.

Acum, adăugați asta la app.js:

 angular.module('F1FeederApp', [ 'F1FeederApp.controllers' ]);

Cu această linie de cod, inițializam de fapt aplicația noastră și înregistrăm modulele de care depinde. Vom reveni la acel fișier ( app.js ) mai târziu.

Acum, să punem totul împreună în index.html :

 <!DOCTYPE html> <html> <head> <title>F-1 Feeder</title> </head> <body ng-app="F1FeederApp" ng-controller="driversController"> <table> <thead> <tr><th colspan="4">Drivers Championship Standings</th></tr> </thead> <tbody> <tr ng-repeat="driver in driversList"> <td>{{$index + 1}}</td> <td> <img src="img/flags/{{driver.Driver.nationality}}.png" /> {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}} </td> <td>{{driver.Constructors[0].name}}</td> <td>{{driver.points}}</td> </tr> </tbody> </table> <script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-route/angular-route.js"></script> <script src="js/app.js"></script> <script src="js/services.js"></script> <script src="js/controllers.js"></script> </body> </html>

Modul de greșeli minore, puteți acum să porniți aplicația și să vă verificați lista (statică) de drivere.

Notă: Dacă aveți nevoie de ajutor pentru depanarea aplicației și vizualizarea modelelor și a domeniului de aplicare în browser, vă recomand să aruncați o privire la minunatul plugin Batarang pentru Chrome.

Încărcarea datelor de pe server

Deoarece știm deja cum să afișăm datele controlerului nostru în viziunea noastră, este timpul să obținem efectiv date live de pe un server RESTful.

Pentru a facilita comunicarea cu serverele HTTP, AngularJS oferă serviciile $http și $resource . Primul este doar un strat deasupra XMLHttpRequest sau JSONP, în timp ce cel din urmă oferă un nivel mai ridicat de abstractizare. Vom folosi $http .

Pentru a abstra apelurile API-ului serverului nostru de la controler, să creăm propriul nostru serviciu personalizat care va prelua datele noastre și va acționa ca un înveliș în jurul $http , adăugând acest lucru la services.js noastre.js :

 angular.module('F1FeederApp.services', []). factory('ergastAPIservice', function($http) { var ergastAPI = {}; ergastAPI.getDrivers = function() { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK' }); } return ergastAPI; });

Cu primele două linii, creăm un nou modul ( F1FeederApp.services ) și înregistrăm un serviciu în acel modul ( ergastAPIservice ). Observați că transmitem $http ca parametru serviciului respectiv. Acest lucru îi spune motorului de injecție de dependență al lui Angular că noul nostru serviciu necesită (sau depinde de ) serviciul $http .

Într-un mod similar, trebuie să îi spunem lui Angular să includă noul nostru modul în aplicația noastră. Să-l înregistrăm cu app.js , înlocuind codul nostru existent cu:

 angular.module('F1FeederApp', [ 'F1FeederApp.controllers', 'F1FeederApp.services' ]);

Acum, tot ce trebuie să facem este să ne modificăm puțin controller.js , să includem ergastAPIservice ca dependență și vom fi gata:

 angular.module('F1FeederApp.controllers', []). controller('driversController', function($scope, ergastAPIservice) { $scope.nameFilter = null; $scope.driversList = []; ergastAPIservice.getDrivers().success(function (response) { //Dig into the responde to get the relevant data $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings; }); });

Acum reîncărcați aplicația și verificați rezultatul. Observați că nu am făcut nicio modificare șablonului nostru, dar am adăugat o variabilă nameFilter în domeniul nostru. Să folosim acea variabilă.

Filtre

Grozav! Avem un controler funcțional. Dar arată doar o listă de șoferi. Să adăugăm câteva funcționalități prin implementarea unei intrări simple de căutare a textului, care va filtra lista noastră. Să adăugăm următoarea linie la index.html , chiar sub eticheta <body> :

 <input type="text" ng-model="nameFilter" placeholder="Search..."/>

Acum folosim directiva ng-model . Această directivă leagă câmpul nostru de text la variabila $scope.nameFilter și se asigură că valoarea acesteia este întotdeauna actualizată cu valoarea de intrare. Acum, să mai vizităm index.html încă o dată și să facem o mică ajustare la linia care conține directiva ng-repeat :

 <tr ng-repeat="driver in driversList | filter: nameFilter">

Această linie îi spune ng-repeat că, înainte de a scoate datele, matricea driversList trebuie să fie filtrată după valoarea stocată în nameFilter .

În acest moment, intervine legarea de date în două sensuri: de fiecare dată când o valoare este introdusă în câmpul de căutare, Angular se asigură imediat că $scope.nameFilter pe care l-am asociat cu acesta este actualizat cu noua valoare. Deoarece legarea funcționează în ambele sensuri, în momentul în care valoarea nameFilter este actualizată, a doua directivă asociată acesteia (adică, ng-repeat ) primește și noua valoare și vizualizarea este actualizată imediat.

Reîncărcați aplicația și verificați bara de căutare.

bara de căutare a aplicației

Observați că acest filtru va căuta cuvântul cheie pe toate atributele modelului, inclusiv pe cele care nu le utilizau. Să presupunem că vrem să filtram numai după Driver.givenName și Driver.familyName : Mai întâi, adăugăm la driversController , chiar sub $scope.driversList = []; linia:

 $scope.searchFilter = function (driver) { var keyword = new RegExp($scope.nameFilter, 'i'); return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName); };

Acum, înapoi la index.html , actualizăm linia care conține directiva ng-repeat :

 <tr ng-repeat="driver in driversList | filter: searchFilter">

Reîncărcați aplicația încă o dată și acum avem o căutare după nume.

Trasee

Următorul nostru obiectiv este să creăm o pagină cu detaliile șoferului, care ne va permite să facem clic pe fiecare șofer și să vedem detaliile carierei sale.

În primul rând, să includem serviciul $routeProvider (în app.js ), care ne va ajuta să facem față acestor rute variate de aplicații . Apoi, vom adăuga două astfel de rute: unul pentru tabelul de campionat și altul pentru detaliile șoferului. Iată noul nostru app.js :

 angular.module('F1FeederApp', [ 'F1FeederApp.services', 'F1FeederApp.controllers', 'ngRoute' ]). config(['$routeProvider', function($routeProvider) { $routeProvider. when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}). when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}). otherwise({redirectTo: '/drivers'}); }]);

Odată cu această modificare, navigarea la http://domain/#/drivers va încărca driversController și va căuta vizualizarea parțială pentru a reda în partials/drivers.html . Dar asteapta! Încă nu avem opinii parțiale, nu? Va trebui să le creăm și pe acestea.

Vizualizări parțiale

AngularJS vă va permite să vă legați rutele la anumite controlere și vederi.

Dar mai întâi, trebuie să îi spunem lui Angular unde să reda aceste vederi parțiale. Pentru asta, vom folosi directiva ng-view , modificând index.html -ul nostru pentru a oglindi următoarele:

 <!DOCTYPE html> <html> <head> <title>F-1 Feeder</title> </head> <body ng-app="F1FeederApp"> <ng-view></ng-view> <script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-route/angular-route.js"></script> <script src="js/app.js"></script> <script src="js/services.js"></script> <script src="js/controllers.js"></script> </body> </html>

Acum, ori de câte ori navigăm prin rutele aplicației noastre, Angular va încărca vizualizarea asociată și o va reda în locul etichetei <ng-view> . Tot ce trebuie să facem este să creăm un fișier numit partials/drivers.html și să punem acolo HTML tabelul nostru de campionat. De asemenea, vom folosi această șansă pentru a lega numele șoferului la traseul nostru cu detaliile șoferului:

 <input type="text" ng-model="nameFilter" placeholder="Search..."/> <table> <thead> <tr><th colspan="4">Drivers Championship Standings</th></tr> </thead> <tbody> <tr ng-repeat="driver in driversList | filter: searchFilter"> <td>{{$index + 1}}</td> <td> <img src="img/flags/{{driver.Driver.nationality}}.png" /> <a href="#/drivers/{{driver.Driver.driverId}}"> {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}} </a> </td> <td>{{driver.Constructors[0].name}}</td> <td>{{driver.points}}</td> </tr> </tbody> </table>

În sfârșit, să decidem ce vrem să arătăm în pagina de detalii. Ce zici de un rezumat al tuturor faptelor relevante despre șofer (de exemplu, nașterea, naționalitatea) împreună cu un tabel care conține rezultatele recente ale acestuia? Pentru a face asta, adăugăm la services.js :

 angular.module('F1FeederApp.services', []) .factory('ergastAPIservice', function($http) { var ergastAPI = {}; ergastAPI.getDrivers = function() { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK' }); } ergastAPI.getDriverDetails = function(id) { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK' }); } ergastAPI.getDriverRaces = function(id) { return $http({ method: 'JSONP', url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK' }); } return ergastAPI; });

De data aceasta, oferim serviciului ID-ul șoferului, astfel încât să recuperăm informațiile relevante doar pentru un anumit șofer. Acum, modificăm controllers.js :

 angular.module('F1FeederApp.controllers', []). /* Drivers controller */ controller('driversController', function($scope, ergastAPIservice) { $scope.nameFilter = null; $scope.driversList = []; $scope.searchFilter = function (driver) { var re = new RegExp($scope.nameFilter, 'i'); return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName); }; ergastAPIservice.getDrivers().success(function (response) { //Digging into the response to get the relevant data $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings; }); }). /* Driver controller */ controller('driverController', function($scope, $routeParams, ergastAPIservice) { $scope.id = $routeParams.id; $scope.races = []; $scope.driver = null; ergastAPIservice.getDriverDetails($scope.id).success(function (response) { $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; }); ergastAPIservice.getDriverRaces($scope.id).success(function (response) { $scope.races = response.MRData.RaceTable.Races; }); });

Lucrul important de observat aici este că tocmai am injectat serviciul $routeParams în controlerul driverului. Acest serviciu ne va permite să accesăm parametrii URL (pentru :id , în acest caz) folosind $routeParams.id .

Acum că avem datele noastre în domeniu, avem nevoie doar de vizualizarea parțială rămasă. Să creăm un fișier numit partials/driver.html și să adăugăm:

 <section> <a href="./#/drivers"><- Back to drivers list</a> <nav class="main-nav"> <div class="driver-picture"> <div class="avatar"> <img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" /> <img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/> {{driver.Driver.givenName}} {{driver.Driver.familyName}} </div> </div> <div class="driver-status"> Country: {{driver.Driver.nationality}} <br/> Team: {{driver.Constructors[0].name}}<br/> Birth: {{driver.Driver.dateOfBirth}}<br/> <a href="{{driver.Driver.url}}" target="_blank">Biography</a> </div> </nav> <div class="main-content"> <table class="result-table"> <thead> <tr><th colspan="5">Formula 1 2013 Results</th></tr> </thead> <tbody> <tr> <td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td> </tr> <tr ng-repeat="race in races"> <td>{{race.round}}</td> <td><img src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td> <td>{{race.Results[0].Constructor.name}}</td> <td>{{race.Results[0].grid}}</td> <td>{{race.Results[0].position}}</td> </tr> </tbody> </table> </div> </section>

Observați că acum folosim directiva ng-show . Această directivă va afișa elementul HTML numai dacă expresia furnizată este true (adică nici false , nici null ). În acest caz, avatarul va apărea numai după ce obiectul driver a fost încărcat în scope de către controler.

Finisaje

Adăugați o grămadă de CSS și redați pagina. Ar trebui să ajungi cu ceva de genul acesta:

pagina redată cu CSS

Acum sunteți gata să porniți aplicația și să vă asigurați că ambele rute funcționează așa cum doriți. De asemenea, puteți adăuga un meniu static la index.html pentru a îmbunătăți capacitățile de navigare ale utilizatorului. Posibilitățile sunt nesfârșite.

EDITARE (mai 2014): Am primit multe solicitări pentru o versiune descărcabilă a codului pe care îl construim în acest tutorial. Prin urmare, am decis să-l lansez aici (dezbrăcat de orice CSS). Cu toate acestea, chiar nu recomand să îl descărcați, deoarece acest ghid conține fiecare pas de care aveți nevoie pentru a construi aceeași aplicație cu propriile mâini, ceea ce va fi un exercițiu de învățare mult mai util și mai eficient.

Concluzie

În acest moment al tutorialului, am acoperit tot ce ai nevoie pentru a scrie o aplicație simplă (cum ar fi un alimentator de Formula 1). Fiecare dintre paginile rămase din demonstrația live (de exemplu, tabelul de campionat al constructorului, detaliile echipei, calendarul) au aceeași structură de bază și concepte pe care le-am analizat aici.

În cele din urmă, rețineți că Angular este un cadru foarte puternic și abia am zgâriat suprafața în ceea ce privește tot ceea ce are de oferit. În partea a 2-a a acestui tutorial, vom oferi exemple de ce Angular se remarcă printre cadrele sale MVC front-end: testabilitatea. Vom revizui procesul de scriere și rulare a testelor unitare cu Karma, realizând integrarea continuă cu Yeomen, Grunt și Bower și alte puncte forte ale acestui fantastic cadru front-end.