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

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

上篇笔记,关于 Angular 的渲染过程及组件的创建&简单学习:[Angular 基础] - Angular 渲染过程 & 组件的创建

Angular 之中的 databinding 是一个相对而言更加复杂,以及我个人觉得相对而言比较灵活的部分------较之 React 的单项数据流而言,Angular 是可以实现双重绑定的:

对于 React 来说,则是需要调用从 ViewModel 中传给 View 层的事件,随后 ViewModel 更新数据,再传递到 View 层,总体上来说 React 的代码更加的可靠(因为数据/事件的流动是单一的),但是也会碰到情况------如嵌套较深时,事件的触发与数据的更新就会产生比较麻烦的情况

这也是二者对于事件和数据处理的不同之处

本篇笔记会对 Angular 的数据绑定进行更加深入地学习

数据传输

即 ViewModel 层将数据传输给 View 层,这里主要学习两种方式:字符串插值(string interpolation) 和 属性绑定(property binding)

string interpolation

string interpolation 是一种比较方便的将数据从 ViewModel 传到 View 层的方法,只需要在中组件中声明对应的变量/方法,并且在 HTML Template 中调用即可。用法如下:

  1. 在组件中声明变量/方法

    ts 复制代码
    export class ServerComponent {
      serverId = 10;
      serverStatus = 'offline';
    
      getServerStatus() {
        return this.serverStatus;
      }
    }
  2. 使用 {``{ var/method() }} 的方式调用

    html 复制代码
    <p>{{ "Server" }} with ID {{ serverId }} is {{ getServerStatus() }}</p>

    ⚠️:var/method() 为一个表达式

效果为:

缺点在于:

  • 返回值必须是字符串

    如果是 primitive type 那么问题不大,数字、布尔值都是可以直接转成字符串,因此正常渲染

    如果是对象的话,则会调用默认的 toString 方法,对于很多没有重写 toString 方法的对象/类来说,则是不可读的 object

  • 代码无法非常复杂

    如果需要写表达式,那么有一个 一行 的限制

    换句话说三元式可用,if/else 不可用

  • 无法赋值或创建新的变量

    以面用的例子来说 {``{ serverId = 20 }} 是会直接报错的:

  • 调用的函数不能有副作用(side effect)

    换言之,只能调用 getter,不能调用 setter

  • 安全性问题

    像 React 一样,Angular 也会清理从 ViewModel 传向 View 层的数据

    但是如果同时使用 string interpolation 和 bypassSecurityTrust,那么当前代码就不会被清洗,如果中间有一些比较危险的代码,那么就会引起安全性的问题

    举例说明就是,如果当前应用有一个功能是去渲染用户之前留下的 comment,这里决定使用 string interpolation 去渲染用户的留言,而开发者假设用户的数据一定是干净的(后段已经进行过处理),所以决定使用 bypassSecurityTrust

    后端也是这么觉得的,因此并没有清理用户数据

    用户的数据里包含了攻击代码------如窃取当前网页中的 JWT token,自动在后台运行来自其他域名的攻击脚本等

    那么就会引发这个安全性的问题,这个情况类似于 React 中直接食用 dangerouslySetInnerHTML

property binding

string interpolation 相当于是在页面上渲染一段文字,有的时候则需要更加动态的控制 DOM 元素的属性,比较常见的案例有,在发送了验证短信后一分钟内按钮呈现 disabled 的状态,或是大部分 input 元素中的 value 等,这些都无法使用 string interpolation 来解决,还是需要使用另一个不同的语法,也就是 property binding,用法如下:

  1. 依旧在 ___.component.ts 中声明对应的变量

    ts 复制代码
    export class ServersComponent {
      allowNewServer = false;
    
      constructor() {
        setInterval(() => {
          console.log(
            new Date().toISOString(),
            'allowNewServer: ',
            this.allowNewServer
          );
          this.allowNewServer = !this.allowNewServer;
        }, 2000);
      }
    }

    这里的设定是每 2s 将 allowNewServer 的值翻转一下

  2. 在 HTML template 中使用 [attribute]='var/method()' 的方式调用

    html 复制代码
    <button class="btn btn-primary" [disabled]="!allowNewServer">
      Add Server
    </button>

    这里还没有新增 Add Server 的功能,这里主要是看 disable 的状态

    ⚠️:var/method() 为一个表达式

效果为:

本质上来说,如果只是渲染一段文字的话,使用 string interpolation 会比较方便,如果是要绑定属性的话,则是使用 property binding,原因是二者没法互用,如下面的例子:

