angular4中modal的使用经验分享

本文是对实际项目中出现的一些问题进行的经验总结
虽然本人还是前端小白,希望能通过这种记录方式积累经验,也希望能给看文章的各位一点帮助
第一次用angular4做项目,可能会有很多笨办法或者逻辑上的不严谨,希望大家多多指教

很多业务场景中都会出现弹出modal框,填写详细信息或报错信息之类的业务需求,本文将对angular4中的modal如何进行数据交互,以及弹出及消失的控制进行一些心得分享

1.简单的父子关系

由于modal总是由某个特定页面,经特殊操作后触发,所modal的component与触发它的component是具有父子关系的。而父子关系的数据传递相对比较简单,常用的有Input/Output传递数据,或通过service进行数据的中转,对于最简单的父-子组件数据传递,普通的Input/Output已经能满足要求
(代码就不写的太复杂了,只把export class中的代码写出来了哈)

ModalComponent.ts

export class ModalComponent implements OnInit{
    // 用于接收需要在modal框中显示的信息或者其他什么信息
    @Input()
    modal_info;
    // 发射隐藏modal的事件
    @Output()
    hide_emitter = new EventEmitter();
    
    constructor() {}
        
    ngOnInit() {}
    
    // 关闭modal框的事件
    hideModal() {
    //将关闭modal的需求发射至父组件
        this.hide_emitter.emit(emitted_info);
    }
}

ParentComponent.html

<modal [modal_info]="parent_info" *ngIf="modal_control === 'child_name'"
    (hide_emitter)="getEmitter($event)">

ParentComponent.ts

export class ParentComponent implements OnInit {
    //modal的默认显示状态是隐藏
    private modal_control = '';
    constructor() {}
    
    ngOnInit() {}
    // 显示modal
    showModal() {
       this.modal_control = 'child_name';
    }
    //当初发modal的关闭事件,父组件接收到子组件发射的事件
    getEmitter(event) {
        //接收到事件则说明modal需要隐藏
        this.modal_control = event;
    }
}

这是一种最简单的业务需求,但也是笔者实现复杂modal的基础原理

2.多个modal的处理

在经历了最简单的父子关系之后,需求从独生子女家庭逐渐过度到了二胎政策开放。
在同一个父组件下可能根据业务的需求,弹出两种,三种,甚至更多种modal,笔者也曾想过在一个component中根据传入值得不同进行针对性显示

<div *ngIf="modal_name === 'modal_one'">
    ...
</div>
<div *ngIf="modal_name === 'modal_two'">
    ...
</div>

但这种个人认为这种写法并不是非常优雅,因为如果需要十个八个modal的时候,代码的维护性就会有所降低(至少看起来很烦人),于是想到是不是应该将每一个modal都做成一个component,这样既有利于html代码的整洁,也可以使ts中的代码更美观,更易维护。

ParentComponent.html

<modal-one [modal_info]="modal_info_two"></modal-one>
<modal-two [modal_info]="modal_info_one"></modal-two>

这样看起来好像是好了一点,但是比较丑的是在Parent的html文件末尾会堆下一堆modal的标签指令,所以笔者考虑了一种祖父-父-子结构的处理方式
祖父组件负责modal显示的触发,将触发的modal传入父组件,再由父组件进行子modal的挑选,这样即使有很多modal在同一个页面中,也是由父组件作为中转人触发,虽然本质上的原理是一样的,只是加了一层媒介,但这样可以让分工更为明确。
GrandParentComponent.html

<parent [modal_info_parent]="modal_info_grand" [modal_name_parent]="modal_name_grand"></parent>

ParentComponent.html

<modal-one [modal_info_child]="modal_info_parent"
    *ngIf="modal_name_parent ==='modal_child_one'"></modal-one>
    
<modal-two [modal_info_child]="modal_info_parent"
    *ngIf="modal_name_parent ==='modal_child_two'"></modal-two>
    
<modal-three [modal_info_child]="modal_info_parent"
    *ngIf="modal_name_parent ==='modal_child_threee'"></modal-three>

