angular9的学习(四)

指令 Directive

inputs

跟上一节一样,不过需要自己试试才知道

<div appEnabled [aaa]="123"  [bbb]="456">12313131</div>

@Directive({
  selector: '[appEnabled]',
  inputs:['aaa']
})
export class EnabledDirective implements OnInit{
  public aaa;
  @Input('bbb') b;
  constructor() {
  }
  ngOnInit(){
    console.log(this.aaa);
    console.log(this.b);
  }
}

outputs

父
<div appEnabled (bankName)="add($event)" (child)="add($event)>12313131</div>

add(e){
    console.log(e)
}
子
@Directive({
  selector: '[appEnabled]',
  outputs:['bankName']
})
export class EnabledDirective implements OnInit{
  public bankName=new EventEmitter();
  @Output('child') c = new EventEmitter();
  constructor() {
  }
  ngOnInit(){
    this.bankName.emit('gggg');
    this.c.emit('ccc')
  }

}

providers 令牌

这个跟之前保持一致

exportAs 别名

我思考的时候一直在想这个有什么作用,后面才发现了,这个跟查找子组件的想法一样

@Directive({
selector: '[appEnabled]',
exportAs: 'enabledDir'
})

父
<div appEnabled #dir='enabledDir' [red]="'sss'">12313131</div>
//我竟然可以执行管道的方法
<button (click)="dir.add()">点击</button>
export class EnabledComponent implements OnInit ,AfterViewInit{
constructor() {
}
@ViewChild('dir') dir;
ngAfterViewInit(){
 console.log(this.dir);
}
add(){
    console.log(1)
}    
}

queries

思考半天,还是不清楚这个属性的用意

host

绑定特定的键值对属性到DOM上

@Directive({
  host:{
    'one':'two'
  }
})

HostListener 监听DOM事件

eventName 要监听的DOM事件。
args 事件发生时传递给处理程序方法的一组参数。
<div appEnabled>点我</div>

@Directive({
  selector: '[appEnabled]',
})

export class EnabledDirective{
  @HostListener('click',['$event.target']) clickHandler(val){
    console.log(val);
  }

}
监控全局键盘事件
 @HostListener('window:keydown', ['$event'])
  keydownN(v:KeyboardEvent) {
    console.log(v);
  }

[属性绑定]

可以绑定一些自定义属性

<div [attr.name]="113">wqwwqwqwq</div>

[class绑定]

<h1 [class.aaa]="isSpecial">12112</h1>
 isSpecial=true;

[style绑定]

<h1 [style.color]="'red'">122112</h1>
<h1 [style.font-size.px]="20">122112</h1>
<div [ngStyle]="{'font-size.px': 20, color: 'white', background: 'red'}">
  Text in this div is 20px size, white color on red background.
</div>

伪事件

<input (keyup.enter)="enterHandler()">
<input (keydown.esc)="escHandler()">
<input (keyup.shift.f)="shiftFHandler()">
    
按 enter
按 esc
按 shift+f

main.ts

可以放入全局功能的js

动态组件

简单版

需求:添加一个动态组件,然后3s后删掉
模块

@NgModule({
  declarations: [BlockComponent, UserComponent, EnabledComponent, RefsComponent, EnabledDirective, DyOneComponent, DyTwoComponent,AdHostDirective],
  imports: [
    BlockRoutingModule,
    ComomModule
  ],
  providers: [BlockService],
  entryComponents: [DyOneComponent, DyTwoComponent] //添加动态的组件
})

指令
import {Directive, ViewContainerRef} from '@angular/core';

@Directive({
  selector: '[appAdHost]'
})
export class AdHostDirective {

  constructor(public viewContainerRef:ViewContainerRef) { }
}

页面
<ng-template appAdHost></ng-template>

ts

export class BlockComponent implements OnInit, AfterViewInit {
  @ViewChild(AdHostDirective) dlHost: AdHostDirective;

  constructor(private cfr: ComponentFactoryResolver) {
  }

