Introduction to the

I started learning Angular2 + in 2017, and I’ve seen Angular get better and better along the way, and I’ve taken a lot of detours on my own. It’s recommended that you master other necessary knowledge while learning Angular. I wrote this article to share how I learned Angular and pave the way for those of you who just learned Angular. If you don’t understand what I wrote, please leave a message.

What are the presents

Angular is a development platform. It helps you build Web applications more easily. Angular is a combination of declarative templates, dependency injection, end-to-end tools, and best practices to solve your development challenges. Angular improves developers’ ability to build Web, mobile, or desktop applications. (Reference from official website)

This article mainly describes the problems I encounter in my daily work. The writing is rough, and I will write what I think of without a unified plan. If there is an opportunity to write an advanced version later.

What skills are needed?

  1. Typescript
  2. rxjs
  3. nodejs
  4. Javascript based

Angular CLI

The Angular CLI is a command-line interface tool that creates projects, adds files, and performs a host of development tasks, such as testing, packaging, and publishing. (Reference from official website)

  1. npm install -g @angular/cli    
  2. ng new my-app --routing --style less(routing with routing module, –style less style sheet less)
  3. Ng serve -p 4800 --aot --host 0.0.0.0 --disable-host-check true(-p enable port 4800 –disable-host-check true enable port check –aot aOT compilation)
  4. ng build --prod -nc(– ProD production mode – NC chunks named after module name)

Presents a

Run ng g c index, automatically create index in app file.

