A legjobb szögletes példák

Az Angular egy TypeScript alapú nyílt forráskódú keretrendszer, amelyet a frontend webalkalmazások fejlesztésére használnak. Az AngularJS utódja, és az Angular összes említése a 2-es és újabb verziókra vonatkozik. Az Angular olyan funkciókkal rendelkezik, mint a generikus, a statikus gépelés, és néhány ES6-szolgáltatással is.

Verziótörténet

A Google 2010. október 20-án jelentette meg az AngularJS eredeti verzióját. Az AngularJS stabil kiadása 2017. december 18-án volt, az 1.6.8 verzió. Az AngularJS 1.7-es verziójának utolsó jelentős kiadására 2018. július 1-jén került sor, és jelenleg 3 éves hosszú távú támogatási időszakban van. Az Angular 2.0-t először 2014. szeptember 22-én jelentették be az ng-Europe konferencián. Az Angular 2.0 egyik új jellemzője a dinamikus betöltés, és az alapvető funkciók nagy részét modulokra helyezték át.

Néhány módosítás után az Angular 4.0 2016 decemberében jelent meg. Az Angular 4 visszafelé kompatibilis az Angular 2.0-val, és néhány új funkció a HttpClient könyvtár és az új útválasztó életciklus-eseményei. Az Angular 5 2017. november 1-jén jelent meg, amelynek fő jellemzője a progresszív webalkalmazások támogatása. Az Angular 6 2018 májusában, az Angular 7 pedig 2018 októberében jelent meg. A legújabb stabil verzió a 7.0.0.

Telepítés

Az Angular telepítésének legegyszerűbb módja az Angular CLI. Ez az eszköz lehetővé teszi új projektek létrehozását és komponensek, szolgáltatások, modulok és így tovább generálását, az Angular csapat által legjobb gyakorlatnak tartott szabvány szerint.

Szögletes 2.x és felfelé

Telepítse a szögletes CLI-t

npm install -g @angular/cli

Hozzon létre egy munkaterületet és egy kezdeti alkalmazást

Alkalmazásokat egy szögletes munkaterület kontextusában fejleszt. A munkaterület egy vagy több projekt fájljait tartalmazza. A projekt azok a fájlok, amelyek egy alkalmazást, egy könyvtárat vagy egy végpontok közötti (e2e) teszteket tartalmaznak.

ng new my-app

Az alkalmazás kiszolgálása

Az Angular tartalmaz egy szervert, amely lehetővé teszi az alkalmazás helyi felépítését és kiszolgálását.

  1. Navigáljon a munkaterület mappájához ( my-app)

Indítsa el a kiszolgálót ng serveaz --openopcióval rendelkező CLI paranccsal

cd my-app ng serve --open

Hurrá, te hoztad létre az első szögletes alkalmazást !!!

Alkatrészek

Az Angular számos vázlatot tartalmaz az építési alkalmazásokhoz. Az alkatrészek ilyen vázlatok. Egyetlen logikai egységet ölelnek fel, amely az alkalmazás egyetlen részével foglalkozik. Az alkatrészek a hatékonyabb működés érdekében gyakran társulnak más sémákkal.

Az alkatrészek egyszerűsítik az alkalmazást. Elsődleges céljuk, hogy a logikát a látható felület egyetlen szakaszába továbbítsák. Az alkalmazások lépésről lépésre történő összeállításához komponensenként kell felépítenie. Az alkatrészek végül is Angular építőelemeként működnek.

Alkatrészosztály és metaadatok

A CLI parancs ng generate component [name-of-component]a következőket eredményezi.

import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) export class ExampleComponent implements OnInit { constructor() { } ngOnInit() { } }

Ez az alapvető csontváz, amelyből minden nagy alkotóelem származik. A @Componentdekoratőr a legfontosabb rész. Enélkül a fenti példa általános osztálytá válik. Az Angular a dekoratőrökre támaszkodik az osztály vázlatos típusának felismerésére.

