文档章节

动态组件做单页应用(非路由跳转)

SubinY
 SubinY
发布于 2017/09/04 17:32
字数 1317
阅读 101
收藏 0
点赞 0
评论 0

angular单页面应用另外一种做法,通过动态生成组件方法并配合UI库primeNG的tabView组件实现无路由单页面跳转

Dynamic Component Loader(动态组件加载器)

组件的模板不会永远是固定的。应用可能会需要在运行期间加载一些新的组件。

这个案例为你展示如何使用ComponentFactoryResolver来动态添加组件。

下面详细叙述这个案例是如何实现标题所说的组件加载实现非路由跳转(关键地方标红,附github/SubinY

管理模块module

module.module.ts

...
@NgModule({
    imports: [
        ...
    ],
    declarations: [
        ...
        HeaderTopComponent,
        NavigationMenuComponent,
        ...ENTRY_COMPONENTS
    ],
    entryComponents: [
        ...ENTRY_COMPONENTS  //在客户端编译的组件均写在这,即传统所说懒加载页面
    ],
    providers: [
        TabViewResponseService
    ]
})
export class ModulesModule {
}

module.html

<div id="main-header">
    <header-top [widthSpread]="widthSpread" (widthChange)="widthChange($event)" (gotoPerson)="navPerson($event)"
                (headerHome)="headerHome($event)" (selectSubMenu)="changeSubMenu($event)"
                (personalCenter)="menuClickHandler($event)" (gotoYujing)="menuClickHandler($event)"></header-top>
    <app-navigation-menu [widthSpread]="widthSpread" [menuData]="navigateInfo"
                         (menuClickHandler)="menuClickHandler($event)"></app-navigation-menu>
</div>
<!--路由页面-->
<!--<router-outlet></router-outlet>-->
<div id="main-con" [ngStyle]="setContentStyle()">
    <div class="tab-view">
        <ui-tabview #tabview [tabs]="tabs" [activeIndex]="tabActiveIndex"
                    (tabClose)="handlerTabClose($event)" (tabChange)="handleTabChange($event)"></ui-tabview>
    </div>
</div>

以往是通过router-outlet输出页面,现在没路由控制就变成上图所示封装一层ui-tabview组件装在primeNG的组件代替路由输出

module.component.ts

...

@Component({
    ...
})
export class ModulesComponent implements OnInit, AfterViewInit {
    ...

    constructor(
        public tabViewService: TabViewService,
        public tabViewResponseService: TabViewResponseService) {
    }

    ngOnInit(): void {
        this.tabsComponent = this.tabViewService.getTabs();
        this.tabs.push(this.tabsComponent[0]);
        this.navigateInfo = navigateInfo;
    }

    /**
     * 菜单点击处理事件
     * @param $event
     */
    menuClickHandler($event) {
        let isOpen = false;
        this.tabs.forEach((tab, index) => {
            if (tab.data.name === $event.name) {
                this.tabActiveIndex = index;
                isOpen = true;
            }
        });
        if (!isOpen) {
            this.tabsComponent.forEach((tab, index) => {
                if (tab.data.name === $event.name) {
                    let selectedTab = this.tabview.findSelectedTab();
                    if (selectedTab) {
                        selectedTab.selected = false;
                    }
                    this.tabs.push(tab);
                    setTimeout(() => {
                        this.tabActiveIndex = this.tabs.length - 1;
                    }, 100)
                }
            });
            setTimeout(() => {
                this.tabViewResponseService.openTabNav(this.uiTabView.el.nativeElement);
            })
        }
    }

    /**
     * tab关闭触发事件
     * @param $event
     */
    handlerTabClose($event) {
        let index = $event.index;
        this.tabs.splice(index, 1);

        this.tabViewResponseService.closeTabNav(this.uiTabView.el.nativeElement);
    }

    handleTabChange($event) {

    }

}

menuClickHandler、handlerTabClose这两个方法就是控制组件数组的添加和删除具体实现看primeNG的tabview组件。tabViewResponseService是用来动态计算页面最大宽度来控制tab的宽度,并且样式是通过scss的一些变量去控制的,有兴趣的可以去github/SubinY去看看。

EntryComponents.ts

/*订单管理*/
import { OrderManageComponent } from "./order-manage/order-manage.component";

/*库存管理*/
import { WarehouseEntryManageComponent } from './warehouse-manage/page/warehouse-entry-manage/warehouse-entry-manage.component';
import { WarehouseLeaveManageComponent } from './warehouse-manage/page/warehouse-leave-manage/warehouse-leave-manage.component';
import { WarehouseCheckComponent } from './warehouse-manage/page/warehouse-check/warehouse-check.component';
import { WarehouseInfoComponent } from './warehouse-manage/page/warehouse-info/warehouse-info.component';
import { WarehouseAbnormalComponent } from './warehouse-manage/page/warehouse-abnormal/warehouse-abnormal.component';
import { WarehouseAreaAdjustmentComponent } from "./warehouse-manage/page/warehouse-area-adjustment/warehouse-area-adjustment.component";

export const ENTRY_COMPONENTS = [
    // 第一个页面必须写在第一位,其他组件随便写位置
    OrderManageComponent,
    WarehouseEntryManageComponent,
    WarehouseLeaveManageComponent,
    WarehouseCheckComponent,
    WarehouseInfoComponent,
    WarehouseAbnormalComponent,
    WarehouseAreaAdjustmentComponent,
]

由于初始化modules模块时,必须有一个页面在登录后显示,所以要显示的页面组件必须放在第一位,这是由modules.component.ts生命周期设定的取数组第一个。

 

TabView(动态组件的核心)

tabview.html

<!--ui-tabview-->
<div class="tab-view">
    <p-tabView #tabRef  [activeIndex]="activeIndex" (onChange)="handleChange($event)" (onClose)="handleClose($event)">
        <p-tabPanel *ngFor="let tab of tabs;let i=index;" header="{{tab.data.name}}" selected="tab.data.selected"  [closable]="true" (click)="tabPanelClick(i)">
            <ui-tab-panel [tabItem]="tab"></ui-tab-panel>
        </p-tabPanel>
    </p-tabView>
    <div id="contextMenu">
        <p-menu [model]="items"></p-menu>
    </div>
</div>

这是primeNG的tabview结构,其中ui-tab-panel就是我们要显示的页面,tabs是外界传进来的页面组件数组。

tab-item.ts

import { Type } from '@angular/core';

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

定义所需组件类型Type和参数data,在后面的tabview.service.ts会用到

tabview.service.ts

import {Injectable} from '@angular/core';

import { TabItem } from "./ui/tabview/tab-item";
import { ENTRY_COMPONENTS } from "../../modules/EntryComponents";

@Injectable()
export class TabViewService {

    constructor() {

    }

    getTabs() {
        let tabs = [];
        for (let c of ENTRY_COMPONENTS) {
            let item = new TabItem(c, {name: c['componentName'] || '无名称'});
            tabs.push(item);
        }
        return tabs;
    }
}

tab-panel.ts

import {
    Component,
    OnInit,
    Input,
    AfterViewInit,
    ViewChild,
    ComponentFactoryResolver,
    Type,
    ViewContainerRef
} from "@angular/core";
import {TabConfig} from "./TabConfig";


@Component({
    selector: "ui-tab-panel",
    template: "<div #dynamicContainer></div>",
})
export class UITabPanel implements OnInit,AfterViewInit {

    @Input()
    tabItem: TabConfig;

    @ViewChild( "dynamicContainer", { read: ViewContainerRef } ) dynamicContentContainer: ViewContainerRef;

    currentTabIndex: number = 1;

    constructor(private componentFactoryResolver: ComponentFactoryResolver) {
    }

    ngOnInit(): void {
        this.loadComponent();
    }

    ngAfterViewInit(): void {
    }


    loadComponent() {
        // this.currentTabIndex = (this.currentTabIndex + 1) % this.tabs.length;

        let componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.tabItem.component);

        // let viewContainerRef = this.tabHost.viewContainerRef;
        this.dynamicContentContainer.clear();

        let componentRef = this.dynamicContentContainer.createComponent(componentFactory);
        (<TabConfig>componentRef.instance).data = this.tabItem.data;
    }


}

