See the Angular Basic Tutorial for the basic tutorial

Angular routing introduction

Color {#0abb3c}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component}{component} Router color{#0abb3c}{Router}Router is used in Angular.

Scenarios where route hops are required:

If there are two jump links on the page, we want to click these links to go to the product list page and the personal center page respectively. We can start by creating two components: GoodsListComponent and PersonalCenterComponent. The specific process is as follows:

Routing to create

  1. Create a new file app-routing.module.ts and place the view configuration to jump to
import { NgModule } from '@angular/core';
// Import routing modules RouterModule and Routes
import { Routes, RouterModule } from '@angular/router'; 
// Introduce components that need to be displayed at different urls
import { GoodsListComponent } from './goods-list/goods-list.component';
import { PersonalCenterComponent } from './personal-center/personal-center.component';

// Configure the route array
const routes: Routes = [
  {
    path: 'goodsList'.// Define the route name
    component: GoodsListComponent, // Specify which component to display
  },
  {
    path: 'personalCenter'.// Define the route name
    component: PersonalCenterComponent, // Specify which component to display}]; @NgModule({The // forRoot() method creates an NgModule containing all directives, the given route, and the Router service itself.
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}
Copy the code

Define your Routes in the Routes array (each route in this array is a JavaScript object with two attributes). The first attribute, PATH, defines the URL path for the route. The second attribute, Component, defines the component to display in the corresponding path.)

  1. Import the AppRoutingModule in app.module.ts
import { AppRoutingModule } from './app-routing.module';
Copy the code
  1. Add these routes in app.component.html to control the presentation of navigation
 <div class="route-change-container">
  <a routerLink="/goodsList">Switch to the item list component</a> <! RouterLink connects the routes you define to the template file.
  <a routerLink="/personalCenter">Switch to the personal center component</a>
</div>

<router-outlet></router-outlet>  <! -- This directive loads components dynamically by route -->
Copy the code
  • First, add two links to the HTML. Assign the link to which the route is to be added to the routerLink property. To display the component when the user clicks on individual links.
  • Next, add in the template <router-outlet>The label. This element notifies Angular that you can update the app view with the routed component selected. Used like a component, as a placeholder to indicate where the router should display the component.

An introduction to two important apis in routing

ActivatedRoute

Color {#0abb3c}{get routing information} Get routing information, it contains routing parameters, static data… For details, please refer to the ActivatedRoute official website

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; // Introduce the ActivatedRoute

@Component({
  selector: 'app-goods-list'.templateUrl: './goods-list.component.html'.styleUrls: ['./goods-list.component.scss']})export class GoodsListComponent implements OnInit {

  constructor(
    private route: ActivatedRoute, // dependencies are injected into this service
  ) {}

 // Remove the routing information from the URL during the initial lifecycle
  public ngOnInit(): void {
    // The first way to get parameters
    const params = this.route.snapshot.params;
    
    // The second way to get parameters
    this.route.params.subscribe(params= > {
      console.log(params); }); }}Copy the code

The difference and use of the above two methods to obtain parameters: route.snapshot:

  • You need to directly access parameters, mainly obtainThe initial valueTo use snapshot without subscribing.

route.params.subscribe():

  • Params is used when parameters are retrieved for each change or when multiple consecutive navigations are required;

Router

Color {#0abb3c}{#0abb3c}{#0abb3c}{#0abb3c}{#0abb3c}{#0abb3c}{# 0abB3c}{# 0abB3c}{# 0abB3c}

// The Router service class needs to be introduced into the component before it is used
import { Router } from '@angular/router';
Copy the code
  1. Navigate () This method supports the same type of parameters as the routerLink directive, so their roles are the same;
  2. NavigateByUrl () absolute route;
  3. The events method is called before each navigation; (For now)

Route parameters were transmitted and obtained

Two forms of route parameter transmission

1. Use scenario of Params (yes /: ID dynamic routing) : For example, when we click the link of commodity list, we hope to transfer the user information or commodity type information to the component of commodity list through THE URL.

// Routes need to be configured
const routes: Routes = [
 {
   path: 'goodsList/:id'.// Define the route name
   component: GoodsListComponent, // Specify which component to display},];Copy the code

2. queryParams (Yes? Id =xx format) Usage scenario: When we want to pass multiple parameters through the URL, we can choose to pass parameters in this way

Dynamic routing is preferred for 1 parameter, and query is more convenient for more than 2 parameters

Three specific methods for transmitting parameters in a route

1. routerLink

Single parameter:

 <a [routerLink]="['/goodsList', id]" routerLinkActive="active-class"> Switch to the item list component </a>// where /goodsListl is the route path I set and id is the parameter to pass
 // We can also use this form for multiple parameters, but it seems less intuitive, so we do not recommend using this method for multiple parameters
Copy the code

Multiple parameters:

 <a [routerLink]="['/personalCenter']" [queryParams]="{name: 'zs', age: 16}"> Switch to personal center component </a>// The format of the parameters can be self-organized into various objects
Copy the code

RouterLinkActive Tracks whether the link routes on the element are active. And allows you to specify one or more CSS classes to add the element when the link route is active.

2. router.navigate

Navigation is based on the supplied command array and starting route. If no starting route is specified, the absolute navigation starts from the root route

Single parameter:

public goToGoodsListPage(): void {
    // The first way
    this._router.navigate([`/goodsList/The ${this.id}`]);
    // The second method achieves the same effect as the first method
    this._router.navigate(['/goodsList'.this.id]);
  }
  
Calling this method in HTML is equivalent to using the routerLink attribute of the A tag
<button (click)="goToGoodsListPage()"> Switch to the item list component </button>Copy the code

Multiple parameters:

  public goToPersonalCenterPage(): void {
    this._router.navigate(['/personalCenter'] and {queryParams: {name: 'zs'.age: 16}});
  }
  
  // call in HTML
   <button (click)="goToPersonalCenterPage()"> Switch to the personal center component </button>Copy the code

3. router.navigateByUrl

Absolute paths must be used for navigation based on the URLS provided. Be consistent with router.navigate for single and multiple parameters.

// The first parameter passed is the form of the array, and the first parameter of navigate is the form of the array
// His pass parameter is currently concatenated above the URL
  public goToPersonalCenterPage(): void {
    this._router.navigateByUrl(`/personalCenter? name=zs&age=16`);
  }
Copy the code

Two ways to receive parameters in a route

1. Accept parameters of type Params

import { ActivatedRoute } from '@angular/router';
constructor(
    private _route: ActivatedRoute,
  ) {}
  
public ngOnInit(): void {
    // The first way is to get only the initial value
    const param = this.route.snapshot.params;
    // The second dynamic fetch method
    this.route.params.subscribe(params= > {
      console.log(params);
    });
  }

Copy the code

2. Accept parameters of type queryParams

import { ActivatedRoute } from '@angular/router';
constructor(
    private _route: ActivatedRoute,
  ) {}
  
public ngOnInit(): void {
    // The first way is to get only the initial value
    const queryParam = this.route.snapshot.queryParams;
    // The second dynamic fetch method
    this.route.queryParams.subscribe(params= > {
      console.log(params);
    });
  }

Copy the code

Route redirection

Application scenario: When users are expected to redirect to a page by default in an application, route redirection is required. Redirection components:

  • Use the path to redirect the source
  • Redirect the target component
  • The pathMatch value is used to configure the route
{ path: ' '.redirectTo: '/personalCenter'.pathMatch: 'full' }, // Redirect to 'personalCenter'
Copy the code

PathMatch color{#0abb3c}{pathMatch}pathMatch is a string used to specify a routing matching policy. The options are prefix (the default) and full.

Prefix: starts with the specified path (that is, the value in path is the same as the starting path entered by the user. For example, if path is “ABC”, / ABC /a is ok, but /abcd is not ok); If path is empty, all routes will be matched to the page

/ ABC /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d /abcd/ABC /d If path is empty, the page will be matched only if localhost:4200 is followed by nothing.

Routing order

The Router at the time of matching routing, using a “first come first serve \ color abb3c # {0} {to} first come first served” strategy, so it shall be a specific static routing \ color abb3c # {0} {specific static routing} put in front of specific static routing, Then you can place the default route matching \color{#0abb3c}{default route matching} the default route matching empty path route, wildcard \color{#0abb3c}{wildcard} the wildcard route is the last one (it can match all routes, when no other routes match, The Router will select it.

const routes: Routes = [
  // Static route
  {
    path: 'goodsList/:id'.component: GoodsListComponent,
  },
  {
    path: 'personalCenter'.component: PersonalCenterComponent,
  },
  // Default empty path routing
  {
    path: ' '.redirectTo: '/personalCenter'.pathMatch: 'full'
  },
  // Wildcard route
  { path: '* *'.component: PageNotFoundComponent },
];
Copy the code

Nested routing

As the application becomes more complex, it may be necessary to create relative routes outside the root component, which become child routes, This means that a second

color{#0abb3c}{

}

needs to be added to the project, because it is another one in addition to the AppComponent < the router – outlet > \ color abb3c # {0} {< the router – outlet >} < the router – outlet >.
−outlet>

−outlet>

Color {#0abb3c}{personal details} Personal details page \color{#0abb3c}{personal details page} Personal details page \color{#0abb3c}{personal details page} Personal details page

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { GoodsListComponent } from './goods-list/goods-list.component';
import { PersonalCenterComponent } from './personal-center/personal-center.component';
import { PersonalSettingComponent } from './personal-setting/personal-setting.component';
import { PersonalDetailComponent } from './personal-detail/personal-detail.component';

const routes: Routes = [
  {
    path: 'goodsList/:id'.component: GoodsListComponent,
  },
  {
    path: 'personalCenter'.component: PersonalCenterComponent,
     children: [  // Add a children array to store the children route
      {
        path: 'personalDetail'.component: PersonalDetailComponent,
      },
      {
        path: 'personalSetting'.component: PersonalSettingComponent,
      },
     ]
  },
  {
    path: ' '.redirectTo: '/personalCenter'.pathMatch: 'full'},];// Add a 
      
        to the HTML of the personal center to specify where the child pages should be displayed
      
<div class="personal-center-container">This is the personal center page<a routerLink="./personalDetail">Switch to the personal details page</a>
  <a routerLink="./personalSetting">Switch to the profile page</a>
</div>
<router-outlet></router-outlet>
Copy the code

Child routes, like other routes, require both path and Component. The only difference is that you put the child route in the children array of the parent route.

Route lazy loading

You can configure the route definition to lazy-load modules, which means Angular loads modules only when they are needed, not when the app starts.

  1. Add a module file to the previous two page components and configure loadChildren in Routes instead of Component
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
  {
    path: 'goodsList/:id'.loadChildren: () = > import('./goods-list/goods-list.module')
                      .then(m= > m.GoodListModule)
  },
  {
    path: 'personalCenter'.loadChildren: () = > import('./personal-center/personal-center.module')
                      .then(m= > m.PersonalCenterModule)
  },
  {
    path: ' '.redirectTo: '/personalCenter'.pathMatch: 'full'},]; @NgModule({The // forRoot() method creates an NgModule containing all directives, the given route, and the Router service itself.
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Copy the code
  1. Add a route module file to the previous two page components and add a route to that component.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { GoodsListComponent } from './goods-list.component';

const routes: Routes = [
  {
    path: ' '.component: GoodsListComponent
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class GoodsListRoutingModule {}Copy the code

Route guard (only understood)

Route guard is used to prevent unauthorized navigation to certain parts of the application. For more information, visit the route Guard website

  • Use CanActivate to handle navigation to a route. // Enter the route
  • Use CanActivateChild to handle navigation to a subroute.
  • Use CanDeactivate to handle exiting from the current route. // Exiting from the route
  • Use Resolve to retrieve route data before route activation.
  • Use CanLoad to handle asynchronous navigation to a feature module. A CanActivate will still load a module. A canload will not be loaded if the condition is not met
export class TestGuard implements CanActivate {
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean {
      // Determine whether the logic of a route can be entered}}Copy the code
{
  path: 'test-path'.component: TestComponent,
  canActivate: [TestGuard], // Use route guard guard
}
Copy the code

Routing events (only understood)

The Router emits navigation events during each navigation through the router. events property. The scope of these events spans multiple points in time between the beginning and end of the navigation. For more information, please visit the routing event website for more details

Introduction to forms in Angular

The difference between reactive and template-driven forms

responsive Template driven
Building a form model Displayed, created in the component class Implicit, with instructions created
The data model Structured and immutable (observable) Unstructured and mutable (data bidirectional binding)
The data flow synchronous asynchronous
Form validation function instruction

Common base classes for common forms

Both reactive and template-driven forms are built on the following base classes.

  • The FormControl instance is used to track the value and validation status of a single FormControl.

  • FormGroup is used to track the value and status of a form control group.

  • FormArray is used to track the value and state of an array of form controls. (understand)

Template-driven forms

Depend on the \color{#0abb3c}{depend on the \color{#0abb3c}{directive} instructions in the template to create and manipulate the underlying object model. It is mainly used to add simple forms, which are simpler to use, but not as extensible as reactive forms. When the control value is updated, the associated property value is changed to the new value.

Create a form

<! We can use template reference variables anywhere in the current template. -->
<! These variables provide the ability to access elements directly from the module.

<! The name must be added because all components and controls in the form should have names -->
<form (ngSubmit) ="onSubmit(myform.value);" #myform="ngForm" novalidate>
  <label for="name">Name:</label>
  <input type="text" name="name" [(ngModel)] ="name">
  <label for="password">Password:</label>
  <input type="password" name="password" [(ngModel)] ="password">
  <button>submit</button>
</form>
Copy the code

Validate input in template-driven forms

<! If we want to verify the above form, the password must not be empty and the name must be at least 2 characters long.
<form (ngSubmit) ="onSubmit(myform);" #myform="ngForm">
  <label for="name">Name:</label>
  <input type="text" name="name" [(ngModel)] ="name" minlength="2">
  <label for="password">Password:</label>
  <input type="password" name="password" [(ngModel)] ="password" required>
  <button>submit</button>
  
  <! This is done through the hasError method.
  <div *ngIf="myform.form.hasError('minlength', 'name')">The length of the name cannot be less than 2 characters</div>
  <div *ngIf="myform.form.hasError('required', 'password')">The password cannot be empty</div>
</form>

Copy the code

Reactive forms (emphasis)

Provides direct, explicit access to the underlying form object model \color{# 0ABb3c}{underlying form object model}. They are more robust than template-driven forms, and they are more extensible, reusable, and testable. And control updates are more efficient because instances of FormControl always return a new value and do not update the existing data model. If forms are a critical part of your application or are highly extensible, use reactive forms

Use reactive forms

  1. Introduce reactive form modules in the module file
import { ReactiveFormsModule } from '@angular/forms';
Copy the code
  1. Introduce a form group in the component
import { FormGroup, FormControl } from '@angular/forms';
Copy the code
  1. Create a FormGroup instance and associate the FormGroup model with the view
 this.profileForm = new FormGroup({
      name: new FormControl(' '),
      password: new FormControl(' ')});Copy the code
<form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)">
    <label for="name">First Name: </label>
    <input id="name" type="text" formControlName="name">
    
    <label for="password">Last Name: </label>
    <input id="password" type="text" formControlName="password">
    <button>submit</button>
</form>
Copy the code

Form a nested

A form group can accept a single form control instance color{# 0ABb3c}{single form control instance} a single form control instance and other form group instances as child controls color{# 0ABB3c}{child controls}. This makes complex form models easier to maintain and logically group them together.

  1. Create a nested form group.
this.profileForm = new FormGroup({
      name: new FormControl(' ', [Validators.required]),
      password: new FormControl(' '),
      // Nested a form group for address in the form
      address: new FormGroup({
        street: new FormControl(' '),
        city: new FormControl(' '),})});Copy the code
  1. Group the nested form in the template.
<form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)">
    <label for="name">First Name: </label><input id="name" type="text" formControlName="name"> <label for="password">Last Name: </label> <input ID ="password" type="text" formControlName="password"> // Use formGroupName to declare a new form group <div formGroupName="address"> <h3>Address</h3> <label for="street">Street: </label> <input id="street" type="text" formControlName="street"> <label for="city">City: </label> <input ID ="city" type="text" formControlName="city"> </div> <button> </button> </form>Copy the code

Form validation

Control state field

Before we look at form validation, let’s take a look at some of the status fields that Angular provides for forms. These fields help users decide at which stages to perform form validation.

  • Touched and untouched

Used to determine whether the user has focus, touched=true if the user has focus. Amanda =false, untouched if it never exists, touched=false; Untouched = true.

  • Pristine and dirty

Dirty indicates that the user manipulates form elements; pristine indicates that the user does not manipulate form elements.

  • Valid and invalid

If a form element has multiple validation conditions valid is true only if all of the validation conditions are valid. If one of the validation conditions is not valid, invalid is true.

Control state CSS class

Angular automatically maps many control properties as CSS classes color{#0abb3c}{CSS class} to the control element color{#0abb3c}{element}. You can use these classes to style the form control element color{#0abb3c}{control element} according to the form state color{#0abb3c}{state}.

  • .ng-valid
  • .ng-invalid
  • .ng-pending
  • .ng-pristine
  • .ng-dirty
  • .ng-untouched
  • .ng-touched
Scenario: For example, we would like the border of the control to be red if it does not meet the validation criteria. Ng-touched:not(form){
  &.ng-invalid:not(form){   // This is an example of SCSS
    border: 1px solid red
  }
}
Copy the code
Steps for form validation
  1. Import a validator function into the form component. Angular provides built-in validators in the Form Validation Organ network
import { Validators } from '@angular/forms';
// Angular provides built-in validators: min Max Required Email minLength maxLength Pattern...
Copy the code
  1. Add the validator to the corresponding field in the form.

Scenario: Verify the following items: (1) The name cannot be empty. ② The password cannot be empty, and the minimum length cannot be less than 4; ③ The street in the address should not be empty

this.profileForm = new FormGroup({
        name: new FormControl(' ', [Validators.required]),
        password: new FormControl(' ', [Validators.required, Validators.minLength(4)),address: new FormGroup({
          street: new FormControl(' ', [Validators.required]),
          city: new FormControl(' '),})});Copy the code
  1. Error notification logic in templates
<form [formGroup] ="profileForm" (ngSubmit) ="submitForm(profileForm.value)">
<! -- name -- -- >
    <label for="name">Name:</label>
    <input id="name" type="text" formControlName="name">
     <div [hidden] ="profileForm? .get('name').valid || profileForm? .get('name')? .untouched">
        <p [hidden] =! "" profileForm.hasError('required', 'name')">Name is mandatory</p>
    </div>
    
<! - password - >
    <label for="password">Password:</label>
    <input id="password" type="text" formControlName="password">
     <! Do not display error messages when the password is valid or the user has never done much.
     <! -- Then determine the specific error notification based on hasError -->
    <div [hidden] ="profileForm? .get('password').valid || profileForm? .get('password')? .untouched">
      <p [hidden] =! "" profileForm.hasError('minlength', 'password')">The password must contain at least four digits</p>
      <p [hidden] =! "" profileForm.hasError('required', 'password')">The password cannot be empty</p>
    </div>

<! -- Address form group -->
    <div formGroupName="address">
        <h3>Address</h3>
        <label for="street">Street: </label>
        <input id="street" type="text" formControlName="street">
        <div [hidden] ="profileForm? .get('address')? .get('street')? .valid || profileForm? .get('address')? .get('street')? .untouched">
            <div [hidden] =! "" profileForm.hasError('required', ['address', 'street'])">Streets can't be empty</div>
        </div>
        <label for="city">City: </label>
        <input id="city" type="text" formControlName="city">
    </div>
    <button>submit</button>
</form>
Copy the code
Custom form validators

Scenario: When filling in the mobile phone number, the user wants to verify that the format of the mobile phone number is correct and can use a custom function to do this

 public ngOnInit(): void {
  this.profileForm = new FormGroup({
    phone: new FormControl(' '[this.mobileValidator])
  });
  }
  
  public mobileValidator = (control: FormControl): any= > {
    const value = control.value;
    // The number is regular
    const mobileReg = / ^ 0? (13 15 [012356789] [0-9] | | 17 [013678] 18 [0-9] | | 14 [57]) [0-9] {8} $/;
    const result = mobileReg.test(value);
    // Null is returned for valid values, or invalid returns the validation error object, which is the validation key property named mobileFormat. Values can generally be any value
    // mobileFormat is used again to retrieve error messages in templates
    return result ? null : {mobileFormat: {info: 'Phone format is not correct'}};
  }
  
  // Use in templates
  <form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)">
    <input id="phone" type="text" formControlName="phone">
    <div>{{profileForm? .get('phone').errors | json}}</div>// You can print out error messages defined in custom functions<div [hidden] ="profileForm? .get('phone').valid || profileForm? .get('phone')? .untouched">
        <p [hidden] =! "" profileForm.hasError('mobileFormat', 'phone')">The phone format is incorrect</p>
    </div>
    <button>submit</button>
</form>


Copy the code

How do I update form data

  1. usesetValue()Method to set a new value for a single control. The setValue() method strictly follows the form group structure, andintegrityReplaces the value of the control.
  2. usepatchValue()Method can replace the form model with any property defined in the object.

Use the FormBuilder service to generate controls

The FormBuilder service provides some handy ways to generate form controls. FormBuilder creates and returns these instances in the same way behind the scenes, but it’s easier to use.

The service has three methods: Control (), group(), and array(). These methods are factory methods used to generate FormControl, FormGroup, and FormArray, respectively, in the component class.

1. Introduce the FormBuilder class into the component and inject the service

import { FormBuilder } from '@angular/forms';
constructor(private fb: FormBuilder){}Copy the code

2. Generate form content

this.profileForm = this.fb.group({
    name: [' '].password: [' ']});/ / is equivalent to
  
this.profileForm = new FormGroup({
  name: new FormControl(' '),
  password: new FormControl(' ')});Copy the code

Cross validation across fields (understand only)

const heroForm = new FormGroup({
  'name': new FormControl(),
  'alterEgo': new FormControl(),
  'power': new FormControl()
}, { validators: identityRevealedValidator });


export const identityRevealedValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null= > {
  const name = control.get('name');
  const alterEgo = control.get('alterEgo');

  return name && alterEgo && name.value === alterEgo.value ? { identityRevealed: true } : null;
};
Copy the code

See Angular’s advanced tutorial 2 – (Dependency Injection +RxJS) for the next chapter