@Componentmetaadatokat egyetlen objektumként fogad. A díszítők csak a JavaScript funkciói a motorháztető alatt. Érveket vesznek fel, mint a metaadat-objektum esetében. A metaadat-objektum konfigurálja az összetevő alapvető függőségeit. Minden mező szerepet játszik.

  • selector: megmondja az Angularnak, hogy társítsa a komponenst az alkalmazás HTML sablonjának egy bizonyos eleméhez.
  • templateUrl: elfogadja az összetevő HTML-fájljának fájlhelyét (itt jelennek meg az adatok).
  • styleUrls:elfogad egy tömb stíluslapfájl-helyet (karakterláncot). Ezek a stíluslapok a komponens hozzárendelt sablonját célozzák meg.

Gondoljon a metaadatokra, mint a konfiguráció nagy foltjára. A dekoratőr úgy viszi, hogy elő tudja állítani az alkatrészre jellemző adatokat. A dekoratőr az alapul szolgáló osztályt az osztály viselkedéséhez szükséges adatokkal díszíti . A komponens osztálya, hogy van.

Az osztály aláírása alapértelmezés szerint exportál, így az összetevő importálható. ngOnInitis megvalósul. implementsutasítja az osztályt, hogy határozzon meg bizonyos módszereket az interfész meghatározása szerint. ngOnInitegy életciklus horog.

Komponens adatok

Az adatok mindent hajtanak. Az alkotóelemek sem kivételek. Az alkatrészek összesítik az összes adatot. Az adatok külsõ fogadásához egy komponensnek kifejezetten nyilatkoznia kell róla. Ez az adatvédelmi forma megakadályozza, hogy az információk összetörjenek az összetevőfán.

Az adatok meghatározzák, hogy mi kerül megjelenítésre az összetevő osztálytól a sablonig. Az osztály adatainak bármilyen frissítése frissíti (vagy legalábbis kell) a sablon megjelenítését.

Az alkatrészek gyakran inicializálják az adatokat tároló tagok (vagy változók) halmazát. A kényelem érdekében az alkatrészosztály-logikában az egészet használják. Ez az információ táplálja a sablont eredményező logikát és annak viselkedését. Lásd a következő példát.

// ./components/example/example.component.ts import { Component, OnInit } from '@angular/core'; import { Post, DATA } from '../../data/posts.data'; @Component({ selector: 'app-example', templateUrl: './example.component.html' }) export class ExampleComponent implements OnInit { username: string; totalPosts: number; allPosts: Post[]; deletePost(index: number): void { this.allPosts.splice(index, 1); this.totalPosts = this.allPosts.length; } ngOnInit(): void { this.username = DATA.author; this.totalPosts = DATA.thePosts.length; this.allPosts = DATA.thePosts; } }

{{ username }}

Change Name:

Posts: {{ totalPosts }}


    DELETE
    {{ post.title }}

    {{ post.body }}


Vegye figyelembe az összetevő interakcióját az adataival. Először onnan szerzi be, ../../data/posts.datamielőtt továbbítaná a megjelenítésre szolgáló sablonba.

Az adatok a sablonban jelennek meg. A kettős göndör zárójelben a változó értéke leképeződik az alkatrészosztályról a zárójelre. A *ngForhurkok az allPostsosztálytömbön. A gombra kattintva egy adott elemet allPostsindexével eltávolít . Még a legfelsőt is megváltoztathatja, usernameha beírja a beviteli mezőbe.

The above interactions alter the component class' data which in turn updates the component's template HTML. Components provide the backbone logic that facilitates the flow of data. The template HTML makes that data readable to the user.

Data Binding

Data often defines the look of an application. Interpreting that data into the user interface involves class logic (*.component.ts) and a template view (*.component.html) . Angular connects them through data binding. Think of data binding as a tool for component interaction.

Component and Template

The component stores most of its logic and data inside of its class decorated with @Component. This decorator defines the class as a component with template HTML. The template of the component represents the class within the application. The focus here needs to be between the component's class and the template HTML.

This is where data binding occurs. Element properties and events get assigned values. These values, defined by the component class, serve either one of two roles. One is to produce data that the template then receives. The other handles events emitted by the template element.