Angular 4.x 动态创建组件

这里解析非常详细,我就不在这再啰嗦了。这次blog过多代码,很少解释,希望大家看懂了动态创建组件这篇文章再回头看这篇案例,应该会让你更加清晰,动态创建组件的用法。

 

1、tabview组件包括tabview.html(装载primeng的p-tabview组件)tabview.ts(封装p-tabview的逻辑代码)tab-panel.ts(包裹有动态加载逻辑的组件)TabConfig.ts(外来组件的接口核心component,data)tab-item.ts(类似抽象类定义需要动态加载的组件,实质是一个对象)tab.directive.ts(需要装载动态加载组件的指令,指向装载的模板,可以用模板变量配合viewChild就无需指令)tabview.service.ts(获取EntryComponents中的懒加载组件,用TabItem定义)

在菜单栏点击菜单,通过TabItem的name去判断是否已经打开指定的tab,有则高亮tab,没有则为局部变量tabs(数组,装载已经打开的tab)放进一个新的组件。当遇到相同组件需要重复打开的时候,也是通过name去控制,当然这里的name必须要动态改变,不能重复。为何能重复打开一个组件,原因是每个组件都动态加载在一个ng-template里面,都是独立的组件,可以说把外面的组件当成内容加载进去每一个不同的ng-template上面而已

 

