Angular 4 Learning Notes (3): router

Keywords: angular Android REST Attribute

Router, or routing, is an important concept in the front end. In order to achieve decoupling, specific addresses and corresponding pages are separated by router. Create a new detail folder in the src/app directory and create a file named gundam-detail.component.
import { Component } from '@angular/core';
import { Gundam } from '../../model/gundam';
@Component({
    template: `
        <div *ngIf="selectedGundam">
        <span>{{selectedGundam.name}}</span>
        <span>{{selectedGundam.type}}</span>
        </div>
    `
})
export class GundamDetailComponent {
        selectedGundam: Gundam;
}

ps: Naming is basically xxx+"-"+"Business Type"+"Component Type". At least this is recommended in official documents. Of course, it's OK to name a component Pig Head Three, but standard naming can increase the readability of the component. Even if you don't mind the maintainers who come after the random fame pit, you can't be sure that you won't refactor the same piece of code for a long time. Therefore, we should be kind to others. If you don't write notes, it's okay. It's better to name it in a standard way.

ps2: Regarding the way of subcontracting, some people like to put views together, controllers together, and then subdivide them further according to logic; others turn around and divide views and controllers into logic first. This seems to have no unified conclusion, I personally like the latter, so this project adopts the latter division.

There's nothing in the file at present, just a simple move of temple from app.component.ts.

First identify the requirements, then start writing router s.

Requirement: Click on any item in the gundam list page to jump to the gundam details page.

As an angular component, if you want to use router on a page, you must first declare it in app.module.ts.

ps: The previous business has nothing to do with app.module.ts, but that doesn't mean it's not important. app.module.ts is equivalent to android mainifist file, which manages the whole project as a whole.

Open app.module.ts:

  1. imports: Basic classes are used in component pages.
  2. Declarations: Existing custom component declarations.
  3. bootstrap: It can be understood as Android's main launch, from which component the project enters at startup.

Before using router, we need to introduce:

import { RouterModule }   from '@angular/router';

Because RouterModule's forRoot method is called, RouterModule.forRoot is the basic class used in the project, so it needs to be written in imports.

  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot()
  ],

RouterModule.forRoot takes two parameters, the first is the route array to indicate jumps, and the second parameter is ignored all the year round. I don't know what's the use.

The route class consists of two key attributes: path and component. By accessing the path, you can find the unique component.

Add an array of route s containing two component s, the home page and the details page, to forRoot.

RouterModule.forRoot([
    {
        path: '',
        component: AppComponent
    },
    {
        path: '',
        component: GundamDetailComponent
    }
])

app.module.ts now looks like this:

import {
NgModule
} from '@angular/core';
import {
BrowserModule
} from '@angular/platform-browser';
import {
FormsModule
} from '@angular/forms';
import { RouterModule }   from '@angular/router';
import {
AppComponent
} from './component/appcomponent/app.component';
import { GundamDetailComponent } from './component/detail/gundam-detail.component';
@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        RouterModule.forRoot([
            {
                path: '',
                component: AppComponent
            },
            {
                path: '',
                component: GundamDetailComponent
            }
            ])
    ],
    declarations: [
        AppComponent,
        GundamDetailComponent
    ],
    bootstrap: [AppComponent],
})
export class AppModule {}

Both path s are still empty, because there is still one key missing item, even if it is written, it will make an error:

Error: Cannot find primary outlet to load 'AppComponent'

In angular, router is used with the label router-outlet. In other words, router decides which component to display and router-outlet decides where to display.

Label the template in app.component.ts

<router-outlet></router-outlet>

Then two homepages are displayed unexpectedly:

app.component.ts is a component as well as a page. angular first enters the app.component.ts rendering interface from bootstrap (that is, the upper part of router-outlet). Encountered and looked for router, found that the corresponding router also has components, and then loaded again.

Therefore, in order to display normally, the home page should also be extracted separately. All components are loaded through app.component.ts. As the outermost container of the whole demo, app.component.ts can perform some common operations (typical: backward action).

New host package and gundam-host.component.ts file under src.
Basically, you can move the entire app around, delete the out tag, and delete the selector.

import {
Component
} from '@angular/core';
import { Gundam } from '../../model/gundam';
import { GUNDAMS } from './../../service/data';
@Component({
    template: `
        <div *ngFor="let gundam of gundams" (click)="onSelected(gundam)">
            <span>
                {{gundam.name}}
            </span>
        </div>
    `
})
export class GundamHostComponent {
    gundam: Gundam = {
        name: 'Manatee',
        type: 'NewType'
    };
    gundams = GUNDAMS;
    selectedGundam: Gundam; // Define a selectedGudam as a variable to show the details
    onSelected (gundam: Gundam): void {
        this.selectedGundam = gundam; // Assignment by parameters
    }
}

app.component.ts only retains labels, and the rest is removed.

Modify the app.module.ts file, import gundam-host.component.ts and add GundamHost Component to component declarations.

Modify the component pointed to by path in route and display the home page component by default after entering:

before

after

The value of path (empty string) does not require additional subpaths.

Modify the path of the details page:

{
    path: 'detail',
    component: GundamDetailComponent
}

Add jump links to the home page:

Click Jump (Path Changed)

Now click on the list item on the home page to jump to a blank detail page. The reason for this blankness is that the value of the detail page needs to be passed from the home page. Now after the home page details page is divided, it needs to be routed for value transfer.

There are many ways to pass values, and there are even many kinds of values.
At present, I first use the most clumsy method: transform the gundam class into a string, pass the string to the details page, and then into the gundam class.

Add a function to the class of the app.component.ts file:

parseGundamToString(gundam: Gundam): string {
    return gundam.name + '&' + gundam.type;
} // Converting the gundam class to a fixed format string