  ngAfterViewInit() {
    this.dlHost.viewContainerRef.createComponent(
      this.cfr.resolveComponentFactory(DyTwoComponent)
    );
    setTimeout(() => {
      this.dlHost.viewContainerRef.clear();
    }, 3000)
  }

  ngOnInit(): void {
  }

}

稍微复杂点的版本

写一个定时器,3s切来切去

export class BlockComponent implements OnInit, AfterViewInit, OnDestroy {
  @ViewChild(AdHostDirective) dlHost: AdHostDirective;
  num: number = 0;
  interval: any;
  comArr = [DyOneComponent, DyTwoComponent];

  constructor(private cfr: ComponentFactoryResolver) {
  }

  ngAfterViewInit() {
    this.addComponent()
  }

  ngOnDestroy() {
    clearInterval(this.interval)
  }

  addComponent() {
    this.interval = setInterval(() => {
      this.num = this.num > 1 ? 0 : this.num;
      this.dlHost.viewContainerRef.clear();
      this.dlHost.viewContainerRef.createComponent(
        this.cfr.resolveComponentFactory(this.comArr[this.num])
      );
      this.num++;
    }, 3000)
  }

  ngOnInit(): void {

  }

}

路由 Router

const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'hero/:id',      component: HeroDetailComponent },
  {
    path: 'heroes',
    component: HeroListComponent,
    data: { title: 'Heroes List' }
  },
  { path: '',
    redirectTo: '/heroes',
    pathMatch: 'full'
  },
  { path: '**', component: PageNotFoundComponent }
];

路由器出口

<router-outlet></router-outlet>

多视口

<router-outlet></router-outlet>
<router-outlet name='left'></router-outlet>
<router-outlet name='right'></router-outlet>

  {
    path: '',
    component: DyTwoComponent,
    outlet:'left'
  },
  {
    path: '',
    component: RefsComponent,
    outlet:'right'
  }

弄了半天才踩出这个坑, 注意了name属性确定了, 该插件不能动态设置,只能写静态的,我尝试了很多种方法,跳转都报错,应该是静态的吧

路由导航

<a routerLinkActive="aaa" *ngFor="let item of arr" [routerLink]="item" [routerLinkActiveOptions]="{exact:true}">点我</a>

public arr=['','/pro/1','/one','/two'];

在链接的路由变成活动的时候将css添加类

<a routerLink="/user/bob" routerLinkActive="class1 class2">Bob</a>
<a routerLink="/user/bob" [routerLinkActive]="['class1', 'class2']">Bob</a>

[routerLinkActiveOptions]="{exact:true}" 仅仅当url与链接完全匹配时,才添加routerLinkActive里面的类

可以把RouterLinkActive 实例分配给模板变量,检查isActive 状态

简单的说就是鼠标点击的时候,拿到这个状态

<a routerLink="/user/bob" routerLinkActive="aaa" #rla="routerLinkActive">
  Bob {{ rla.isActive ? '是' : '否'}}
</a>
<a routerLink="/user/bob">link to user component</a>

动态的
[routerLink]=['/team', teamId, 'user', userName, {details: true}] 
/team/11/user/bob;details=true

如果第一段是前置/,./,../

/		根目录
./		当前路由的子级
../		上升一个级别

关于# ?

<a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education">
  link to user component
</a>

/user/bob#education?debug=true

ActivatedRoute 从节点中提取信息

  {
    path: 'heroes',
    component: HeroListComponent,
    data: { title: 'Heroes List' }
  },

export class EnabledComponent implements OnInit {
   this.route.data.subscribe(val=>{
     console.log(val); //title: 'Heroes List'
   })
  constructor(private route: ActivatedRoute) {
  }

  ngOnInit(): void {

    this.route.params.subscribe(val=>{
      console.log(val); //动态的参数
    });
    this.route.url.subscribe(val=>{
      console.log(val); // url
    });
    this.route.queryParamMap.subscribe(val=>{
      console.log(val); // 所有的映射的参数
    })
    this.route.paramMap.subscribe(val=>{
      console.log(val); //当前的,**这个用的多一些**
    })
  }
}