Code Example

Element Properties

To recognize data-bound element properties, Angular uses a special bracket syntax.

// my.component.ts @Component({ templateUrl: './my.component.html' }) export class MyComponent { value:type = /* some value of type */; }
 innerHTML

Bear with me on this one.

[property] mirrors the property in the Domain Object Model (DOM) element's object node. Do not confuse object properties with a DOM element's attributes. Properties and attributes often share the same name and do the same thing. There is one clear distinction however.

Remember that attr (attributes) is a single property of the underlying DOM object. It gets declared at the DOM’s instantiation with attribute values matching the element's definition. It maintains the same value after that. Properties each have their own key-value field in a DOM object node. These properties are mutable post-instantiation.

Know the difference between attributes and properties. It will lead to a better understanding of how Angular binds data to properties (property binding). Angular will hardly ever bind data to an element’s attributes. Exceptions to this are very rare. One last time: Angular binds component data to properties, not attributes!

Referring back to the example, the [ … ] in the element's property assignment have special meaning. The brackets show that property is bound to “value” on the right of the assignment.

value also has special meaning within context of the brackets. value by itself is a string literal. Angular reads it and matches its value against component class members. Angular will substitute the value of the matching member attribute. This of course refers to the same component class that hosts the template HTML.

The unidirectional flow of data from component to template is complete. The member matched against right assignment of the bracketed property provides the value. Note that changes to the member's value in the component class percolate down to the template. That is Angular's change detection at work. Changes within the template's scope have no effect on the component class member.

Key take-away: the component class provides the data while the template displays it.

I failed to mention that data values can also show up in a component’s innerHTML. This last example implements double curly braces. Angular recognizes these braces and interpolates the matching component class data into the innerHTML of the div.

 The value of the component class member ‘value’ is {{value}}. 

Event Handling

If the component supplies data, then the template supplies events.

// my.component.ts @Component({ templateUrl: './my.component.html' }) export class MyComponent { handler(event):void { // function does stuff } }
// my.component.html innerHTML

This works similarly to property binding.

The (event) pertains to any valid event type. For example, one of the most common event types is click. It emits when you click your mouse. Regardless of the type, event is bound to “handler” in the example. Event handlers are usually member functions of the component class.

The ( … ) are special to Angular. Parenthesis tell Angular an event is bounded to the right assignment of handler. The event itself originates from the host element.

When the event does emit, it passes the Event object in the form of $event. The handler maps to the identically named handler function of the component class. The unidirectional exchange from the event-bound element to the component class is complete.

Emitting events from the handler, while possible, do not impact the template element. The binding is unidirectional after all.

Directives

Directives are component elements and attributes created and recognized by Angular. Angular associates the element or attribute with its corresponding class definition. @Directive or @Component decorates these classes. Both are indicative to Angular that the class performs as a directive.

Some directives modify the style of the host element. Other directives display views or insert into existing ones as embedded views. On other words, they alter the HTML layout.

In any case, directives signal the Angular compiler. They mark components for modification depending on the class logic of the directive.

Structural Directive

Here are three examples of structural directives. Each one has a logical counterpart (if, for, and switch).

  • *ngIf
  • *ngFor
  • *ngSwitchCase and *ngSwitchDefault

Important note: all three are available through the CommonModule import. It is available from @angular/common for importation within the application's root module.

*ngIf

*ngIf tests a given value to see if it is truthy or falsy based off general boolean evaluation in JavaScript. If truthy, the element and its innerHTML show up. Otherwise, they never render to the Domain Object Model (DOM).

Hello!

Hi!

This is a contrived example. Any member value from the template's component class can be substituted in for true or false.

NOTE: You also can do following thing with *ngIf to get access to observalbe value

 {{ anyNameYouWant }} 
*ngFor

*ngFor loops based off a right-assigned, microsyntactic expression. Microsyntax moves beyond the scope of this article. Know that microsyntax is a short form of logical expression. It occurs as a single string capable of referencing class member values. It can loop iterable values which makes it useful for *ngFor.


    
  • Potato {{ i + 1 }}: {{ potato }}