Modify the template of the app.component.ts file to convert the transformed gundam string when accessing the gundam path

<div *ngFor="let gundam of gundams" routerLink="/detail/name=parseGundamToString(gundam)">
    <span>
    {{gundam.name}}
    </span>
</div>

Modify the path of the details page

{
    path: 'detail/:gundam',
    component: GundamDetailComponent
}

/ Gundam is a placeholder and a parameter description. The parameter attribute passed in is gundam.

In the detail file, you can get the passed string from the url connection.

The time to get this string should be when the detail page is initialized. Angular provides the so-called hook, which is used to indicate the activity cycle of a component -- in fact, a method similar to onStart or onCreate in Android.

Add OnInit hooks, or interfaces, to gundam-detail.component.ts:

import { Component, OnInit } from '@angular/core';

implements keywords and OnInit are added after class to implement the interface:

export class GundamDetailComponent implements OnInit {
    selectedGundam: Gundam ;
    ngOnInit(): void {
    }
}

The rest is to read the parameters from the connection.

To read the parameters passed on the connection, we still need to use several classes in router, so we need to import them in detail.

import { ActivatedRoute, Params }   from '@angular/router';

When the import is complete, the call is made by injection into the constructor:

(I haven't talked about injection yet.)

constructor(
private route: ActivatedRoute){}

angular automatically creates instances of Activated Route.

First output in ngOnInit to see what params are

this.route.params.switchMap((params: Params) => console.log(params))

ps: switchMap is the official method of getting url parameters from angular. It also needs to be imported beforehand to use:

import 'rxjs/add/operator/switchMap';

ps2: Arrow function

(params: Params) => this.gundamStr = params['gundam']

It's an arrow function, equivalent to

function(params){
    this.gundamStr = params['gundam']
}

params is the return value of switchMap, which returns the class where the parameters are passed through the routing connection.

ps3: The arrow function is really one of the most disgusting things in ES6.

Output in console:

The parameter passed in is a string formatted by a gundam class, so we need to add a function to the gundam class in detail.

parseStringToGundam(str: string): Gundam {
    const temp = str.split('&');
    const tempGundam: Gundam = {
    name: temp[0],
    type: temp[1]
    };
    return tempGundam;
}

Ultimately, the initialization of getting detail looks like this

ngOnInit(): void {
    this.route.params // Get it by injection route Parameters in params
    .switchMap((params: Params) => this.gundamStr = params['gundam']) // Get it by parametersgundamString and PaymentdetailA temporary variable in
    .subscribe(() => this.selectedGundam = this.parseStringToGundam(this.gundamStr)); // Resolve the temporary variable through the inverse formatting function and return it to the displaymodel
}

There's really no good way to pass values between mobile web pages, either angular or react. Previously, our approach was to pass short parameters directly through the connection. Long, large or object parameters were saved locally first, and then the second page was read locally.
But there's really no way to throw an intent like android that goes straight past.

Back home page:

Click on a list:

Packet structure:

Generally speaking, the business is separated and the structure is much cleaner. Although it can't be reflected now, I feel very happy when I write about it. I can't Miss cutting firewood when I sharpen my knife.

As router s, they can also be separated.

At present, there are only 2 pages in my project. If there are more than 20 pages, for example, app.module.ts will become messy again.

So throw router out, too.

Create a new file app-routing.module.ts, and then translate the footRoot (with a reference).

In the app-routing.module.ts file, ngModul is also required. Personal understanding of ngModul is equivalent to a class indicator, which is exported for reference by other classes.

import {
NgModule
} from '@angular/core';
import { RouterModule }   from '@angular/router';
import { GundamDetailComponent } from './component/detail/gundam-detail.component';
import { GundamHostComponent } from './component/host/gundam-host.component';
@NgModule({
    imports: [
        RouterModule.forRoot([
            {
                path: '',
                component: GundamHostComponent
            },
            {
                path: 'detail/:id',
                component: GundamDetailComponent
            }
        ])
    ],
    exports: [RouterModule]
})
export class AppRoutingModule {
}

Now that you have this class, you can import it into app.module.ts and use it to make the whole file look cleaner.

import {
NgModule
} from '@angular/core';
import {
BrowserModule
} from '@angular/platform-browser';
import {
FormsModule
} from '@angular/forms';
import {
AppComponent
} from './component/appcomponent/app.component';
import { GundamDetailComponent } from './component/detail/gundam-detail.component';
import { GundamHostComponent } from './component/host/gundam-host.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        AppRoutingModule // Call Routing
    ],
    declarations: [
        AppComponent,
        GundamDetailComponent,
        GundamHostComponent
    ],
    bootstrap: [AppComponent],
})
export class AppModule {}

Of course, the official documents have been further simplified.
Since forRoot is a Route array, the array can also be extracted separately, and further extraction can also be placed in another file.

import {
NgModule
} from '@angular/core';
import { RouterModule, Route }   from '@angular/router';
import { GundamDetailComponent } from './component/detail/gundam-detail.component';
import { GundamHostComponent } from './component/host/gundam-host.component';
const routes: Route[] = [
    {
        path: '',
        component: GundamHostComponent
    },
    {
        path: 'detail/:gundam',
        component: GundamDetailComponent
    }
];
@NgModule({
    imports: [
        RouterModule.forRoot(routes)
    ],
    exports: [RouterModule]
})
export class AppRoutingModule {
}

Personally, I'm lazy, so I'll take this step first.

Now even the main page and the details page are separated, and the coupling degree of the project is further reduced.

We continue to separate business logic from business logic.

Posted by tidalik on Mon, 08 Jul 2019 11:57:01 -0700