@Component({
    selector: 'app-index', / / selector encapsulation: ViewEncapsulation. None, / / the default Native private Emulated cannot access None does not handle styleUrls: ['./index.component.less'// Style sheet link styles: ['.primary {color: red}'[], viewProviders:[],// Dependency module providers:[],// dependency serviceexportAs:'appIndex'//templateUrl:'./index.component.html'// Link to template:"<div>{{msg}}</div>",
    host:{
        '[class.test]': 'true'.'[attr.id]': ' 'test' ',}})export class IndexComponent{
    constructor() {
    }

    @ViewChild('movieplayer') movieplayerRef ElementRef / / get the current dom instance. msg:string ='hello angular';
    clickTest() {} list: Array < any > = [1, 2, 3, 4, 5]; show:boolean =true;
    word:string
    width:number = 100;
    showTest:boolean = true;

    @Output() myEvent = new EventEmitter<any>();
    @Input() myProperty;
    _my2Property
    @Input() set my2Property(val){
        this._my2Property = val;
    };
    get my2Property() {returnthis._my2Property; }}Copy the code


<app-index [myProperty]="{a:1}" (myEvent)="$event.stopPropagation()" #indexRef="appIndex"></app-index>Copy the code


Angular template syntax

  1. []Attributes bind
  2. (a)event
  3. *ngForThe list of rendering
  4. *ngIfConditions apply colours to a drawing
  5. [(ngModel)]Two-way binding
  6. #xxxxA local variable
<div 
    [title]="msg" 
    (click)="clickTest($event)" 
    [style.width.px]="width"
    [ngStyle]="{'width.%':width}"
    [ngClass]="{'class-test':showTest}"
    [class.class-test]="showTest"
    *ngFor="let li of list; let i = index; trackBy: trackById" 
    *ngIf="show">
    <input type="text" [(ngModel)]="word">
    {{word}}
</div>

<video #movieplayer></video>
<button (click)="movieplayer.play()">

<ng-template ngFor [ngForOf]="list" let-li let-i="index" let-odd="odd" [ngForTrackBy]="trackById"></ng-template>

<ng-template [ngIf]="show"></ng-template>

<div [ngSwitch]="conditionExpression">
    <template [ngSwitchCase]="case1Exp"></template>
    <template ngSwitchCase="case2LiteralString"></template>
    <template ngSwitchDefault></template>
</div>Copy the code

Routing and Navigation

  • routing.module.ts
const routes: Routes = [
    {
         path:' ',
         component:IndexComponent,
         resolve:{resolve:ResolverService},
         canActivate:[AuthGuard],
         data:{},
    }
]

@NgModule({
    imports: [RouterModule.forRoot(routes,{useHash: true})],
    exports: [RouterModule]
})
export class IndexRoutingModule {
    
}Copy the code
  • resolver-service.ts
@Injectable()
export class CurrentResolverService implements Resolve<any> {
    constructor(private router: Router, private userService_: UserService) {
    }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any {
        //1.
        return Observable.create({})
        //2.
        return new Promise((resolve, reject) => {
            setTimeout(()=> {
                resolve({})
            },2000)
        })
        //3.
        return{}}}Copy the code
  • auth.guard.ts
@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {

    constructor() {

    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
        //1.
        return Observable.create(false) / / 2.return new Promise((resolve, reject) => {
            setTimeout(()=> {
                resolve(false)}, 2000)}) / / 3.return false} // canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {returnthis.canActivate(childRoute, state); } / / asynchronous loading component canLoad (route: the route) : observables < Boolean > | Promise < Boolean > | Boolean {returnnull; }}Copy the code
  • location
<router-outlet></router-outlet>
<router-outlet name="aux"></router-outlet>Copy the code
  • Routing hop
<a routerLink="/path" [routerLinkActive]="'active'">
<a [routerLink]="[ '/path', routeParam ]">
<a [routerLink]="[ '/path', { matrixParam: 'value' } ]">
<a [routerLink]="[ '/path' ]" [queryParams]="{ page: 1 }">
<a [routerLink]="[ '/path' ]" fragment="anchor">Copy the code

The life cycle

  • ngOnChangesThis life cycle is triggered when the component binding data changes
//OnChanges
ngOnChanges(changes: SimpleChanges): void {
    let value = changes.test && changes.test.currentValue
}Copy the code
  • ngOnInitAfter the constructor is called, all input attributes are initialized
//OnInit
ngOnInit(): void{
    
}Copy the code
  • ngDoCheckCalled whenever change detection is performed on an input property of a component or directive. You can use it to extend change detection logic and perform custom detection logic.
//DoCheck
ngDoCheck(): void {
    console.info('ok')}Copy the code
  • ngOnDestroyOnly called once before the instance is destroyed.
//OnDestroy
ngOnDestroy(): void {
    console.info('ok')}Copy the code

service

Instead of replicating the same code over and over again, we create a single reusable data service and inject it into the components that need it. Using separate services keeps components lean, allows them to focus on supporting views, and makes it easier to unit test components with Mock services. (quoted from official website)

@Injectable()
export class TestService {
    constructor() {
    }

    private data = {num:1};

    get num() {return this.data.num
    }

    add(){
        ++this.data.num
    }

    minus(){
        --this.data.num
    }
}Copy the code

How to use the service

  • If an item has singleton mode, inject metadata directly into app.module.ts
    providersIn the can
@NgModule({
    providers:[
        TestService
    ]
})Copy the code
  • The non-singleton pattern is simply introduced into the providers of its own component or module.
@Component({
    providers:[
        TestService
    ]
})Copy the code

Dependency injection

Dependency injection is an important programming pattern. Angular has its own dependency injection framework, and it’s almost impossible to build Angular applications without it. It is so widely used that almost everyone calls it DI for short. (quoted from official website)



Through constructor injection
TestServiceInstance and save it to the name
_testServicePrivate property of the

export class IndexComponent{
    constructor(private _testService:TestService) {
    }
}Copy the code
Alternative will

@Injectable()
export class Test2Service extend TestService{
    constructor() { super(); }} @Component({providers:[{provide: TestService, useClass: Test2Service}],// dependency service})export class IndexComponent{
    constructor(private _testService:TestService) {
    }
}Copy the code

The HTTP service

The new version follows the old one
HttpModuleModule changed to
HttpClientModule  。

It is recommended to introduce in app.module.ts
HttpClientModule 

@NgModule({
    declarations: [
        AppComponent,
    ],
    imports: [
        BrowserModule,
        CommonModule,
        FormsModule,
        HttpClientModule,
    ],
    providers: [
        
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
} Copy the code
This configuration can be used in any module or component
HttpClientService.

Here are some of the more common methods.

request(method: string, url: string, options: { body? : any; headers? : HttpHeaders | { [header: string]: string | string[]; }; observe? :'body'; params? : HttpParams | { [param: string]: string | string[]; }; reportProgress? : boolean; responseType:'any'; withCredentials? : boolean; }): Observable<any>; get(url: string, options: { headers? : HttpHeaders | { [header: string]: string | string[]; }; observe? :'body'; params? : HttpParams | { [param: string]: string | string[]; }; reportProgress? : boolean; responseType:'any'; withCredentials? : boolean; }): Observable<any>; post(url: string, body: any | null, options: { headers? : HttpHeaders | { [header: string]: string | string[]; }; observe? :'body'; params? : HttpParams | { [param: string]: string | string[]; }; reportProgress? : boolean; responseType:'any'; withCredentials? : boolean; }): Observable<any>;Copy the code
It’s not easy to do the same thing, and it’s not easy to maintain, so I’m going to use a class that encapsulates all interface calls.

// Define the parameter fields returned by the interface. Generally, the parameters returned by the interface are fixedexportinterface Result<T> { result? : any success? : any message? : any }Copy the code

export class ConfigService {
    static baseUrl    = 'http://127.0.0.1:8080';
    static uploadPath = 'http://127.0.0.1:8080';
    constructor(private _http: HttpClient) {

    }

    configForm() {
        return new HttpHeaders().set('Content-Type'.'application/x-www-form-urlencoded; charset=UTF-8'); } /** * @deprecated handles */ directly through FormDataconfigFormData() {
        return new HttpHeaders().set('Content-Type'.'multipart/form-data'); / /; charset=UTF-8 }configJson() {
        return new HttpHeaders().set('Content-Type'.'application/json; charset=UTF-8');
    }

    postForm<T>(url, body = {}, config = {}): Observable<Result<T>> {
        returnthis._http.post<T>(ConfigService.baseUrl + url, qs.stringify(body), {headers: this.configForm(), ... config}) } postFormData<T>(url, body = {}, config = {}): Observable<Result<T>> { const f = new FormData();for (let i in body) {
            f.append(i, body[i]);
        }
        returnthis._http.post<T>(ConfigService.baseUrl + url, f, {... config}) } postFormDataUpload<T>(url, body = {}, config = {}): Observable<Result<T>> { const f = new FormData();for (let i in body) {
            if(body.hasOwnProperty(i)) f.append(i, body[i]);
        }
        returnthis._http.post<T>(ConfigService.uploadPath + url, f, {... config}) } postJson<T>(url, body = {}, config = {}): Observable<Result<T>> {returnthis._http.post<T>(ConfigService.baseUrl + url, body, {headers: this.configJson(), ... config}) } get<T>(url, body: any = {}, config = {}): Observable<Result<T>> {return this._http.get<T>(`${ConfigService.baseUrl + url}?${qs.stringify(body)}`, config)
    }
}Copy the code
So when you wrap it like this, whatever method you use, the final pass is, okay
url, body, config. Easy to manage. If the project is large, you can add a few more services, divided by modules, different modules by different
serviceService.



@Injectable()
exportclass UserService extends ConfigService { constructor(_http: HttpClient) { super(_http); } / withdraw from the * * * * @ returns {observables < Result < any > >} * /quit() {
        returnEnclosing the get url (` `)} / login * * * * @ returns {observables < Result < any > >} * / login (body = {}, config = {}) {returnThis. PostJson (' url ', body, config)}} @returns {Observable<Result<any>>} */ register(body = {},config = {}){return this.postForm('url', body, config); }}Copy the code
The advantage of this writing is that the structure is clear, cooperative development is more friendly, each responsible for their own modules.



HTTP interceptor

All interface requests pass through the interceptor, which decides whether to continue the request. similar
window.addEventListener('fetch',(event)=> {}) This listen event. Official document
moving

@Injectable()
export class NoopInterceptor implements HttpInterceptor {
    constructor() {
    }
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        req = req.clone({headers: req.headers});
        let xhr = next.handle(req);
        return xhr
            .do(response => {
                if (response instanceof HttpResponse) {
                    if (response.type == 4) {
                        if(response.status == 200) { //... Unified logic handling, such as barrage prompt}else if(response.status == 500) { //... Unified error handling}}}}). Catch (error => {return Observable.throw(error || "Server Error"); }}})Copy the code
Use the following method so that all interface requests pass through
NoopInterceptorClass.
@NgModule({
    declarations: [
        AppComponent,
    ],
    imports: [
        BrowserModule,
        CommonModule,
        FormsModule,
        HttpClientModule,
    ],
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: NoopInterceptor,
            multi: true,
        }
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
} Copy the code
If you want to see the theory of Angular, go there



How to write a Pipe

Angular has built-in pipes such as DatePipe, UpperCasePipe, LowerCasePipe, CurrencyPipe, and PercentPipe. They can all be used directly in any template. (quoted from official website)

@Pipe({name: 'keys'})
exportClass KeysPipe implements PipeTransform {/ value preprocessing the value of the * * * * @ @ args parameter array {{value | keys: : XXX XXXX}} * @ XXX, XXXX is parametersreturn*/ transform(value, args: string[]): any {let keys = [];
        for (let key in value) {
            keys.push({key: key, value: value[key]});
        }
        returnkeys; }}Copy the code
Pure pipe and impure pipe
// The default is pure pipe@pipe ({name:'keys',
    pure: true}) // pipe@pipe ({name:'keys',
    pure: false,})Copy the code
The difference is change detection. If the value will not change in the future, pure pipes are used, otherwise impure pipes are used.

How to write an animation

In the current version, the animation module is separate, and some animation-specific modules and functions need to be imported into the root application module before animations can be added to the application
BrowserAnimationsModule .

@NgModule({
  imports: [ BrowserModule, BrowserAnimationsModule ],
})
export class AppModule { }Copy the code


import {animate, style, transition, trigger} from '@angular/animations';

export const fadeIn = trigger('fadeIn', [
    transition('void => *', [
        style({opacity: 0}),
        animate(150, style({opacity: 1}))
    ]),
    transition('* => void', [ animate(150, style({opacity: 0})) ]) ]); / * * * use * / @ Component ({animations: [fadeIn], the template: ` < div [@ fadeIn] > < / div > `,})export class IndexComponent{
    constructor() {}}Copy the code
I’m only demonstrating the simplest usage here. I suggest you go to the documentation on the official website. Please move on



Asynchronous route loading

The smallest unit of asynchronous components is a module
NgModule , directly to the code.

  • user-list.component.ts
@Component({
    selector: 'app-user-list',
    templateUrl: './user-list.component.html', encapsulation: ViewEncapsulation. None, / / HTML doesn't do any processing styleUrls: ['./user-list.component.less']})export class UserListComponent implements OnInit {
    
}Copy the code
  • user-routing.module.ts
const routes = [
    {
        path: ' ',
        children:[
            {
                path: 'userList',
                component: UserListComponent
            },
        ]
    }
]
@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class UserRoutingModule {
}Copy the code
  • user.module.ts
@NgModule({
    declarations: [
        UserListComponent,
    ],
    imports: [
        UserRoutingModule
    ],
    providers: [],
})
export class UserModule {
}Copy the code
  • app-routing.module.ts
const routes = [
    {
        path: ' ',
        component: IndexComponent,
        children:[
            {
                path: 'user',
                loadChildren: 'app/user/user.module#UserModule',
            },
        ]
    }
]
@NgModule({
    imports: [RouterModule.forRoot(routes, {useHash: true})],
    exports: [RouterModule]
})
export class AppRoutingModule {
}Copy the code

How to write a business component

First, analyze what kind of result the component needs, what necessary parameters are needed, and what the structure looks like. The appearance can be changeable, but the final result is only one.

As shown in the following figure, a component similar to provincial and municipal linkage is made. The requirement is to obtain the selected provincial and municipal ID set. If the parent level is in check state, the subset should be excluded; if the parent level is not in full selection state, the subset should also be excluded.

Once you know the requirements, you need to think about how this component might be used for other requirements in the future. When writing the component, try to break down the functionality as much as possible.

  • directional-select.component.html
<p class="selected-text"> the current selected: < span > {{result (.) length}} < / span > < / p > < div class ="tags-box">
    <ul class="clearfix data-tags">
        <li *ngFor="let rl of resultList; let index = index">
            {{rl.name}}
            <i class="yc-icon icon" (click)="removeResultList(rl)">X</i>
        </li>
    </ul>
</div>
<div class="select-list-inner">
    <div class="scope" *ngFor="let list of cacheList; let index1 = index" [ngStyle]="{' width. % ': 100.0 / cacheList length}">
        <ul class="list-with-select">
            <li class="spaui" *ngFor="let l of list; let index2 = index" (click)="pushCache(index1,index2,list)">
                <app-checkbox [(ngModel)]="l.selected" (eventChange)="areaItemChange(l)" [label]="l.name" [checkState]="l.checkState" [not_allow_selected]="l[hasCheckbox]"></app-checkbox>
                <i *ngIf="l[child]? .length > 0" class="icon yc-icon"> &#xe664; 
            </li>
        </ul>
    </div>
</div>Copy the code
This component depends on the app-checkBox and can be replaced with the native checkbox.

  • directional-select.component.less
.select-list-inner {
    background: #fff;
    width: 100%;
    border: 1px solid #ddd;
    max-height: 272px;
    overflow: auto;
    display: flex;
    .scope {
        &:first-child {
            border-left: none;
        }
        flex: auto;
        width: 0;
        overflow: auto;
        max-height: 270px;
        transition: width .2s;
        border-left: 1px solid #dfe1e7;
        .list-with-select {
            margin: 10px 0;
            .spaui {
                &:hover {
                    background: #f8f9fa;
                }
                height: 40px;
                line-height: 40px;
                padding-left: 20px;
                width: 100%;
                cursor: pointer;
                font-size: 15px;
                position: relative;
                > i {
                    float: right;
                    padding-right: 20px;
                }
            }
        }
    }
}

.tags-box {
    position: relative;
    border-top: 1px solid transparent;
    border-bottom: 1px solid transparent;
    margin-bottom: 10px;
    overflow-y: auto;
    max-height: 202px;
    overflow-x: hidden;
    transition: height .2s ease-in-out;
    ul.data-tags {
        width: 100%;
        position: relative;
        margin-bottom: 10px;
        li {
            float: left;
            margin-right: 5px;
            border: 1px solid #dfe1e7;
            border-radius: 20px;
            background: #f0f3f6;
            height: 34px;
            line-height: 32px;
            padding-left: 24px;
            padding-right: 8px;
            margin-top: 10px;
            font-size: 14px;
            transition: padding .2s ease-in-out;
            .icon {
                opacity: 0;
                transition: opacity .2s;
            }
            &:hover {
                background: #e1e7f1;
                padding-left: 16px;
                padding-right: 16px;
                transition: padding .2s ease-in-out;
                .icon {
                    opacity: 1;
                    transition: opacity .2s;
                    cursor: pointer;
                }
            }
        }
    }
}

.selected-text {
    float: left;
    line-height: 55px;
    padding-right: 10px;
    > span {
        font-size: 16px;
        font-weight: 700;
        color: #ff5e5e;}}Copy the code
  • directional-select.component.ts
import {Component, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";

const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DirectionalSelectComponent),
    multi: true
};

@Component({
    selector: 'directional-area-select'.exportAs: 'directionalAreaSelect',
    templateUrl: './directional-select.component.html',
    styleUrls: ['./directional-select.component.less'],
    providers: [
        CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR
    ]
})
export class DirectionalSelectComponent implements OnInit, ControlValueAccessor {

    constructor() {
    }
    
    onChange = (value: any) => {
    };

    writeValue(obj: any): void {
        if (obj instanceof Array && obj.length > 0) {
            this.filterFill(obj);
            this.resultList = this.result(3);
        }
    }

    registerOnChange(fn: any): void {
        this.onChange = fn;
    }

    registerOnTouched(fn: any): void {
    }

    ngOnInit() {

    }

    resultList;
    cacheList: any[] = [];
    private list: any[] = [];

    inputListChange() {
        if (this._inputList instanceof Array && this._inputList.length > 0) {
            this.list = this._inputList.map(d => {
                this.recursionChild(d);
                return d;
            });
            this.cacheList.length = 0;
            this.cacheList.push(this.list);
        }
    }

    _inputList;
    @Input('firstNotSelect') firstNotSelect = false;

    @Input('inputList') set inputList(value) {
        this._inputList = value;
        this.inputListChange();
    }

    @Input('value') value;
    @Input('hasCheckbox') hasCheckbox = 'not_allow_selected';

    @Input('child') child = 'options';
    @Output('eventChange') eventChange = new EventEmitter<any>(); * @param index1 subscript * @param index2 subscript * @param list subscript */ pushCache(index1, index2, List) {// Select laterlet cl = this.cacheList[index1 + 1];
        let child = list[index2][this.child];
        if (child instanceof Array && child.length > 0) {
            if(! cl) { this.cacheList.push(child); }else {
                if(cl ! == child) { this.cacheList.splice(index1 + 1, 1, child) } } }else {
            if(cl ! == child && ! (child instanceof Array)) { this.cacheList.pop(); }} // Select forwardif (child && child.length > 0) {
            while (this.cacheList.length > index1 + 2) {
                this.cacheList.pop();
            }
        }
    }

    removeResultList(data) {
        data.selected = false; this.areaItemChange(data); CheckState 1 2 3 areaItemChange(data) {if (data[this.hasCheckbox]) return;

        let child = data[this.child];

        if (data.selected) {
            data.checkState = 1
        } else{data.checkState = 3} // Look downifChild. The child && (length > 0) {enclosing recursionChildCheck (child)} / / up to find this. RecursionParentCheck (data); this.resultList = this.result(3);if (this.resultList instanceof Array && this.resultList.length > 0) {
            this.onChange(this.resultList.map(r => r.id));
        } else{ this.onChange(null); } this.eventChange.next(); } @param data */ private recursionParentCheck(data) {recursionParentCheck(data) {let parent = data.parent;
        if (parent) {
            let l = parent[this.child];
            let length = l.reduce((previousValue, currentValue) => {
                returnpreviousValue + ((currentValue.selected) ? 1:0)}, 0);let length2 = l.reduce((previousValue, currentValue) => {
                returnpreviousValue + ((currentValue.checkState == 2) ? 1:0)}, 0);if (length == l.length) {
                parent.checkState = 1;
                parent.selected = true;
            } else if (length == 0 && length2 == 0) {
                parent.checkState = 3
            } else {
                parent.checkState = 2;
                parent.selected = false; } this.recursionParentCheck(parent); @param list */ private recursionChildCheck(list) {if (list && list.length > 0) {
            list.forEach(data => {
                let checked = data.parent.selected;
                data.selected = checked;
                if (checked) {
                    data.checkState = 1;
                    data.selected = true;
                } else {
                    data.checkState = 3;
                    data.selected = false;
                }
                letl = data[this.child]; This. RecursionChildCheck (l)})}} private recursionChild(target) {let list = target[this.child];
        if (list && list.length > 0) {
            list.forEach(data => {
                data.parent = target;
                this.recursionChild(data)
            })
        }
    }

    /**
     * type@param {number} @param {number}type
     * @param {any[]} result
     * @returns {any[] | undefined}
     */
    result(type = 1, result = []) {
        if (this.firstNotSelect) {
            return this.recursionResult2(this.list, result, type);
        }
        return this.recursionResult(this.list, result, type); } @param list * @param {any[]} result * @param {number}type
     * @returns {any[] | undefined}
     */
    private recursionResult2(list, result = [], type = 1) {
        if (list && list.length > 0) {
            list.forEach(data => {
                let child = data[this.child];
                if (child && child.length > 0) {
                    this.recursionResult2(child, result, type);
                } else if (data.checkState == 1) {
                    switch (type) {
                        case 1:
                            result.push(data.id);
                            break;
                        case 2:
                            result.push({
                                id: data.id,
                                name: data.name,
                            });
                            break;
                        case 3:
                            result.push(data);
                            break; }}})}return result;
    }

    private recursionResult(list, result = [], type = 1) {
        if(list&& list.length > 0) {list.foreach (data => {// all selected and the parent has no check boxif ((data[this.hasCheckbox] && data.checkState == 1) || data.checkState == 2) {
                    let child = data[this.child];
                    if (child && child.length > 0) {
                        this.recursionResult(child, result, type); } // All checked and parent has check box result does not need to contain subset}else if(data.checkState == 1 && ! data[this.hasCheckbox]) { switch (type) {
                        case 1:
                            result.push(data.id);
                            break;
                        case 2:
                            result.push({
                                id: data.id,
                                name: data.name,
                            });
                            break;
                        case 3:
                            result.push(data);
                            break; }}})}returnresult; } filterFill(result) {this.cachelist. length = 1;let bo = result.find(n => {
            if (n == 1156 || String(n).length >= 6) return n
        });
        if (result instanceof Array && result.length > 0 && bo) {
            let child = this.list[0][this.child];
            while(child instanceof Array && child.length > 0) { this.cacheList.push(child); child = child[0][this.child]; }}else {
            let child = this.list[1][this.child];
            while(child instanceof Array && child.length > 0) { this.cacheList.push(child); child = child[0][this.child]; }} this. RecursionFilter (result, this.list)} private recursionFilter(target, list, result = []) {if (target instanceof Array && target.length > 0) {
            list.forEach((data) => {
                let child = data[this.child];
                let bo = target.find((d => {
                    if (d == data.id) {
                        returnd; }}));if (bo) {
                    data.selected = true;
                    data.checkState = 1;
                    this.recursionChildCheck(child);
                    this.recursionParentCheck(data);
                }
                if(child instanceof Array && child.length > 0) { this.recursionFilter(target, child); }})}}}Copy the code
The initialization state is a list containing only the top parent level, and clicking on it will bring up a list of subsets, and so on.

Thus, you can declare a variable
cacheListTo store the top-level parent data as the first layer, which you can view
inputListChangeMethods.
recursionChildThe method is to include all subsets with their own parent references.

pushCacheDisplays a list of the corresponding subset data

areaItemChange when
checkboxThis method is triggered when a change occurs. The tasks accomplished by this method include looking up for the parent to change its state, looking down for the subset to change its state, and listing the results in the form of labels. The corresponding methods can be viewed
recursionChildCheck
recursionParentCheck
result(3)

This class implements
ControlValueAccessorClass, you can use bidirectional binding to get values.



Route guard permission management

The route guard auth.guard.ts class extends from the CanActivate and CanActivateChild classes.

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
    constructor() { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { //1. You can use the current access route to request background access to check whether the current user has permission, or save the permission to the local cache.return Observable.create(false) / / 2.return new Promise((resolve, reject) => {
            setTimeout(()=> {
                resolve(false},2000)}) //3.return false} // The child route guardian parent has child route protection enabled. Entering the self-route will trigger this callback function. canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {return this.canActivate(childRoute, state);
    }Copy the code
  • The routing configurationapp-routing.module.ts  

const routes = [
    {
        path: ' ',
        component: IndexComponent,
        canActivate: [AuthGuard],
        children:[
            {
                path: 'user',
                loadChildren: 'app/user/user.module#UserModule',
            },
        ]
    }
]
@NgModule({
    imports: [RouterModule.forRoot(routes, {useHash: true})],
    exports: [RouterModule]
})
export class AppRoutingModule {
}Copy the code

  •  user-routing.module.ts  

const routes: Routes = [
    {
        path: ' ', canActivateChild: [AuthGuard],// Children: [{path:'userlist',
                component: UserListComponent,
                resolve: {jurisdiction: CurrentResolverService},
                data: {current: limitRole.userlist}
            }
        ]
    }
];
@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class UserRoutingModule {
}Copy the code