细节: tab页切换的同时顶部标题栏也会跟着你改变

核心代码

import { Title } from "@angular/platform-browser";
this.title.setTitle('IPS-'+tab.data.name);

 

https://segmentfault.com/a/1190000010086185

Angular4 动态加载组件杂谈

官网动态组件加载器

动态创建模块,动态添加组件,拖拽组件生成UI例子

© 著作权归作者所有

共有 人打赏支持
SubinY
粉丝 8
博文 86
码字总数 63997
作品 0
佛山
程序员
基于Vue的多项目整合实践

在笔者所在的前端开发团队中,采用前后端分离方案是在整个业务线稳定后进行的。业务前期主要采用后端套模板的方式,现阶段是采用基于的单页开发模式。 这会出现一种情形,产品在不断迭代过程...

代码君的自白 ⋅ 2017/10/15 ⋅ 0

Vue单页及多页应用全局配置404页面实践

这篇文章也发在我的博客,欢迎围观😄 写在前面 前后端分离后,控制路由跳转的责任转移到了前端,后端只负责给前端返回一个html文档以及提供各种接口。下面我们用作例子的两个项目,均采用v...

FrankCheung ⋅ 05/20 ⋅ 0

Vue2.0笔记——vue-router路由

简介 使用Vue.js开发SPA(Single Page Application)单页面应用。 vue-router可以通过html5的history API或者hash实现单页应用,即不刷新跳转,切换地址,只是页面上的组件的切换; vue-rout...

逝岁月 ⋅ 04/24 ⋅ 0

关于vue-router那些事

一、前言 要学习vue-router就要先知道这里的路由是什么?为什么我们不能像原来一样直接用标签编写链接哪?vue-router如何使用?常见路由操作有哪些?等等这些问题,就是本篇要探讨的主要问题...

howgod ⋅ 05/21 ⋅ 0

Vue2.0之vue-router

一、前言 要学习vue-router就要先知道这里的路由是什么?为什么我们不能像原来一样直接用标签编写链接哪?vue-router如何使用?常见路由操作有哪些?等等这些问题,就是本篇要探讨的主要问题...

浪里行舟 ⋅ 05/21 ⋅ 0

vue-router用法记录

###Vue-Router简介 Vue-Router是Vue.js官方维护的路由插件,同时也是官方推荐的路由插件。它与Vue.js内核深度结合,让开发单页应用更加容易简便。 ####基本配置 HTML 当 对应的路由匹配成功,...

iceuncle ⋅ 2017/11/28 ⋅ 0

vue-router 入门学习

对于单页面应用,尤其是移动端的网页应用,使用标签实现页面切换和跳转会有300ms的延迟,遇到网速慢就会一直加载。所以在SPA应用中,用视图切换来模拟页面的切换 vue-router 是Vue.js官方的路...

frwupeng517 ⋅ 2017/08/05 ⋅ 0

前端路由简介以及vue-router实现原理

后端路由简介 路由这个概念最先是后端出现的。在以前用模板引擎开发页面时,经常会看到这样 大致流程可以看成这样: 浏览器发出请求 服务器监听到80端口(或443)有请求过来,并解析url路径 ...

muwoo ⋅ 06/01 ⋅ 0

全新单页JavaScript框架iceJS

单页Web应用作为新一代Web模式,为用户提供了更流畅的体验满足感。早在Chrome5、IE8和Firefox3的年代就可以通过hash技术实现单页Web应用,而HTML5新history API的出现让单页Web应用从幕后迅速...

JOU_icejs ⋅ 2017/12/15 ⋅ 0

浅谈移动端页面无刷新跳转问题的解决方案

浅谈移动端页面无刷新跳转问题的解决方案 2017-12-22 祈澈姑娘 最近开发微信公众号,用的框架是 一般的 UI,但是涉及到多页面之间的相互跳转的问题,降低了浏览器的性能,用户体验特别不好,...

祈澈姑娘 ⋅ 2017/12/22 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

JavaScript零基础入门——(十一)JavaScript的DOM操作

JavaScript零基础入门——(十一)JavaScript的DOM操作 大家好,欢迎回到我们的JavaScript零基础入门。最近有些同学问我说,我讲的的比书上的精简不少。其实呢,我主要讲的是我在开发中经常会...

JandenMa ⋅ 今天 ⋅ 0

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 今天 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 今天 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 今天 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 今天 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部