Hoe Angular-sorteerfilters te upgraden
- Inleiding
- Stap 1 – Code van Git trekken
- Step 2 – Vervang de AngularJS Syntax
- Step 3 – Sortering toevoegen
- Sorteren in het Component
- Laten we eens kijken of het sorteren werkt
- Sortering toevoegen aan de sjabloon
- Fix the Cursor
- Step 4 – Filtering toevoegen
- Een filterfunctie toevoegen
- Filtering toevoegen aan de sjabloon
- Inspect the Product
- Stap 5 – Repareer de valutapijp
- Conclusie
Inleiding
AngularJS, Een van de nuttigste functies van het eerste aanbod van AngularJS was de mogelijkheid om gegevens op de pagina te filteren en te sorteren met behulp van alleen sjabloonvariabelen en -filters. Twee-weg data binding won veel bekeerlingen tot AngularJS.
Vandaag de dag, echter, veel front-end ontwikkelaars de voorkeur aan een-weg data binding, en die orderBy
en filter
filters zijn zonondergang met de komst van Angular. (Opmerking: in dit artikel zal ik “AngularJS” gebruiken om te verwijzen naar 1.x en gewoon “Angular” om te verwijzen naar 2+.)
Maar hoe worden we verondersteld om datzelfde effect te bereiken? Het antwoord ligt in onze componenten, dus laten we eens kijken naar een ngUpgrade project en leren hoe dit te doen!
Stap 1 – Code van Git trekken
We gaan stap voor stap het sjabloon van een vers herschreven component bijwerken. Daarna voegen we sorteren en filteren toe om alle functionaliteit te herstellen die het had in AngularJS. Dit is een belangrijke vaardigheid om te ontwikkelen voor het ngUpgrade proces.
Om te beginnen, neem even de tijd om het voorbeeld project dat we zullen gebruiken te clonen (vergeet niet om npm install
in zowel de public
en server
mappen uit te voeren). Bekijk deze commit voor ons startpunt:
git checkout 9daf9ab1e21dc5b20d15330e202f158b4c065bc3
Dit voorbeeld project is een ngUpgrade hybride project dat zowel AngularJS 1.6 als Angular 4 gebruikt. Het heeft een werkende Express API en een Webpack builds voor zowel ontwikkeling als productie. Voel je vrij om het te verkennen, fork het, en gebruik de patronen in je eigen projecten. Als je een versie van dit project wilt bekijken die Angular 5 gebruikt, bekijk dan deze repo. Voor het doel van deze tutorial zijn de verschillen tussen de twee versies niet van belang (ik zal wijzen op alles wat klein is).
Step 2 – Vervang de AngularJS Syntax
In dit stadium van onze applicatie, is onze orders component herschreven in Angular, met al zijn afhankelijkheden geïnjecteerd en opgelost. Als we echter zouden proberen onze applicatie uit te voeren, zouden we fouten in de console zien die duiden op problemen met onze template. Dat is wat we eerst moeten oplossen. We gaan de AngularJS syntax in het ordersjabloon (orders/orders.html
) vervangen, zodat we de route kunnen laden en de orders op de pagina kunnen tonen. Het filteren en sorteren doen we hierna.
Het eerste wat we moeten doen is ons ontdoen van alle instanties van $ctrl
in deze template. Ze zijn niet langer nodig in Angular. We kunnen gewoon een zoek en vervang doen om te zoeken naar $ctrl.
(let op de punt), en het vervangen door niets.
Nu laten we de data-ng-click
in onze knop op regel 13 vervangen. In Angular gebruiken we in plaats van ng-click
gewoon het click
event, met haakjes om aan te geven dat het om een event gaat. Haakjes geven een input aan, en haakjes geven een output of een event aan.
<button type="button" (click)="goToCreateOrder()" class="btn btn-info">Create Order</button>
We zeggen hier alleen dat op de click event, de goToCreateOrder
functie op onze orders component afgaat.
Voordat we verder gaan, laten we even een minuutje nemen om te bewijzen dat onze component daadwerkelijk aan het laden is. Commentarieer de hele div
die onze orders laadt (vanaf regel 17). Om de applicatie te draaien, open een terminal en voer de volgende commando’s uit:
cd servernpm start
Dat zal de Express server starten. Om de Webpack dev server te draaien, opent u een andere terminal en voert u uit:
cd publicnpm run dev
(U kunt deze processen draaiende houden voor de rest van deze tutorial.)
U zou moeten zien dat onze applicatie weer laadt. Als u naar de bestelroute gaat, ziet u dat de bestelcomponent correct wordt weergegeven.
We kunnen ook op de knop Bestel maken klikken en dan worden we correct naar onze bestelroute en ons bestelformulier gestuurd.
Okee, laten we teruggaan naar de HTML. Un-comment dat div
(onze app zal weer worden gebroken).
Laten we de rest van de instanties data-ng-click
vervangen door de (click)
event handler. U kunt ofwel Find & Replace gebruiken of gewoon de sneltoets van uw editor voor het selecteren van alle occurrences (in VS Code voor Windows is dit Ctrl+Shift+L).
Volgende, vervang alle occurrences van data-ng-show
door *ngIf
. Er is eigenlijk geen direct equivalent voor ng-show
in Angular, maar dat is niet erg. Het verdient de voorkeur om *ngIf
te gebruiken, omdat je op die manier elementen toevoegt en verwijdert uit het DOM in plaats van ze alleen maar te verbergen en te tonen. Dus, alles wat we moeten doen is onze data-ng-show
s vinden en vervangen door *ngIf
.
Ten slotte moeten we twee dingen doen om onze tabel body te repareren. Ten eerste, data-ng-repeat
vervangen door *ngFor="let order of orders"
. Merk op dat we ook de orderBy
en filter
filters in die regel verwijderen, zodat de hele tr
er als volgt uitziet:
<tr *ngFor="let order of orders">
Ten tweede kunnen we de data-ng
prefix verwijderen voor de href
link naar de order detail route. AngularJS zorgt hier nog steeds voor de routing, maar we hoeven dat voorvoegsel niet meer te gebruiken omdat dit nu een Angular-sjabloon is.
Als we de applicatie nog eens bekijken, kunt u zien dat de orders correct op het scherm worden geladen:
Er zijn natuurlijk wel een paar dingen mis mee. De sorteerlinks werken niet meer, en nu is onze valuta een beetje in de war omdat de valutapijp in Angular iets anders is dan zijn AngularJS-tegenhanger. Daar komen we nog op. Voor nu is dit een goed teken, omdat het betekent dat onze gegevens naar de component gaan en op de pagina worden geladen. Dus, we hebben de basis van deze template omgezet naar Angular. Nu zijn we klaar om het sorteren en filteren aan te pakken!
Step 3 – Sortering toevoegen
We hebben onze bestellingen op het scherm, maar we hebben nog geen manier om ze te ordenen of te sorteren. In AngularJS was het heel gebruikelijk om het ingebouwde orderBy
filter te gebruiken om de gegevens op de pagina te sorteren. Angular heeft niet langer een orderBy
filter. Dit is omdat het nu sterk wordt aangemoedigd om dat soort business logica in de component te plaatsen in plaats van het op de template te hebben. Dus, dat is wat we hier gaan doen. (Opmerking: we gaan hier gebruik maken van gewone oude functies en gebeurtenissen, niet van een reactieve formulier aanpak. Dit is omdat we proberen om deze dingen in kleine stapjes te begrijpen. Als je eenmaal de basis onder de knie hebt, voel je dan vrij om verder te gaan met observables!)
Sorteren in het Component
We hebben het orderBy
filter al verwijderd uit ng-repeat
toen we het veranderden in *ngFor
. Nu gaan we een sorteer functie maken op de orders component. We kunnen de klikgebeurtenissen op onze tabelkoppen gebruiken om die functie op te roepen en de eigenschap door te geven waarop we willen sorteren. We laten die functie ook heen en weer schakelen tussen oplopend en aflopend.
Open de orders component (./orders/orders.component.ts
) en voeg twee publieke eigenschappen toe aan de class. Deze zullen overeenkomen met de twee eigenschappen waar onze template al naar verwijst. De eerste wordt sortType
van het type string
. De tweede wordt sortReverse
van het type boolean
en we zetten de standaardwaarde op false. De sortReverse
eigenschap houdt alleen bij of de volgorde moet worden omgedraaid – zie het niet als een synoniem voor oplopend of aflopend.
Dus nu zou je dit moeten hebben na de declaratie van de titel in de klasse:
sortType: string;sortReverse: boolean = false;
Volgende voegen we de functie toe die we zullen gebruiken met de Array.sort prototype functie in JavaScript. Voeg dit toe na de functie goToCreateOrder
(maar nog steeds binnen de klasse):
dynamicSort(property) { return function (a, b) { let result = (a < b) ? -1 : (a > b) ? 1 : 0; return result; } }
Deze dynamische sorteerfunctie zal de waarde van de eigenschappen van objecten in een array vergelijken. De geneste ternaire functie kan een beetje lastig te begrijpen zijn op het eerste gezicht, maar het komt er op neer dat als de waarde van onze eigenschap van A kleiner is dan B, geef -1 terug. Anders, als het groter is, geef 1 terug. Als de twee gelijk zijn, geef je 0 terug.
Nou, dit is geen super verfijnde of diepe vergelijking. Er zijn veel geavanceerdere hulpfuncties die je zou kunnen schrijven om voor jou te sorteren, en voel je vrij om te experimenteren met hoe je deze kunt breken. Maar voor ons doel is het voldoende, en je kunt deze logica gewoon vervangen door de sorteerlogica die je zelf wilt.
Dat is dus onze helperfunctie. De sort functie op het Array prototype kan een functie doorgegeven krijgen die het kan gebruiken om items in een array te vergelijken. Laten we een functie met de naam sortOrders
op onze klasse maken die daarvan gebruik maakt met de nieuwe dynamicSort
functie:
sortOrders(property) { }
Het eerste wat we moeten doen is de sortType
eigenschap op onze klasse gelijk maken aan de eigenschap die is doorgegeven. Dan willen we de sortReverse
eigenschap aanzetten. We krijgen dan dit:
sortOrders(property) { this.sortType = property; this.sortReverse = !this.sortReverse;}
Nu kunnen we de sort
functie oproepen op this.orders
, maar onze dynamische sorteer functie doorgeven met onze eigenschap:
sortOrders(property) { this.sortType = property; this.sortReverse = !this.sortReverse; this.orders.sort(this.dynamicSort(property));}
En er is nog een laatste ding dat we moeten doen. We moeten onze dynamicSort
functie een klein beetje aanpassen om de volgorde van de array te kunnen omkeren voor oplopend of aflopend. Om dit te doen, koppelen we het resultaat van de dynamicSort
aan de sortReverse
eigenschap van de klasse.
Het eerste wat we doen is een variabele declareren:
let sortOrder = -1;
Dan kunnen we controleren of onze sortReverse
eigenschap van onze klasse waar of onwaar is. Als dat waar is, stellen we onze variabele voor de sorteervolgorde in op 1:
if (this.sortReverse) { sortOrder = 1; }
We koppelen onze functies op deze manier aan elkaar omdat we in onze sorteerfunctie omwille van de demonstratie een omschakeling uitvoeren. Om grondiger te zijn, zou een andere benadering zijn om een variabele te hebben met de naam sortDescending
in plaats van sortReverse
die wordt aangestuurd door een aparte functie. Als u deze route volgt, doet u het omgekeerde – sortOrder
zou 1 zijn tenzij sortDescending
waar was.
We zouden deze laatste twee dingen ook kunnen combineren in een ternaire expressie, maar voor de duidelijkheid laat ik het een beetje meer omslachtig. En om ons resultaat het tegenovergestelde te maken van wat het normaal zou zijn, kan ik result
gewoon vermenigvuldigen met onze sortOrder
. Dus onze dynamicSort
functie ziet er nu zo uit:
dynamicSort(property) { let sortOrder = -1; if (this.sortReverse) { sortOrder = 1; } return function(a, b) { let result = a < b ? -1 : a > b ? 1 : 0; return result * sortOrder; }; }
Overnieuw, dit is een demonstratie implementatie van sorteren, zodat u de belangrijkste concepten begrijpt van het gebruik van een aangepaste sorteer functie op uw component.
Laten we eens kijken of het sorteren werkt
Tot nu toe hebben we een dynamicSort
helper functie en een sortOrders
functie toegevoegd aan onze klasse, zodat we kunnen sorteren op onze component in plaats van op onze template.
Om te zien of deze functies werken, voegen we een standaard sortering toe aan onze ngOnInit
functie.
In ons forkJoin
abonnement, na de forEach
waar we de klantnaam eigenschap toevoegen, roepen we this.sortOrders
op en geven we de totale items eigenschap door:
this.sortOrders('totalItems');
Wanneer het scherm wordt vernieuwd, zou u moeten zien dat de bestellingen worden gesorteerd op het totale aantal items.
Nu hoeven we deze sortering alleen nog maar in onze sjabloon te implementeren door de functie sortOrders
in de koppelingen van de tabelkop op te roepen.
Sortering toevoegen aan de sjabloon
We hebben onze sortOrders
functie correct werkend op onze orders component, wat betekent dat we nu klaar zijn om het toe te voegen aan onze sjabloon, zodat de tabel headers weer klikbaar zijn.
Voordat we dat doen, laten we de standaard sortering in onze ngOnInit
functie veranderen in alleen ID:
this.sortOrders('id');
Dat is een beetje normaler dan het gebruik van het totaal aantal items.
Nu kunnen we aan ons sjabloon werken. Het eerste wat we willen doen is de functie sortOrders
in al onze klikgebeurtenissen oproepen. U kunt de instanties van sortType =
selecteren en ze vervangen door sortOrders(
. Vervolgens kunt u de instanties van ; sortReverse = !sortReverse
vervangen door )
.
We moeten ook twee van de property-namen aanpassen die we hier doorgeven, en ook in de *ngIf
instanties. Vervang de 3 instanties van orderId
door id
en de 3 instanties van customername
door customerName
.
Het laatste wat ik moet doen is elk van de href
tags in de headers tussen haakjes wikkelen zodat Angular het overneemt en deze links eigenlijk nergens heen gaan. De klik gebeurtenis zal het ding zijn dat wordt afgevuurd. Dus, de headers zouden dit patroon moeten volgen:
<th> <a ="" (click)="sortOrders('id')"> Order Id <span *ngIf="sortType == 'id' && !sortReverse" class="fa fa-caret-down"></span> <span *ngIf="sortType == 'id' && sortReverse" class="fa fa-caret-up"></span> </a></th>
Hop naar de browser en test al je tabel header links. Je zou moeten zien dat elk van onze eigenschappen nu sorteert, zowel in oplopende als aflopende volgorde. Geweldig!
Dit is geweldig, maar we zijn een ding kwijt – onze cursor is een selector, geen aanwijzer. Laten we dat met wat CSS oplossen.
Fix the Cursor
We hebben onze sortering goed werkend op onze bestelpagina, maar onze cursor is nu een selector in plaats van een aanwijzer, en dat is vervelend.
Er zijn een paar verschillende manieren waarop we CSS kunnen gebruiken om dit op te lossen:
- We zouden een klasse kunnen maken in het SCSS bestand van onze hoofd-app.
- We zouden in-line CSS kunnen schrijven, hoewel dat bijna nooit de voorkeur verdient.
- We zouden gebruik kunnen maken van Angular’s scoped CSS met behulp van de styles optie in de component decorator
We gaan voor de laatste optie, want alles wat we hoeven te doen is één regel toevoegen aan onze styles voor dit specifieke component.
Open de orders component klasse opnieuw. In de component decorator kunnen we een nieuwe eigenschap toevoegen, genaamd styles
. Styles is een array van strings, maar de strings zijn CSS regels. Om onze cursor te repareren, hoeven we alleen maar een regel uit te schrijven die zegt dat als we in een tabelrij een link hebben, we de cursor eigenschap veranderen in pointer. Onze decorator ziet er nu zo uit:
@Component({ selector: 'orders', template: template, styles: })
Nu, als we met de muis over onze rij headers gaan, zie je dat we de aanwijzer cursor hebben. Wat cool is aan deze aanpak is dat deze CSS regel geen invloed zal hebben op andere componenten. Het zal alleen van toepassing zijn op onze orders component!
Nu, laten we eens kijken of we iets kunnen doen aan onze filtering. Dat “filter filter” is verwijderd uit Angular, dus we zullen creatief moeten zijn en een manier moeten verzinnen om het op onze component te implementeren.
Step 4 – Filtering toevoegen
We zijn klaar om onze filterbox te vervangen die vroeger het AngularJS filter gebruikte om door de orders collectie te zoeken op basis van een string die we zochten. De AngularJS filter stond op onze template en had geen code nodig in onze controller of component. Tegenwoordig wordt dat soort logica in de template ontmoedigd. Het verdient de voorkeur om dat soort sorteren en filteren in onze componentklasse te doen.
Een filterfunctie toevoegen
Terug in onze component maken we een nieuwe array van orders genaamd filteredOrders
. Vervolgens geven we onze orders
-array door aan een filterfunctie die de filteredOrders
-array instelt. Tenslotte gebruiken we de filteredOrders
op onze template in onze *ngFor
in plaats van onze oorspronkelijke array. Op die manier wijzigen we nooit de gegevens die terugkomen van de server, we gebruiken alleen een subset ervan.
Het eerste wat we doen is de nieuwe eigenschap declareren op onze klasse :
filteredOrders: Order;
Dan, in onze forkJoin
die onze oorspronkelijke array van orders instelt, kunnen we de initiële staat van filteredOrders
instellen op onze orders array:
this.filteredOrders = this.orders;
Nu zijn we klaar om onze functie toe te voegen die daadwerkelijk het filteren voor ons zal doen. Plak deze functie direct na onze sorteerfuncties onderaan onze component:
filterOrders(search: string) { this.filteredOrders = this.orders.filter(o => Object.keys(o).some(k => { if (typeof o === 'string') return o.toLowerCase().includes(search.toLowerCase()); }) ); }
Laten we eens praten over wat er in deze functie gebeurt. Ten eerste geven we de functie een string eigenschap van search
. Dan lopen we door onze orders en vinden dan alle sleutels van de objecten. Voor alle sleutels gaan we kijken of er some
waarden zijn van die eigenschappen die overeenkomen met onze zoekterm. Dit stukje JavaScript kan er in het begin een beetje verwarrend uitzien, maar dat is in principe wat er gebeurt.
Merk op dat, in onze if
verklaring, we expliciet testen op strings. In ons voorbeeld beperken we onze query tot strings. We gaan niet proberen om te gaan met geneste eigenschappen, nummer eigenschappen, of iets dergelijks. Onze zoekterm zal overeenkomen met onze klantnaam eigenschap, en als we er ooit voor kiezen om ons adres of een andere string eigenschap weer te geven, zal het ook daar doorheen zoeken.
Natuurlijk kunnen we deze functie ook aanpassen om te testen op getallen, of door een andere laag van geneste objecten te kijken, en dat is helemaal aan jou. Net als bij het sorteren, beginnen we met een demonstratie-implementatie en laten je je fantasie gebruiken om het complexer te maken.
Over de functie sortOrders
gesproken, voordat we verder gaan, moeten we nog een laatste ding doen aan de component. We hoeven alleen maar sortOrders
te wijzigen om nu filteredOrders
te gebruiken en niet onze oorspronkelijke orders
, omdat we willen dat het filter voorrang krijgt boven het sorteren. Verander het gewoon in deze:
sortOrders(property) { this.sortType = property; this.sortReverse = !this.sortReverse; this.filteredOrders.sort(this.dynamicSort(property));}
Nu zijn we klaar om deze filtering op het sjabloon te implementeren.
Filtering toevoegen aan de sjabloon
Laten we teruggaan naar onze sjabloon en deze aanpassen om onze filtering te gebruiken.
Het eerste wat we moeten doen is data-ng-model
vervangen. In plaats daarvan gaan we het keyup
event gebruiken, dus we schrijven, “keyup” en zetten er haakjes omheen ((keyup)
). Dit is een ingebouwd event in Angular waarmee we een functie kunnen uitvoeren op het moment dat een invoer wordt ingedrukt. Omdat we onze functie filterOrders
hebben genoemd, wat de naam was van de eigenschap die we in het AngularJS filter doorgaven, hoeven we er alleen maar haakjes naast te zetten. Onze invoer ziet er tot nu toe zo uit:
<input type="text" class="form-control" placeholder="Filter Orders (keyup)="filterOrders()">
Maar wat geven we door aan de filter orders functie? Nou, standaard geven events iets door dat $event
heet. Dit bevat iets dat target
heet, en dat bevat dan de waarde van de invoer. Er is een probleem met het gebruik van $event
. Het is erg moeilijk om die nevelige types bij te houden, omdat target.value
echt van alles kan zijn. Dit maakt het moeilijk om te debuggen of om te weten welk type waarde wordt verwacht. In plaats daarvan heeft Angular een heel handig ding dat we kunnen doen, namelijk een template variabele toewijzen aan deze invoer.
Gelukkig biedt Angular ons een methode om dit te doen. Na onze input tag, kunnen we het hash teken (#) toevoegen en dan de naam van ons gewenste model. Laten we het #ordersFilter
noemen. Het maakt niet uit waar in de tag je dit zet of hoe je het noemt, maar ik zet het graag na de input, zodat je ziet welk model bij welke input hoort als ik even op de pagina kijk.
Nu kan ik die variabele doorgeven aan onze filterOrders
functie op de keyup
gebeurtenis. We hebben het hash symbool er niet voor nodig, maar we moeten wel .value
toevoegen. Dit zal de werkelijke waarde van het model doorgeven en niet het hele model zelf. Onze voltooide invoer ziet er zo uit:
<input #ordersFilter type="text" class="form-control" placeholder="Filter Orders" (keyup)="filterOrders(ordersFilter.value)">
Tot slot moeten we onze *ngFor
aanpassen om de filteredOrders
array te gebruiken in plaats van de gewone orders
array:
<tr *ngFor="let order of filteredOrders">
Inspect the Product
U kunt zien hoeveel schoner ons sjabloon is nu onze filtering en sortering in de component zit.
Nu laten we dit eens bekijken in de browser. Als u wat tekst in het vak invoert, zoals “sally”, zou u moeten zien dat onze bestellingen veranderen en dat de sortering erbovenop werkt:
Tof, we hebben weer een AngularJS-functie vervangen!
Nu hoeven we nog maar één ding te doen aan dit component: de valutapijp repareren.
Stap 5 – Repareer de valutapijp
De laatste stap is het bijwerken van het voormalige valutafilter, dat in Angular nu de valutapijp wordt genoemd. We hoeven alleen maar een paar parameters toe te voegen aan de pijp in het sjabloon die we in AngularJS niet hoefden op te geven. Dit gedeelte verschilt als je Angular 4 of Angular 5 gebruikt:.
In Angular 4, doe je dit:<td>{{order.totalSale | currency:'USD':true}}</td>
In Angular 5+, doe je dit:<td>{{order.totalSale | currency:'USD':'symbol'}}</td>
De eerste optie is de valutacode (er zijn er veel, je bent niet beperkt tot Amerikaanse dollars!). De tweede is de symboolweergave. In Angular 4 is dit een boolean die aangeeft of het valutasymbool of de code moet worden gebruikt. In Angular 5+ zijn de opties symbol
, code
, of symbol-narrow
als strings.
U zou nu het verwachte symbool moeten zien:
En we zijn klaar! Om de afgewerkte code te zien, bekijk deze commit.
Conclusie
Jullie hebben goed werk geleverd door dit tot het einde vol te houden! Hier is wat we bereikt hebben in deze handleiding:
- Vervangen van AngularJS template syntax met Angular syntax
- Verplaatsen van sorteren naar de component
- Gebruik van scoped CSS styles
- Verplaatsen van filteren naar de component
- Vervangen van het AngularJS valuta filter met de Angular valuta pijp
Waar moet je nu verder mee? Er zijn veel dingen die je zou kunnen doen:
- Het sorteren geavanceerder maken (bijvoorbeeld: moet de volgorde worden gereset of hetzelfde blijven als de gebruiker op een nieuwe header klikt?)
- Het filteren geavanceerder maken (zoeken op getallen of geneste eigenschappen)
- Overgaan op een reactieve aanpak. Je zou kunnen luisteren naar een observable van waardeveranderingen in plaats van de
keyup
functie en daar sorteren en filteren in doen. Met observabelen kun je ook leuke dingen doen, zoals de input debouncen!