[Angular 基础] - 自定义事件 自定义属性

[Angular 基础] - 自定义事件 & 自定义属性


之前的笔记:

  • [Angular 基础] - Angular 渲染过程 & 组件的创建

  • [Angular 基础] - 数据绑定(databinding)

  • [Angular 基础] - 指令(directives)

以上是能够实现渲染静态页面的基础


之前的内容主要学习了怎么通过绑定原生 HTML(style, class, click 等) 和 Angular(ngFor, (click), {{ string interpolation }} 等) 的事件和属性动态渲染静态页面,这里开始讲组件沟通之间的部分,让页面开始真正的动起来

也就是 组件(component)指令(directives) 的进阶学习

设置项目

目前项目的结构如下:

src/app/
├── app.component.css
├── app.component.html
├── app.component.ts
├── app.module.ts
├── cockpit
│   ├── cockpit.component.css
│   ├── cockpit.component.html
│   └── cockpit.component.ts
└── server-element├── server-element.component.css├── server-element.component.html└── server-element.component.ts3 directories, 10 files

app

其中最基层的 app 的作用是存储一个 serverList,并且使用 serverList 去渲染对应的 cockpitserver-element,具体文件如下:

  • VM 层

    import { Component } from '@angular/core';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css'],
    })
    export class AppComponent {serverElements = [];
    }
    
  • V 层

    <div class="container"><app-cockpit></app-cockpit><hr /><div class="row"><div class="col-xs-12"><app-server-element*ngFor="let element of serverElements"></app-server-element></div></div>
    </div>
    

    这里就会开始涉及组件之间的沟通:

    • cockpit 会创建一个 server,并且将数据添加到 serverElements
    • server-element 会接受 element,也就是 for 循环里的元素

cockpit

有些无关紧要的说明:

駕駛艙(英語:Cockpit),是飞行员控制飛機的座艙,通常位於一架飛機的前端。除了早期的部分飛機,如今大部分飛機的駕駛艙采用密閉式的設計。

