ionic4/angular6 slides组件动态加载组件

2018-10-27  本文已影响439人  Gemkey

在开发过程中,遇到需要使用ionSlides来动态加载数据的一个需求,即在每次切换到新的slide时,需要动态的去加载每个组件的数据。在刚开始做开发的时候发现直接使用组件时,所有组件都会被加载,然后就去请求后台,切换slide时,再去请求,这就导致了大量的资源浪费,也增加了后台的压力,尤其是当请求比较缓慢时体验是非常差的,因此便考虑需要怎么处理这个问题。

首先想到的是做组件的动态加载,于是就跟着官网的示例开发自己的功能,官网链接:https://www.angular.cn/guide/dynamic-component-loader。在这里就以demo来做演示。
首先创建一个dynamic-component模块,目录结构如下:

目录结构

其中
1.lnk-image组件为需要被动态加载的组件
2.anchor为锚点指令

dynamic-component.module.ts

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {RouterModule, Routes} from '@angular/router';

import {IonicModule} from '@ionic/angular';

import {DynamicComponentPage} from './dynamic-component.page';
import {AnchorDirective} from './directive/anchor/anchor.directive';
import {LnkImageComponent} from './component/image/lnk-image.component';

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

@NgModule({
    imports: [
        CommonModule,
        FormsModule,
        IonicModule,
        RouterModule.forChild(routes)
    ],
    declarations: [
        DynamicComponentPage,
        AnchorDirective,
        LnkImageComponent
    ],
    entryComponents: [
        LnkImageComponent
    ]
})
export class DynamicComponentPageModule {
}

dynamic-component.page.html

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>动态组件</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content padding>
  <ion-slides pager (ionSlidesDidLoad)="onSlidesDidLoad($event)" (ionSlideDidChange)="onSlideDidChange($event)">
    <ion-slide *ngFor="let image of images">
      <ng-template appAnchor></ng-template>
    </ion-slide>
  </ion-slides>
</ion-content>
import {Component, ComponentFactoryResolver, OnInit, QueryList, Type, ViewChild, ViewChildren} from '@angular/core';
import {LnkImageComponent} from './component/image/lnk-image.component';
import {Slides} from '@ionic/angular';
import {AnchorDirective} from './directive/anchor/anchor.directive';

@Component({
    selector: 'app-dynamic-component',
    templateUrl: './dynamic-component.page.html',
    styleUrls: ['./dynamic-component.page.scss'],
})
export class DynamicComponentPage implements OnInit {
    images = [                                                              // 图片组件
        {src: 'http://t2.hddhhn.com/uploads/tu/20150520/061112158440.jpg', author: 'gemini'},
        {src: 'http://img.zcool.cn/community/0125fd5770dfa50000018c1b486f15.jpg@1280w_1l_2o_100sh.jpg', author: 'naci'},
        {src: 'http://t2.hddhhn.com/uploads/tu/20150520/152219311390.jpg', author: 'lucy'},
        {src: 'http://img12.3lian.com/gaoqing02/01/58/85.jpg', author: 'grey'},
        {src: 'http://t2.hddhhn.com/uploads/tu/20150520/061109468190.jpg', author: 'elusa'}
    ];
    currentIndex;                                                           // 当前slide的索引
    components: DynamicComponentItem[];                                     // 动态组件数组
    @ViewChild(Slides) slides: Slides;                                      // slides 对象
    @ViewChildren(AnchorDirective) anchors: QueryList<AnchorDirective>;     // 锚点对象

    constructor(
        private componentFactoryResolver: ComponentFactoryResolver
    ) {
    }

    ngOnInit() {
        // 初始化组件参数
        this.initComponents();
    }

    /**
     * slides 对象初始化完成事件
     * @author : gemini
     * @date: 2018/10/27
     * @param $event 事件对象
     */
    onSlidesDidLoad($event) {
        this.loadComponent();
    }

    /**
     * slides 对象切换完成
     * @author : gemini
     * @date: 2018/10/27
     * @param $event 事件对象
     */
    onSlideDidChange($event) {
        this.loadComponent();
    }

    /**
     * 加载组件
     * @author : gemini
     * @date: 2018/10/27
     */
    async loadComponent() {
        this.currentIndex = await this.slides.getActiveIndex();
        // 根据当前slide的索引,拿到当前动态组件对象item
        const dynamicComponentItem = this.components[this.currentIndex];
        // 根据当前slide的索引,从锚点列表中,取到对应的slide上的锚点
        const anchor = this.anchors.toArray()[this.currentIndex];
        // 获取组件工厂
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(LnkImageComponent);
        // 获取锚点上的ViewContainerRef
        const viewContainerRef = anchor.viewContainerRef;
        // 清除原模板上的内容
        viewContainerRef.clear();
        // 动态创建组件
        const componentRef = viewContainerRef.createComponent(componentFactory);
        // 给组件传入数据
        (<LnkImageComponent>componentRef.instance).src = dynamicComponentItem.data.src;
        (<LnkImageComponent>componentRef.instance).author = dynamicComponentItem.data.author;
    }

    /**
     * 初始化组件数组
     * @author : gemini
     * @date: 2018/10/27
     */
    initComponents() {
        this.components = [];
        // 根据参数,创建动态组件对象
        for (let i = 0; i < this.images.length; i++) {
            const image = this.images[I];
            this.components.push(new DynamicComponentItem(LnkImageComponent, {src: image.src, author: image.author}));
        }
    }
}

/**
 * 动态组件对象
 * @author : gemini
 * @date: 2018/10/27
 */
export class DynamicComponentItem {
    /**
     * 构造函数中的两个参数
     * @author : gemini
     * @date: 2018/10/27
     * @param component 组件对象
     * @param data 需要传入给组件的数据
     */
    constructor(public component: Type<any>, public data: any) {
    }
}

anchor.directive.ts

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

/**
 * 锚点指令
 * @author : gemini
 * @date: 2018/10/27
 */
@Directive({
    selector: '[appAnchor]'
})
export class AnchorDirective {

    constructor(public viewContainerRef: ViewContainerRef) {
    }

}

lnk-image.component.ts

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

@Component({
    selector: 'app-lnk-image',
    templateUrl: './lnk-image.component.html',
    styleUrls: ['./lnk-image.component.scss']
})
export class LnkImageComponent implements OnInit {
    @Input() src;         // 图片显示的url
    @Input() author;      // 作者
    constructor() {
    }

    ngOnInit() {
        console.log('ImageComponent', `[author]:${this.author};[src]${this.src};`);
    }
}
<div>
    <div class="image-container">
        <img [src]="src">
    </div>
    <div class="author">
        <h5>{{author}}</h5>
    </div>
</div>
.image-container {
  width: 100%;
  height: 50%;
  img {
    width: 100%;
    height: 100%;
  }
}

.author {
  text-align: center;
}

在自己的项目中需要使用时,只需要作出如下替换:
1.images可以通过http请求,或在service以及作为组件入参等各种方式传入,然后再做初始化组件对象。
2.替换对应的需要动态加载的组件。

效果

从图片中可以看到,每次切换图片后,才会去请求图片的url。

上一篇下一篇

猜你喜欢

热点阅读