[‘Russet’, ‘Sweet’, ‘Laura’] is an iterable value. Arrays are one of the most common iterables. The *ngFor spits out a new per array element. Each array element is assigned the variable potato. This is all done utilizing microsyntax. The *ngFor defines the structural content of the ulelement. That is characteristic of a structural directive.

NOTE: You can also do following thing with *ngFor directive to get access to observalbe value (hacky)

 {{ anyNameYouWant }} 
*ngSwitchCase and *ngSwitchDefault

These two structural directives work together to provide switch functionality to template HTML.

{{ potato }} is a Russet Potato.

{{ potato }} is a Sweet Potato.

{{ potato }} is a Laura Potato.

{{ potato }} is not a Russet, Sweet, nor Laura Potato.

Only one of the *ngSwitch… expressions renders. Notice the [ngSwitch] attribute inside of the divelement wrapping the switch. This passes the value of potato along the *ngSwitch... chain. This chain of structural directives determine which h1 element renders.

As such, [ngSwitch] is not a structural directive unlike the *ngSwitch… statements. It passes the value along whereas the switch block determines the final layout of HTML.

Remember that stylization and value passing are not the responsibility of structural directives. That concerns attribute directives. Structural directives determine only the layout.

Pipes

Output data transformations ensure data is in a desirable format by the time it loads onto the user’s screen. Normally data transforms behind the scenes. With pipes, transforming data can take place in the template HTML. Pipes transform template data directly.

Pipes look nice and are convenient. They help keep the component’s class lean of basic transformations. To put it technically, pipes encapsulate data transformation logic.

Use Cases

Angular comes prepackaged with a basic set of pipes. Working with a couple of them will develop the intuition to handle the rest. The following list provides three examples.

  • AsyncPipe
  • DatePipe
  • TitleCasePipe
AsyncPipe

This sections requires a basic understanding of Promises or Observables to fully appreciate. The AsyncPipe operates on either of the two. AsyncPipe extracts data from Promises/Observables as output for whatever comes next.

In the case of Obervables, AsyncPipe subscribes automatically to the data source. Regardless of where the data comes from, the AsyncPipe subscribes to the source observable. async is the syntactical name of AsyncPipe as shown below.


    
  • Potatoe {{i + 1}}: {{potato}}

In the example, potatoSack$ is an Observable pending an upload of potatoes. Once the potatoes arrive, either synchronously or asynchronously, the AsyncPipe receives them as an iterable array. The list element then fills up with potatoes.

DatePipe

Formatting date strings takes a fair bit of hacking with the JavaScript Date object. The DatePipe provides a powerful way to format dates assuming the given input is a valid time format.

TitleCasePipe

Transforms text to title case. Capitalizes the first letter of each word, and transforms the rest of the word to lower case. Words are delimited by any whitespace character, such as a space, tab, or line-feed character.

// example.component.ts @Component({ templateUrl: './example.component.html' }) export class ExampleComponent { timestamp:string = ‘2018-05-24T19:38:11.103Z’; }
 Current Time: { date:‘short’} 

The format of the above timestamp is ISO 86011—not the easiest to read. The DatePipe (date) transforms the ISO date format into a more conventional mm/dd/yy, hh:mm AM|PM. There are many other formatting options. All these options are in the official documentation.

Creating Pipes

While Angular only has a set number of pipes, the @Pipe decorator lets developers create their own. The process begins with ng generate pipe [name-of-pipe], replacing [name-of-pipe] with a preferable filename. This command yields the following:

import { Pipe, PipeTransform } from ‘@angular/core’; @Pipe({ name: 'example' }) export class ExamplePipe implements PipeTransform { transform(value: any, args?: any): any { return null; } }

This pipe template simplifies custom pipe creation. The @Pipe decorator tells Angular the class is a pipe. The value of name: ‘example’, in this case being example, is the value Angular recognizes when scanning template HTML for custom pipes.