这里命名为 cockpit 大概是因为一个 server 既可以是 server,也可以是一个 blueprint。这个不用细究 class/object 的区别,主要还是自定义事件和属性方面的问题

  • VM 层

    import { Component } from '@angular/core';@Component({selector: 'app-cockpit',templateUrl: './cockpit.component.html',styleUrl: './cockpit.component.css',
    })
    export class CockpitComponent {newServerName = '';newServerContent = '';onAddServer() {}onAddBlueprint() {
    }
    
  • V 层

    <div class="row"><div class="col-xs-12"><p>Add new Servers or blueprints!</p><label>Server Name</label><input type="text" class="form-control" [(ngModel)]="newServerName" /><label>Server Content</label><input type="text" class="form-control" [(ngModel)]="newServerContent" /><br /><div class="btn-toolbar"><button class="btn btn-primary" (click)="onAddServer()">Add Server</button><button class="btn btn-primary" (click)="onAddBlueprint()">Add Server Blueprint</button></div></div>
    </div>
    

server-element

这里会接受一个 server,并且将其渲染到页面上

  • VM 层

    import { Component } from '@angular/core';@Component({selector: 'app-server-element',templateUrl: './server-element.component.html',styleUrl: './server-element.component.css',
    })
    export class ServerElementComponent {}
    
  • V 层

    <div class="panel panel-default"><div class="panel-heading">{{ element.name }}</div><div class="panel-body"><p><strong *ngIf="element.type === 'server'" style="color: red">{{ element.content }}</strong><em *ngIf="element.type === 'blueprint'">{{ element.content }}</em></p></div>
    </div>
    

此时因为组件之间的交流还没有完成,所以代码运行肯定会失败的,不过最基础的是已经完成了

绑定自定义属性

首先是从渲染 server-listserver-element 开始,所以需要将 cockpit 内的东西注释掉,以防报错

如果不会报错的话则可以忽略,我后面又做了点修改……

model

先新建一个 server-element 的 model 让其他文件引用,我改了下结构,现在 model 在这里:

❯ tree src/app/
src/app/
├── model
│   └── server-element.model.ts

内容如下:

export class ServerElement {constructor(public name: string,public type: 'server' | 'blueprint',public content: string) {}
}

app VM 层

这里主要就是在数组里放一个数据,新增代码如下:

export class AppComponent {serverElements: ServerElement[] = [{ type: 'server', name: 'Testserver', content: 'Just a test!' },];
}

app V 层

这里会更新一下代码,绑定 自定义属性 element

<div class="container"><app-cockpit></app-cockpit><hr /><div class="row"><div class="col-xs-12"><app-server-element*ngFor="let serverElement of serverElements"[element]="serverElement"></app-server-element></div></div>
</div>

其中 [element]="serverElement" 就是新增的代码,也就是绑定的 自定义属性

server-element V 层

这里是选择接受参数的地方,已经从上面的 V 层知道传进来的自定义属性是 element,因此这里就用 element 作为变量名:

<div class="panel panel-default"><div class="panel-heading">{{ element.name }}</div><div class="panel-body"><p><strong *ngIf="element.type === 'server'" style="color: red">{{ element.content }}</strong><em *ngIf="element.type === 'blueprint'">{{ element.content }}</em></p></div>
</div>

server-element VM 层

VM 层是掌管数据的地方,因此 VM 层还需要声明一下 element 的存在:

import { Component } from '@angular/core';
import { ServerElement } from '../model/server-element.model';@Component({selector: 'app-server-element',templateUrl: './server-element.component.html',styleUrl: './server-element.component.css',
})
export class ServerElementComponent {// 不做类型声明也不会报错,但是会有简易element: ServerElement;
}

这时候效果如下:

在这里插入图片描述

Angular 渲染了一个元素,但是这个元素是空的,这个原因是因为 scoping 的问题,element 本质上还是只对父组件——即 app 组件——可见,如果想让它在子组件里也能被访问到,需要用一个新的装饰器:@Input(),修改如下:

export class ServerElementComponent {@Input() element: ServerElement;
}

随后即可正常渲染:

在这里插入图片描述

⚠️:Input 需要从 @angular/core 中导入

自定义属性的 alias

有的时候会想要设置 alias,而非使用传递过来的变量名——比如说可能父元素会创建一个事件然后传递 event 到子元素中,子元素则可以根据需求去重命名这是一个 mouseEvent, inputEvent, formEvent 或是其他,修改方法如下:

export class ServerElementComponent {// () 内的才是父组件里使用的变量名@Input('element') aliasElement: ServerElement;
}

这个时候,对于当前组件来说,可访问的变量为 aliasElement,因此 V 层也需要进行对应的修改:

<div class="panel panel-default"><div class="panel-heading">{{ aliasElement.name }}</div><div class="panel-body"><p><strong *ngIf="aliasElement.type === 'server'" style="color: red">{{ aliasElement.content }}</strong><em *ngIf="aliasElement.type === 'blueprint'">{{ aliasElement.content }}</em></p></div>
</div>

绑定自定义事件

这个时候需要将 cockpit 里的代码还原

这里同样需要注意的一点是数据的传输方向,在父组件中,只有 serverElements 被声明了,具体的添加事件是发生在子组件中的,也就是说,事件的传输方向并不是由父组件向子组件进行传输,而是从子组件传递到父组件。准确的说也不是传送,而是发送(emit 🚀)。和 React 相反,Angular 的事件通常情况下是从子组件发送到父组件,父组件通过监听事件进行对应的处理

其实这个处理大方向和上面绑定自定义属性差不多,最大的差别就是 flow

cockpit VM 层

实现如下:

export class CockpitComponent {@Output() serverCreated = new EventEmitter<Omit<ServerElement, 'type'>>();@Output() blueprintCreated = new EventEmitter<Omit<ServerElement, 'type'>>();newServerName = '';newServerContent = '';onAddServer() {this.serverCreated.emit({name: this.newServerName,content: this.newServerContent,});}onAddBlueprint() {this.blueprintCreated.emit({name: this.newServerName,content: this.newServerContent,});}
}

⚠️:这里的 Output 同样需要从 angular-core 导入

👀:注意这里的语法,这是一个 EventEmitter,并且类型是 Output。这也说明了事件的方向是自下而上,而非自上而下——对比 React,React 将 event handler 从上往下传,并在子元素进行调用

cockpit V 层

保持不变

app VM 层

变动如下

export class AppComponent {serverElements: ServerElement[] = [{ type: 'server', name: 'Testserver', content: 'Just a test!' },];serverData: ServerElement;onServerAdded(serverData: Omit<ServerElement, 'type'>) {this.serverElements.push({type: 'server',name: serverData.name,content: serverData.content,});}onBlueprintAdded(blueprintData: Omit<ServerElement, 'type'>) {this.serverElements.push({type: 'blueprint',name: blueprintData.name,content: blueprintData.content,});}
}

⚠️:Omit 是 TypeScript 的语法,详细的使用方法可以查看官方文档:Utility Types

app V 层

变动如下:

<div class="container"><app-cockpit(serverCreated)="onServerAdded($event)"(blueprintCreated)="onBlueprintAdded($event)"></app-cockpit><hr /><div class="row"><div class="col-xs-12"><app-server-element*ngFor="let serverElement of serverElements"[element]="serverElement"></app-server-element></div></div>
</div>

实现后效果如下:

在这里插入图片描述

自定义事件的 alias

这个和自定义属性的方式实现的也差不多:

import { Component, EventEmitter, Output } from '@angular/core';
import { ServerElement } from '../model/server-element.model';@Component({selector: 'app-cockpit',templateUrl: './cockpit.component.html',styleUrl: './cockpit.component.css',
})
export class CockpitComponent {@Output('serverCreated') svCreated = new EventEmitter<Omit<ServerElement, 'type'>>();@Output('blueprintCreated') bpCreated = new EventEmitter<Omit<ServerElement, 'type'>>();newServerName = '';newServerContent = '';onAddServer() {this.svCreated.emit({name: this.newServerName,content: this.newServerContent,});}onAddBlueprint() {this.bpCreated.emit({name: this.newServerName,content: this.newServerContent,});}
}

同样是 () 内的代表外部的变量名,而声明的则是组件内部可用的名称


到这里就实现了数据和事件的跨组件交流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/257535.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目访问量激增该如何应对

✨✨ 欢迎大家来到喔的嘛呀的博客✨✨ &#x1f388;&#x1f388;希望这篇博客对大家能有帮助&#x1f388;&#x1f388; 目录 引言 一. 优化数据库 1.1 索引优化 1.2 查询优化 1.3 数据库设计优化 1.4 事务优化 1.5 硬件优化 1.6 数据库配置优化 二. 增加服务器资源…

收藏:不错的讲座《拆解成功领导者的三重底层思维逻辑》

在B 站看到个不错的讲座《拆解成功领导者的三重底层思维逻辑》&#xff0c;地址&#xff1a;第145期-拆解成功领导者的三重底层思维逻辑_哔哩哔哩_bilibili 演讲内容文章摘要在这里&#xff1a;《直播精华 | 拆解成功领导者的思维逻辑》&#xff08;直播精华 | 拆解成功领导者的…

c语言操作符(上)

目录 ​编辑 原码、反码、补码 1、正数 2、负数 3、二进制计算1-1 移位操作符 1、<<左移操作符 2、>>右移操作符 位操作符&、|、^、~ 1、&按位与 2、|按位或 3、^按位异或 特点 4、~按位取反 原码、反码、补码 1、正数 原码 反码 补码相同…

专业140+总分420+浙江大学842信号系统与数字电路考研经验电子信息与通信,真题,大纲,参考书。

今年考研已经结束&#xff0c;初试专业课842信号系统与数字电路140&#xff0c;总分420&#xff0c;很幸运实现了自己的目标&#xff0c;被浙大录取&#xff0c;这在高考是想都不敢想的学校&#xff0c;在考研时实现了&#xff0c;所以大家也要有信心&#xff0c;通过自己努力实…

【c语言】字符串常见函数 下

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;c语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&a…

CVE-2023-41892 漏洞复现

CVE-2023-41892 开题&#xff0c;是一个RCE Thanks for installing Craft CMS! You’re looking at the index.twig template file located in your templates/ folder. Once you’re ready to start building out your site’s front end, you can replace this with someth…

【Web】CVE-2021-31805 s2-062漏洞复现学习

目录 Struts2介绍 漏洞概况 OGNL与Struts2 简单原理 漏洞复现 正向rce 反弹shell payload分析 Struts2介绍 Struts 2 是一个流行的用于构建 Java Web 应用程序的开源 Web 应用程序框架。它是 Apache 软件基金会下的一个顶级项目&#xff0c;是 Struts 框架的升级版本。…

LabVIEW智能温度监控系统

LabVIEW智能温度监控系统 介绍了一个基于LabVIEW的智能温度监控系统&#xff0c;实现对工业环境中温度的实时监控与调控。通过集成传感器技术和LabVIEW软件平台&#xff0c;系统能够自动检测环境温度&#xff0c;及时响应温度变化&#xff0c;并通过图形用户界面(GUI)为用户提…

CFS三层靶机

参考博客&#xff1a; CFS三层内网靶场渗透记录【详细指南】 - FreeBuf网络安全行业门户 CFS三层靶机搭建及其内网渗透【附靶场环境】 | TeamsSix CFS三层网络环境靶场实战 - PANDA墨森 - 博客园 (cnblogs.com) CFS三层靶机实战--内网横向渗透 - 知乎 (zhihu.com) CFS靶机…

C语言——枚举类型

&#x1f4dd;前言&#xff1a; 在之前的文章中我们已经讲解了自定义类型中的结构体类型和联合体类型&#xff0c;现在我们再充分学习一下C语言中的枚举类型&#xff1a; 1&#xff0c;什么是枚举类型 2&#xff0c;枚举类型的定义和变量的声明 3&#xff0c;对变量进行赋值 &a…

【C++】类和对象(四)

前言&#xff1a;在类和对象中&#xff0c;我们走过了十分漫长的道路&#xff0c;今天我们将进一步学习类和对象&#xff0c;类和对象这块荆棘地很长&#xff0c;各位一起加油呀。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质量&a…

docker (一)-简介

1.什么是docker Docker 是一个开源的应用容器引擎&#xff0c;由于docker影响巨大&#xff0c;今天也用"Docker" 指代容器化技术。 2.docker的优势 一键部署&#xff0c;开箱即用 容器使用基于image镜像的部署模式&#xff0c;image中包含了运行应用程序所需的一…

auto关键字详讲

目录 1.问题思考 2.auto关键字介绍 3. 早期auto的缺陷&#xff1a; 4.什么叫自动存储器&#xff1f; 5. c标准auto关键字 5.1auto的使用细节 5.2 auto什么时候不能推导变量的类型呢&#xff1f; 5.3基于范围的for循环 5.3.1范围for的用法 5.3.2 范围for的使用条件 6.…

软考27-上午题-查找

一、基本概念 1-1、查找表&#xff1a; 同一类型的数据元素构成的集合。 对查找表常用的操作&#xff1a; 从查找表中查询某个特定的元素&#xff1b;检索某个特定的元素的各种属性。 通常只进行这两种操作的查找表&#xff1a;静态查找表 1-1-2、静态查找表&#xff1a; 顺…

快速搭建PyTorch环境:Miniconda一步到位

快速搭建PyTorch环境&#xff1a;Miniconda一步到位 &#x1f335;文章目录&#x1f335; &#x1f333;一、为何选择Miniconda搭建PyTorch环境&#xff1f;&#x1f333;&#x1f333;二、Miniconda安装指南&#xff1a;轻松上手&#x1f333;&#x1f333;三、PyTorch与Minic…

新年开始更新自己!八大出生缺陷惠民项目!漫漫回程路——“早”读

时间不等人呢&#xff01; 引言代码第一篇 人民日报 【夜读】新的一年&#xff0c;从更新自己开始第二篇&#xff08;跳&#xff09;人民日报 来啦 新闻早班车要闻社会政策 结尾 引言 天还是那个天 但是今天是一个不一样的日子 是我爷爷的忌日 所以按习俗 我们早早就开始拜 然后…

机器学习:Softmax介绍及代码实现

Softmax原理 Softmax函数用于将分类结果归一化&#xff0c;形成一个概率分布。作用类似于二分类中的Sigmoid函数。 对于一个k维向量z&#xff0c;我们想把这个结果转换为一个k个类别的概率分布p(z)。softmax可以用于实现上述结果&#xff0c;具体计算公式为&#xff1a; 对于…

Spring 用法学习总结(三)之 AOP

Spring学习 7 bean的生命周期8 AOP面向切面编程8.1 AOP相关术语8.2 AOP使用 7 bean的生命周期 bean的生命周期主要为bean实例化、bean属性赋值、bean初始化、销毁bean&#xff0c;其中在实例化和初始化前后都使用后置处理器方法&#xff0c;而InstantiationAwareBeanPostProce…

浅析Linux内核线程监测机制:Hung Task

文章目录 概述Hung Task配置Hung Task机制初始化Hung Task监测线程 相关参考 概述 Hung Task机制周期性地监测系统中处于TASK_UNINTERRUPTIBLE状态&#xff08;即D状态&#xff09;的进程&#xff0c;如果超过120s&#xff08;时间可配&#xff09;&#xff0c;进程状态还没有…

Ambiguous Medical Image Segmentation using Diffusion Models利用扩散模型分割模糊医学图像

摘要&#xff1a; 事实证明&#xff0c;在临床任务中&#xff0c;来自一组专家的集体见解总是优于个人的最佳诊断。对于医学图像分割任务&#xff0c;现有的基于人工智能的替代研究更多地侧重于开发能够模仿最佳个体的模型&#xff0c;而不是利用专家组的力量。 在本文中&…