html 复制代码
<p>{{ allowNewServer }}</p>
<p [innerText]="allowNewServer"></p>

的效果是一样的,但是混用就会报错:

事件绑定

数据从 View 层传输到 ViewModel 层,其绑定的方式与 property binding 相似:

  • VM 层实现一个事件

    ts 复制代码
    export class ServersComponent {
      serverCreationStatus = 'No server was created!';
    
      onCreateServer() {
        this.serverCreationStatus = 'Server was created!';
      }
    }
  • V 层绑定该事件

    html 复制代码
    <button
      class="btn btn-primary"
      [disabled]="!allowNewServer"
      (click)="onCreateServer()"
    >
      Add Server
    </button>

效果:

传递 event 对象

这里需要对 event 事件对象进行绑定,View 层修改如下:

html 复制代码
<label for="server-name">Server Name: {{ serverName }}</label>
<input
  type="text"
  class="form-control"
  id="server-name"
  (input)="onUpdateServerName($event)"
/>

这时候就可以在 ViewModel 中接收到 $event 了:

ts 复制代码
  onUpdateServerName($event: Event) {
    this.serverName = (<HTMLInputElement>$event.target).value;
  }

此时的效果为:

其实到此的实现和 React 还是挺像的,V 层调用 VM 层的表达式,将事件对象传到 VM 层;VM 层处理 business logic,将修改过的代码反映到 V 层上。不过下一个双向绑定就能确实的展现 React 和 Angular 在数据传输上的区别了。

⚠️:这里变量名称使用 $event 是一个预定俗称的规则(convention),可以改成其他的名称

❗:注意这里的 value 没有通过 property binding 实现绑定,所以这里的数据显示的是 input 里的数据

双向绑定

‼️:在使用双向绑定前必须要先在 AppModule 中导入 FormsModule,如:

ts 复制代码
// 导入 FormsModule
import { FormsModule } from '@angular/forms';

@NgModule({
  // 新增 FormsModule
  imports: [BrowserModule, FormsModule],
})
export class AppModule {}

接下来就可以使用 ngModel 了,具体使用如下:

  1. 修改 V 层代码

    html 复制代码
    <input
      type="text"
      class="form-control"
      id="server-name"
      [(ngModel)]="serverName"
    />

VM 层则不需要修改,最终效果如下:

乍一看好像 2 way databinding 和之前的实现没什么区别,不过如果将二者代码同时渲染,并且修改一下 serverName 的默认值,就能看到区别了:

  1. 使用 [(ngModel)] 同时兼具了 event binding 和 property binding

    对比起来,不使用 2 way binding 想要达成以下效果则需要这样的实现:

    html 复制代码
    <input
      type="text"
      class="form-control"
      id="server-name"
      (input)="onUpdateServerName($event)"
      [value]="serverName"
    />
  2. 数据的同步方式不一样

    使用 property binding+event binding 的效果看起来和 2 way binding 一样,不过实际上它还是通过把值从 VM 层传到 V 层进行数据渲染,V 层调用 VM 层的 change handler 去实现数据变更,因此本质上它的 flow 还是 VM 层到 V 层的单方向实现

    2 way binding 则会让 VM 层监听 V 层的变化,因此当 V 层的数据变化时,VM 层的数据也会同时进行更新。而如果其他地方也有 change handler 修改了 VM 层中的数据,则 V 层也能监听到 VM 层的变化,同时更新数据

相关推荐
无责任此方_修行中1 小时前
每周见闻分享:杂谈AI取代程序员
javascript·资讯
Σίσυφος19002 小时前
halcon 条形码、二维码识别、opencv识别
前端·数据库
学代码的小前端2 小时前
0基础学前端-----CSS DAY13
前端·css
dorabighead3 小时前
JavaScript 高级程序设计 读书笔记(第三章)
开发语言·javascript·ecmascript
css趣多多3 小时前
案例自定义tabBar
前端
姑苏洛言5 小时前
DeepSeek写微信转盘小程序需求文档,这不比产品经理强?
前端
林的快手5 小时前
CSS列表属性
前端·javascript·css·ajax·firefox·html5·safari
匹马夕阳5 小时前
ECharts极简入门
前端·信息可视化·echarts
bug总结5 小时前
新学一个JavaScript 的 classList API
开发语言·javascript·ecmascript
网络安全-老纪5 小时前
网络安全-js安全知识点与XSS常用payloads
javascript·安全·web安全