On to the class logic. The PipeTransform implementation provides the instructions for the transformfunction. This function has special meaning within context of the @Pipe decorator. It receives two parameters by default.

value: any is the output that the pipe receives. Think of { someValue } . The value of someValue gets passed to the transform function’s value: any parameter. This is the same transform function defined in the ExamplePipe class.

args?: any is any argument that the pipe optionally receives. Think of { example:[some-argument] } . [some-argument] can be replace by any one value. This value gets passed to the transform function’s args?: any parameter. That is, the transform function defined in ExamplePipe's class.

Whatever the function returns (return null;) becomes the output of the pipe operation. Take a look at the next example to see a complete example of ExamplePipe. Depending on the variable the pipe receives, it either uppercases or lowercases the input as new output. An invalid or nonexistent argument will cause the pipe to return the same input as output.

// example.pipe.ts @Pipe({ name: 'example' }) export class ExamplePipe implements PipeTransform { transform(value:string, args?:string): any { switch(args || null) { case 'uppercase': return value.toUpperCase(); case 'lowercase': return value.toLowerCase(); default: return value; } } }
// app.component.ts @Component({ templateUrl: 'app.component.html' }) export class AppComponent { someValue:string = "HeLlO WoRlD!"; }
{ example }
{ someValue }
{ example:‘lowercase’ }

Lifecycle Hooks

Lifecycle hooks are timed methods. They differ in when and why they execute. Change detection triggers these methods. They execute depending on the conditions of the current cycle. Angular runs change detection constantly on its data. Lifecycle hooks help manage its effects.

An important aspect of these hooks is their order of execution. It never deviates. They execute based off a predictable series of load events produced from a detection cycle. This makes them predictable. Some assets are only available after a certain hook executes. Of course, a hook only execute under certain conditions set in the current change detection cycle.

In Order of Execution:

ngOnChanges

ngOnChanges triggers following the modification of @Input bound class members. Data bound by the @Input() decorator come from an external source. When the external source alters that data in a detectable manner, it passes through the @Input property again.

With this update, ngOnChanges immediately fires. It also fires upon initialization of input data. The hook receives one optional parameter of type SimpleChanges. This value contains information on the changed input-bound properties.

ngOnInit

ngOnInit fires once upon initialization of a component’s input-bound (@Input) properties. The next example will look similar to the last one. The hook does not fire as ChildComponent receives the input data. Rather, it fires right after the data renders to the ChildComponent template.

ngDoCheck

ngDoCheck fires with every change detection cycle. Angular runs change detection frequently. Performing any action will cause it to cycle. ngDoCheck fires with these cycles. Use it with caution. It can create performance issues when implemented incorrectly.

ngDoCheck lets developers check their data manually. They can trigger a new application date conditionally. In conjunction with ChangeDetectorRef, developers can create their own checks for change detection.

ngAfterContentInit

ngAfterContentInit fires after the component's content DOM initializes (loads for the first time). Waiting on @ContentChild(ren) queries is the hook's primary use-case.

@ContentChild(ren) queries yield element references for the content DOM. As such, they are not available until after the content DOM loads. Hence why ngAfterContentInit and its counterpart ngAfterContentChecked are used.

ngAfterContentChecked

ngAfterContentChecked fires after every cycle of change detection targeting the content DOM. This lets developers facilitate how the content DOM reacts to change detection. ngAfterContentCheckedcan fire frequently and cause performance issues if poorly implemented.

ngAfterContentChecked fires during a component’s initialization stages too. It comes right after ngAfterContentInit.

ngAfterViewInit

ngAfterViewInit fires once after the view DOM finishes initializing. The view always loads right after the content. ngAfterViewInit waits on @ViewChild(ren) queries to resolve. These elements are queried from within the same view of the component.

In the example below, BComponent’s h3 header is queried. ngAfterViewInit executes as soon as the query's results are available.

ngAfterViewChecked

ngAfterViewChecked fires after any change detection cycle targeting the component’s view. The ngAfterViewChecked hook lets developers facilitate how change detection affects the view DOM.