这样的技巧将每个modal分割开来,并将筛选modal的功能通过input属性从祖父组件传递到父组件,是父组件成为了一个单纯的变量传递者。到了这一步,其实也有一个问题:这样做是否太过与小题大作,因为父组件的额外添加,意味着变量的传递过程从父-子-父变成了祖父-父-子-父-祖父,为了完成这样的数据量可能要多些一大堆EventEmitter去发射数据。就现在看来,这种做法确实存在很大的冗余,但这种结构的用武之地并不是这样简单的组件关系

3.递归组件的modal处理

angular4中修复了一个关于ngFor循环空数据而可能出现的死循环bug,这对于递归组件是一个好消息
组件的递归是指在组件中再次调用本身,这种需求可能出现的场景,最典型就是树状数据结构的循环显示。

假设我们需要显示一个树状结构的可展开式的带缩进下拉列表,由于每一个节点的ui本质上都是一样的,所以使用同一个component不停的递归调用自己是非常方便的,这时候我们我们加了一个需求,每一个节点都可以点击,弹出一个modal,用于修改该节点的信息,这个时候问题就来了,modal的应该写在哪,modal中该节点的数据从哪来。
笔者一开始的想法,直接把modal写在递归的component不就得了,但是很快发现了问题。
ParentComponent.html

<branch-data [tree]="parent_data"></branch-data>

ChildComponent.html

<div *ngFor="let child of tree">
    <div>......</div>
    <div>......</div>
    <branch-data [tree]="child"></branch-data>
</div>

这是一个非常典型的angular4中的树形结构数据递归显示的样例代码
由于modal是点击节点UI是触发的---也就是branch-data的(click)事件,那modal对应标签的位置就很值得思考
既可以放在节点的组件中----这样可以很好的避免树结构复杂的父子关系导致的output/intput方法失效,
或者不去管触发modal的节点,而是将modal的显示统一放在根节点上进行控制

无论哪一种方式,都涉及到另一种组件间传值的方式----service
service的好处在于可以专注于数据的储存于传递,而不用在意数据的输入者输出者是什么关系。
service是一个典型的单例模式,一个单向绑定的单例(但是在一些特殊格式的存储上,服务也表相出了一些近似于双向绑定的表现,具体原因笔者还在研究)。可以通过service保存控制modal显示消失的变量,并通过EventEmitter的发射将数据发射至父组件,从而实现数据的联通

Modal.Service.ts

export class ModalServcie {
    private modal_emitter = new EventEmitter();
    constructor() {
    }
    getModalEmitter() {
        return this.modal_emitter;
    }
    emitModalName(modal_name) {
        this.modal_emitter.emit(modal_name);
    }
}

ModalComponent.ts

export class ModalComponent implements OnInit {
    @Input()
    modal_name;
    constructor(private modalService: ModalService) {
    }
    hideModal() {
        this.modalService.emitModalName('');
    }
}

ParentComponent.ts

export class ParentComponent implements OnInit, OnDestroy {
    private modal_emitter;
    private modal_name = '';
    
    constructor(private modalService: ModalService) {
        this.modal_emitter = this.modalService.getModalEmitter()
            .subscribe(data => {
                this.modal_name = data;
            });
    }
    
    ngOnInit() {
    }
    
    ngOnDestroy() {
        this.modal_emitter.unsubscribe();
    }
    showModal() {
        this.modal_name = 'child_name';
    }
}

ParentComponent.html

<modal [modal_name]='modal_name'></modal>

modalComponent.html

<child-modal [modal_name]='modal_name' *ngIf="modal_name === 'child_modal'"></child-modal>

上面的代码依靠服务,实现了一个三级子孙关系的数据传递
通过input属性,将控制modal显示与消失的modal_name变量出入控制所有modal的组件ModalComponent,再穿入childModal,这个数据传递可以控制显示行为。
而当需要modal消失时,只需要在childModal中发射ModalService中的emit函数,parent就可以接受到modal_name的新值,从而控制modal的消失

