3

本文是基于angular官方文档所给出的代码来进行的。
传送门
我们以广告为例来展示其相应功能。
下面是我们想要实现的效果:

我们会发现广告有不同大小,格式。同一个格式下会有不同的内容。下面我们先来说明这是如何实现的。
广告的每一种格式都对应着一个组件,而这些组件都要实现一个共同的接口用来把数据传给组件。

我们实例中的接口很简单,只声明了data项,在使用中我们还可以进行更加细化的设置:

//广告组件实现的公共接口
export interface AdComponent {
  data: any;
}

之后我们便可以像下面这样编写我们的广告栏的样式:

@Component({
  template: `
    <div class="job-ad">
      <h4>{{data.headline}}</h4>
      {{data.body}}
    </div>
  `
})
export class HeroJobAdComponent implements AdComponent {
  //接受传入的data
  @Input() data: any;
}

之后我们还需要像下面这样对整个广告的内容进行规定——声明格式来源组件和广告内容

export class AdItem {
  constructor(public component: Type<any>, public data: any) {}
}

之后我们就可以转接到服务层编写广告的获取操作:

@Injectable()
export class AdService {
  getAds() {
    return [
      new AdItem(
        HeroProfileComponent,
        { name: 'Bombasto', bio: 'Brave as they come' }
      ),
      new AdItem(
        HeroProfileComponent,
        { name: 'Dr IQ', bio: 'Smart as they come' }
      ),
        . . .
    ];
  }
}

之后便到了最重要的部分——动态组件加载器。

在添加组件之前,先要定义一个锚点来告诉 Angular 要把组件插入到什么地方。
使用一个名叫 AdDirective 的辅助指令来在模板中标记出有效的插入点。

@Directive({
  selector: '[adHost]',
})
export class AdDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

AdDirective 注入了 ViewContainerRef 来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主。
此处个人理解为:ViewContainerRef就可以实现在当前组件中插入显示其他组件内容的功能。

我们先把动态组件加载器中完整代码放出来:

export class AdBannerComponent implements OnInit, OnDestroy {
  @Input() ads: AdItem[] = [];

  currentAdIndex = -1;

  @ViewChild(AdDirective, {static: true}) 
  adHost!: AdDirective;
  interval: number|undefined;

  ngOnInit() {
    this.loadComponent();
    this.getAds();
  }

  ngOnDestroy() {
    clearInterval(this.interval);
  }

  loadComponent() {
    //当前的广告,用于在Ads数组中循环取样
    this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
    const adItem = this.ads[this.currentAdIndex];

    const viewContainerRef = this.adHost.viewContainerRef;
    //清除当前视图中的广告,避免广告堆积
    viewContainerRef.clear();

    const componentRef = viewContainerRef.createComponent<AdComponent>(adItem.component);
    componentRef.instance.data = adItem.data;
  }

  getAds() {
    this.interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
  }
}

首先我们通过@ViewChild注解用于从模板视图中获取匹配的元素也就是获取我们上面配置的锚点。
接下来,我们要把 viewContainerRef 指向这个组件的现有实例。

这里的 loadComponent() 方法很重要。它先选取了一个广告。

要把这个组件添加到模板中,我们可以调用 ViewContainerRef 的 createComponent()。
createComponent() 方法返回一个引用,指向这个刚刚加载的组件。 使用这个引用就可以与该组件进行交互,比如设置它的属性或调用它的方法。

也就是说我们需要把要显示的广告的组件通过viewContainerRef与componentRef进行绑定,然后再把广告的data传给componentRef.instance,而这个componentRef便是我们所要展示的。

之后我们要做的就是每隔一定时间循环调用loadComponent(),在此处我们使用如下代码实现:

 interval: number|undefined;

 ngOnInit() {
    this.loadComponent();
    this.getAds();
  }

  getAds() {
    this.interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
  }

到此我们的动态组件加载器便已完成,我们使用时只需在想要显示广告的页面中进行这样调用即可:

//V层
<div>
      <app-ad-banner [ads]="ads"></app-ad-banner>
</div>

其中的ads是通过上面的M层获取的。

   //c层
   this.ads = this.adService.getAds();

流程图:
未命名文件 (1).jpg


李明
441 声望18 粉丝