ngOnDestroy

ngOnDestroy fires upon a component’s removal from the view and subsequent DOM. This hook provides a chance to clean up any loose ends before a component's deletion.

Views

Views are almost like their own virtual DOM. Each view contains a reference to a corresponding section of the DOM. Inside a view are nodes that mirror what is in the this section. Angular assigns one view node per DOM element. Each node holds a reference to a matching element.

When Angular checks for changes, it checks the views. Angular avoids the DOM under the hood. The views reference the DOM on its behalf. Other mechanisms are in place to ensure that view changes render to the DOM. Conversely, changes to the DOM do not affect the views.

Again, views are common across all development platforms besides the DOM. Even if developing for one platform, views are still considered best practice. They guarantee Angular has a correct interpretation of the DOM.

Views may not exist on third-party libraries. Direct DOM manipulation is an escape hatch for this kind of scenario. Granted, do not expect the application to function cross-platform.

Types of Views

There are two main types of views: embedded and host.

There also exists view containers. They hold embedded and host views and are often referred to as simple “views”.

Every @Component class registers a view container (view) with Angular. New components generate a custom selector targeting a certain DOM element. The view attaches to that element wherever it appears. Angular now knows the component exists looking at the view model.

Host Views and Containers

Host views host dynamic components. View containers (views) attach automatically to elements already in the template. Views can attach to any element beyond what is unique to component classes.

Think of the traditional method of component generation. It begins by creating a class, decorating it with @Component, and filling in metadata. This approach occurs for any pre-defined component element of the template.

Try using the Angular command-line interface (CLI) command: ng generate component [name-of-component]. It yields the following.

import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-example', templateUrl: './example.component.html', styleUrls: ['./example.component.css'] }) export class ExampleComponent implements OnInit { constructor() { } ngOnInit() { } }

This creates the component with the selector app-example. This attaches a view container to in the template. If this were the root of the application, its view would encapsulate all other views. The root view marks the beginning of the application from Angular's perspective.

Creating components dynamically and registering them in the Angular view model takes a few extra steps. Structural directives help manage dynamic content (*ngIf, *ngFor, and *ngSwitch…). Directives do not scale to bigger applications however. Too many structural directives complicates the template.

This is where instantiating components from existing class logic comes in handy. These components need to create a host view that can insert into the view model. Host views holds data for components so that Angular recognizes their structural purpose.

Embedded Views

Structural directives create an ng-template surrounding a chunk of HTML content. The directive's host element has a view container attached. This make it so that the content can conditionally render into its intended layout.

The ng-template holds embedded view nodes representing each element within its innerHTML. ng-template is by no means a DOM element. It comments itself out. The tags define the extend of its embedded view.

Instantiating an embedded view requires no external resources beyond its own reference. The @ViewChild query can fetch that.

With the template reference, calling createEmbeddedView from it does the trick. The innerHTML of the reference becomes its own embedded view instance.

In the next example, is a view container. ng-container gets commented out during compilation just like ng-template. Thus it provides an outlet for inserting the embedded view while keeping the DOM lean.

The embedded view template inserts at the layout location of ng-container. This newly inserted view has no additional view encapsulation besides the view container. Remember how that differs from host views (host views attach to their ng-component element wrapper).

import { Component, AfterViewInit, ViewChild, ViewContainerRef, TemplateRef } from '@angular/core'; @Component({ selector: 'app-example', template: ` 

Application Content

End of Application

Template Content

Dynamically Generated!

` }) export class ExampleComponent implements AfterViewInit { @ViewChild("template", { read: TemplateRef }) tpl: TemplateRef; @ViewChild("container", { read: ViewContainerRef }) ctr: ViewContainerRef; constructor() { } ngAfterViewInit() { const view = this.tpl.createEmbeddedView(null); this.ctr.insert(view); } }

@ViewChild queries for the template reference variable#template. This provides a template reference of type TemplateRef. TemplateRef holds the createEmbeddedView function. It instantiates the template as an embedded view.