这种实现方法有如下好处

  1. 可以将项目中的所有modal放入modalComponent进行控制,只需控制modalComponent的input,就可以使child-modal获得其想要的值,便于modal的统一管理

  2. 虽然modalComponent中可能会同时放置10个,20个甚至更多modal,但是被ngIf掉的modal是会被销毁掉的,所以真正占用内存的只有ngIf为true的那个modal,这很巧妙地避免了内存泄露问题

  3. 这种方法对于避免复杂的父子关系,子孙关系非常有效,忽视数据输入者与输出者的关系,而只专注于输入输出的对象,解决了普通的output面对复杂层级关系时的无力

但这个方法也有一些需要斟酌的地方

  1. 由于这种实现完全依赖于EventEmitter,所以在订阅了服务的EventEmitter后,务必,务必,务必记得在OnDestroy中将其注销。因为非Output渠道使用的EventEmitter是不会随着Component的OnDestroy中自动注销的,一定要手动把他解除订阅才行。否则在频繁的切换,初始化组件后,发生内存泄漏是百分之百的事情

  2. 对于EventEmitter这种用法,一个非常合适的例子是当你订阅的是一个http流,在有搜索,筛选,排序之类的功能时,我们需要频繁的向后台发送请求,并且每次可能都会带有不同的参数,这时使用订阅就是一个很好的选择,需要发送请求时,只需更新服务中的Observable,然后重新执行emit函数,那你之前订阅的EventEmitter就会根据需求执行诸如流的subscribe等方法,省去了很多重复的代码

this.http_emitter = this.yourService.getEmitter()
    .subscribe(data => {
        // 此时的data是你http请求流
        data.subscribe(data1 => {
        // 此时的data1是http请求成功后返回的数据
        });
    })

上面这种更新数据的方法,如果你没有在OnDestroy中注销EventEmitter的订阅的话,每次切换组件后再发射http的请求,你都会发现,单次请求实际触发的http数量在不断增加
这是因为你没有注销掉的http_emiter实际上也收到了emit函数的发射要求,所以会导致你的内存可能存储了很多很多http_emitter,而他们会在你执行emit函数时同时发出http请求,后果就不用多说,占用大量服务器流量

好了,这次的总结分享就这么多了,希望对看文章的各位能有所帮助

也期待大家可以多多给予指点~多多交流~~~

ps:代码部分是纯手打的。。如果有什么错误还请大家指出并见谅。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Angular ,服务(Service)是用于封装可重用功能和数据的类。它被设计为在组件之间共享数据和逻辑的间层。以下是在 Angular 使用服务的一般步骤: 1. 创建服务: - 使用 Angular CLI 命令行工具生成一个新的服务:`ng generate service my-service`。 - 或者手动创建一个新的 TypeScript 类,并添加 `@Injectable()` 装饰器。 2. 在组件使用服务: - 在组件类导入服务类:`import { MyService } from './my-service.service';`。 - 在组件的构造函数注入服务:`constructor(private myService: MyService) { }`。 - 然后就可以在组件使用 `myService` 实例来调用服务提供的方法或访问数据。 下面是一个简单的示例: ```typescript import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' // 在根级别提供该服务 }) export class MyService { private data: string = 'Hello from service!'; getData(): string { return this.data; } setData(newData: string): void { this.data = newData; } } ``` ```typescript import { Component } from '@angular/core'; import { MyService } from './my-service.service'; @Component({ selector: 'app-my-component', template: ` <p>{{ message }}</p> <button (click)="updateMessage()">Update Message</button> ` }) export class MyComponent { message: string; constructor(private myService: MyService) { this.message = myService.getData(); } updateMessage(): void { this.myService.setData('New message from component!'); this.message = this.myService.getData(); } } ``` 在上面的示例,我们创建了一个名为 `MyService` 的服务类,其包含了一个私有数据 `data` 和两个公共方法 `getData()` 和 `setData()`。然后在组件类 `MyComponent` 注入了 `MyService` 服务,并通过调用服务的方法来获取和更新数据。 请注意,为了使服务可以在整个应用程序共享,我们使用了 `providedIn: 'root'` 将服务提供在根级别。这样,Angular 就会在需要时自动创建和管理服务的单一实例。 希望这个简单的例子能帮助你理解 Angular 如何使用服务。在实际开发,服务常常用于共享数据、处理业务逻辑、与后端 API 进行通信等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值