监控路由发生变化的事件

要写的专业点,修改原来的学习(二)的内容

export class AppComponent implements OnInit,OnDestroy{
routeEnd:Observable<NavigationEnd>;
constructor(private router: Router) {
 this.routeEnd = this.router.events.pipe(
   filter(evt => evt instanceof NavigationEnd)
 ) as Observable<NavigationEnd>;
}
ngOnInit(){
 this.routeEnd.subscribe(val=>{
   console.log(val);
 })
}
ngOnDestroy(){
    this.routeEnd.unsubscribe() //不要忘记处理手动订阅
}
}

路由复用策略

也可以叫缓存路由

RouteReuseStrategy

abstract  class  RouteReuseStrategy {
  // 判断是否复用路由
  abstract  shouldReuseRoute(future:  ActivatedRouteSnapshot, curr:  ActivatedRouteSnapshot): boolean
  // 存储路由快照&组件当前实例对象
  abstract  store(route:  ActivatedRouteSnapshot, handle:  DetachedRouteHandle):  void
  // 判断是否允许还原路由对象及其子对象
  abstract  shouldAttach(route:  ActivatedRouteSnapshot): boolean
  // 获取实例对象,决定是否实例化还是使用缓存
  abstract  retrieve(route:  ActivatedRouteSnapshot):  DetachedRouteHandle  |  null
  // 判断路由是否允许复用
  abstract  shouldDetach(route:  ActivatedRouteSnapshot): boolean
}

shouldReuseRoute

当要在路由之间导航的时候调用,调用这个方法并不代表一定会跳转到新的页面。
如果返回true, 那么不会进行跳转;如果返回false,才会跳转并且执行其余的那几个方法。

shouldDetach

离开页面的时候调用的方法, 会比落地页面的构造函数先执行

如果返回true,则会执行 store方法

store

shouldDetach 返回true的时候调用,可以在这里存储被detach下来的RouteHandle,

shoeldAttach

判断是否恢复之前的store路由

如果返回true的话,retrieve 方法会被调用

retrieve

返回之前存储的RouteHandle,如果返回null 的话则不起作用

创建一个服务

import {Injectable} from '@angular/core';
import {
  ActivatedRouteSnapshot,
  RouteReuseStrategy,
  DetachedRouteHandle
} from '@angular/router'

@Injectable()
export class BlockService implements RouteReuseStrategy {
  public handlers: { [key: string]: DetachedRouteHandle } = {};

  //表示对路由允许复用
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    //默认对所有路由复用 可通过给路由配置项增加data: { keep: true }来进行选择性使用,代码如下
    //如果是懒加载路由需要在生命组件的位置进行配置
    if (!route.data.keep) {
      return false;
    }
    return true;
  }

  //当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const path = route.routeConfig?.path
    if (path) {
      this.handlers[path] = handle;
    }
  }

  //若path在缓存中有的都认为允许还原路由
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    if(route.routeConfig?.path){
      return !!this.handlers[route.routeConfig?.path]
    }
    return false
  }

  // 从缓存中获取快照,若无则返回null
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle| null {
    if (!route.routeConfig) return null;
    if (route.routeConfig.loadChildren) return null; //在loadChildren路径上通过修改自定义RouteReuseStrategy中的检索函数时从不检索分离的路由。
    if(route.routeConfig?.path){
      return this.handlers[route.routeConfig?.path];
    }
    return null
  }

  //进入路由触发,判断是否同一路由
  shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === current.routeConfig;
  }
}

在app.module.ts导入这个服务

 providers: [{
    provide: RouteReuseStrategy,
    useClass: BlockService
  }],
posted @ 2020-04-03 19:58  猫神甜辣酱  阅读(629)  评论(0编辑  收藏  举报