The single argument of createEmbeddedView is for context. If you wanted to pass in additional metadata, you could do it here as an object. The fields should match up with the ng-template attributes (let-[context-field-key-name]=“value”). Passing null indicates no extra metadata is necessary.

A second @ViewChild query provides a reference to ng-container as a ViewContainerRef. Embedded views only attach to other views, never the DOM. The ViewContainerRef references the view that takes in the embedded view.

An embedded view may also insert into the component view of . This approach positions the view at the very end of ExampleComponent’s view. In this example however, we want the content to show up in the very middle where ng-container sits.

The ViewContainerRefinsert function inserts the embedded view into the ng-container. The view content shows ups in the intended location right in the middle of ExampleComponent's view.

Routing

Routing is essential. Many modern web applications host too much information for one page. Users should not have to scroll through an entire application’s worth of content either. An application needs to split itself into distinguishable sections.An Angular best practice is to load and configure the router in a separate, top-level module that is dedicated to routing and imported by the root AppModule.

Users prioritize necessary information. Routing helps them find the application section with such information. Any other information useful to other users may exist on an entirely separate route. With routing, both users can find what they need quickly. Irrelevant details stay obscured behind irrelevant routes.

Routing excels at sorting and restricting access to application data. Sensitive data should never display to unauthorized users. Between every route the application may intervene. It can examine a user’s session for authentication purposes. This examination determines what the route renders if it should render at all. Routing gives developers the perfect chance to verify a user before proceeding.

As for Angular, routing takes up its own entire library within the framework. All modern front-end frameworks support routing, and Angular is no different. Routing happens from the client-side using either hash or location routing. Both styles allow the client to manage its own routes. No additional assistance from the server is necessary past the initial request.

Basic Routing

Routing utilities export with RouterModule available from @angular/router. It is not part of the core library since not all applications require routing. The most conventional way to introduce routing is as its own feature module.

As route complexity grows, having it as its own module will promote the root module’s simplicity. Keeping it stupid simple without compromising functionality constitutes good design for modules.

import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AComponent } from '../../components/a/a.component'; import { BComponent } from '../../components/b/b.component'; // an array of soon-to-be routes! const routes: Routes = []; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule { }

.forRoot(...) is a class function available from the RouterModule class. The function accepts an array of Route objects as Routes. .forRoot(...) configures routes for eager-loading while its alternative .forChild(...) configures for lazy-loading.

Eager-loading meaning the routes load their content into the application from the get-go. Lazy-loading happens on-demand. The focus of this article is eager-loading. It is the default approach for loading in an application. The RouterModule class definition looks something like the next block of code.

@NgModule({ // … lots of metadata ... }) export class RouterModule { forRoot(routes: Routes) { // … configuration for eagerly loaded routes … } forChild(routes: Routes) { // … configuration for lazily loaded routes … } }

Do not worry about the configuration details the example omits with comments. Having a general understanding will do for now.

Notice how AppRoutingModule imports the RouterModule while also exporting it. This makes sense given AppRoutingModule is a feature module. It imports into the root module as a feature module. It exposes RouterModule directives, interfaces, and services to the root component tree.

This explains why AppRoutingModule must export RouterModule. It does so for the sake of the root module’s underlying component tree. It needs access to those routing utilities!

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { AComponent } from './components/a/a.component'; import { BComponent } from './components/b/b.component'; import { AppRoutingModule } from './modules/app-routing/app-routing.module'; @NgModule({ declarations: [ AppComponent, AComponent, BComponent ], imports: [ AppRoutingModule, // routing feature module BrowserModule ], providers: [], bootstrap: [ AppComponent ] }) export class AppModule { }

The AppRoutingModule token imports from the very top. Its token inserts into the root module’s imports array. The root component tree may now utilize the RouterModule library. That includes its directives, interfaces, and services as already mentioned. Big thanks goes to AppRoutingModule for exporting RouterModule!

A RouterModule segédprogramok hasznosak lesznek a root összetevőihez. Az alap HTML AppComponent él egy irányelv: router-outlet.


    

    routerLinka RouterModule attribútumirányelve. Minden elemhez kapcsolódik

      az útvonalak beállítása után. router-outletérdekes viselkedésű komponens irányelv. Inkább jelzőként működik az átirányított tartalom megjelenítéséhez. Az átirányított tartalom a navigációtól az adott útvonalig terjed. Ez általában egyetlen összetevőt jelent az AppRoutingModule alkalmazásban konfigurálva

      Az átirányított tartalom azonnal megjelenik . Semmi sem teszi benne. Ez nem jelent túl nagy különbséget. Ennek ellenére ne számítson arra, router-outlethogy úgy viselkedik, mint egy továbbított tartalom tárolója. Ez csak egy jelölő az átirányított tartalom hozzáadásához a dokumentumobjektum-modellhez (DOM).

      The first question to address is what routes will this application consume? Well, there are two components: AComponent and BComponent. Each one should have its own route. They can render from AppComponent’s router-outlet depending on the current route location.

      The route location (or path) defines what appends to a website's origin (e.g. //localhost:4200) through a series of slashes (/).

      // … same imports from before … const routes: Routes = [ { path: 'A', component: AComponent }, { path: 'B', component: BComponent } ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule { }

      //localhost:4200/A renders AComponent from AppComponent’s router-outlet. //localhost:4200/B renders BComponent. You need a way to route to these locations without using the address bar though. An application should not rely upon a web browser's address bar for navigation.

      The global CSS (Cascading Style-sheets) supplements the HTML below it. An application’s router link ought to have a pleasant appearance. This CSS applies to all other examples too.

      /* global styles.css */ ul li { cursor: pointer; display: inline-block; padding: 20px; margin: 5px; background-color: whitesmoke; border-radius: 5px; border: 1px solid black; } ul li:hover { background-color: lightgrey; }
      
          
      • Go to A!
      • Go to B!

      This is basic routing! Clicking either of the routerLink elments routes the web address. It reassigns it without refreshing the web browser. Angular’s Router maps the routed address to the Routes configured in AppRoutingModule. It matches the address to the path property of a single Route object within the array. First match always wins, so match-all routes should lie at the very end of the Routes array.

      Match-all routes prevent the application from crashing if it cannot match the current route. This can happen from the address bar where the user may type in any route. For this, Angular provides a wildcard path value ** that accepts all routes. This route usually renders a PageNotFoundComponent component displaying “Error 404: Page not found”.

      // … PageNotFoundComponent imported along with everything else … const routes: Routes = [ { path: 'A', component: AComponent }, { path: 'B', component: BComponent }, { path: '', redirectTo: 'A', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ];

      The Route object containing redirectTo keeps the PageNotFoundComponent from rendering as a result of //localhost:4200. This is the applications home route. To fix this, redirectTo reroutes the home route to //localhost:4200/A. //localhost:4200/A indirectly becomes the application’s new home route.

      The pathMatch: 'full' tells the Route object to match against the home route (//localhost:4200). It matches the empty path.

      These two new Route objects go at the end of the array since first match wins. The last array element (path: '**') always matches, so it goes last.

      There is one last thing worth addressing before moving on. How does the user know where he or she is in the application relative to the current route? Sure there may be content specific to the route, but how is user supposed to make that connection? There should be some form of highlighting applied to the routerLinks. That way, the user will know which route is active for the given web page.

      This is an easy fix. When you click a routerLink element, Angular’s Router assigns focus to it. This focus can trigger certain styles which provide useful feedback to the user. The routerLinkActive directive can track this focus for the developer.

      
          
      • Go to A!
      • Go to B!

      The right assignment of routerLinkActive represents a string of classes. This example portrays only one class (.active), but any number of space-delimited classes may apply. When the Router assigns focus to a routerLink, the space-delimited classes apply to the host element. When the focus shifts away, the classes get removed automatically.

      /* global styles.css */ .active { background-color: lightgrey !important; }

      Users can now easily recognize how the current route and the page content coincide. lightgreyhighlighting applies to the routerLink matching the current route. !important ensures the highlighting overrides inline stylings.