HeRunBin's blog


  • 首页

  • 标签

  • 归档

  • Search

Angular实现元素双击可编辑

发表于 2018-02-28 | 更新于: 2018-09-26
字数统计: 764 | 阅读时长 ≈ 4

一.概述

如果希望实现一个DOM元素,双击可编辑,失去焦点不可编辑,常用的方式,是绑定dblclick事件,当事件触发,将文本内容替换为表单元素,失去焦点,再替换为文本,这种做法固然简单,但如果要操作的dom元素太多,既要添加表单元素,还要监听dblclick、blur事件,代码不可避免会变得非常繁琐

所以我采取的做法是,抽象出一个指令,将指令注册为表单控件,实现双向绑定,结合DOM的contentEditable属性,只要在DOM元素上添加该指令即可具备该能力

使用

1
<div appCanEdit [(ngModel)]="item.value"></div>

效果

二.实现步骤

1. 创建指令,实现 ControlValueAccessor接口

ControlValueAccessor是一个连接表单模型和视图(DOM元素)的接口,自定义的表单控件必须实现这个接口,它的作用是把 form 模型中值映射到视图中,当视图发生变化时,通知 form directives 或 form controls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Directive({
selector: '[appCanEdit]'
})
export class ElementCanEditDirective implements ControlValueAccessor {

_value;
// callback function
propagateChange = (value: any) => { };
// Writes a new value to the element ,temporarily store the value in '_value'
writeValue(value: any) {
if (value) {
this._value = value;
}
}
// when the control's value changes in the UI, call the callback function
registerOnChange(fn: any) {
this.propagateChange = fn;
}

registerOnTouched(fn: any) { }

constructor(
) { }

}

2. 注册成为表单控件,将控件本身注册到DI框架成为一个可以让表单访问其值的控件

1
2
3
4
5
6
7
8
9
10
@Directive({
selector: '[appCanEdit]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ElementCanEditDirective),
multi: true
}
],
})

3. 通过@HostListener 为组件添加事件绑定,通过@HostBinding实现元素innerText随value而变化

当元素处于可编辑状态时,添加content-editable类样式,以便灵活控制样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@HostBinding() get innerText() {
return this._value;
}

@HostListener('blur', ['$event.target'])
onBlur(ele: HTMLElement) {
ele.contentEditable = 'false';
this._value = ele.innerText;
this.propagateChange(this._value);
this.render.removeClass(ele, 'content-editable');
}

@HostListener('dblclick', ['$event.target'])
dbClick(ele: HTMLElement) {
// plaintext-only
ele.contentEditable = 'true';
ele.innerText = ele.innerText;
ele.focus();
this.render.addClass(ele, 'content-editable');
}

三.完整代码

1
<div appCanEdit [(ngModel)]="item.value"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { Component, OnInit, Input, EventEmitter, Directive, HostListener, HostBinding, Renderer2, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

/**
* 让DOM元素可编辑
* 可在元素上使用 ngModel
* @export
* @class ElementCanEditDirective
* @implements {ControlValueAccessor}
*/
@Directive({
selector: '[appCanEdit]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ElementCanEditDirective),
multi: true
}
],
})
export class ElementCanEditDirective implements ControlValueAccessor {

_value;
// callback function
propagateChange = (value: any) => { };
// Writes a new value to the element ,temporarily store the value in '_value'
writeValue(value: any) {
if (value) {
this._value = value;
}
}
// when the control's value changes in the UI, call the callback function
registerOnChange(fn: any) {
this.propagateChange = fn;
}

registerOnTouched(fn: any) { }

@HostBinding() get innerText() {
return this._value;
}

@HostListener('blur', ['$event.target'])
onBlur(ele: HTMLElement) {
ele.contentEditable = 'false';
this._value = ele.innerText;
this.propagateChange(this._value);
this.render.removeClass(ele, 'content-editable');
}

@HostListener('dblclick', ['$event.target'])
dbClick(ele: HTMLElement) {
ele.contentEditable = 'true';
ele.innerText = ele.innerText;
ele.focus();
this.render.addClass(ele, 'content-editable');
}

constructor(
private render: Renderer2
) { }

}

AngularJs到Angular的变化

发表于 2017-10-28 | 更新于: 2018-09-19
字数统计: 341 | 阅读时长 ≈ 1

概述

关于AngularJs、Angular 一脉相承的思路与不同之处

以下只是个人使用过程中的的一些感受,具体二者之间的差别参考 [官方文档](https://angular.cn/guide/ajs-quick-reference) 

细节

  1. 引入脚手架,提供Typescript es6超集 , 支持 import export ,不需额外引入模块加载器

  2. 将组件和指令二者剥离开来,前者组成页面,后者分为结构性指令和功能性指令, 不再支持replace等

  3. 移除controller, $scope等

  4. provider,service,factory 统一为 service, 减少理解上的困惑

  5. angularJs 只有一个注入器,所有服务都是单例,Angular的注入器则是一个树状结构,可以为一个注入对象实现不同的实例(在不同的注入器上)

  6. controller <=> component 一定意义上等价,都封装了model实体模型,指定html模板,业务逻辑,也都挂靠在模块上

  7. 在setTimeout等异步执行环境中不再需要手动调用$scope.$apply() , angular的代码都运行在 zone中,通过为异步方法打补丁的方式,在方法执行完后自动的执行数据检查,如果使用第三方类库,存在异步操作,
    想要纳入Angular管理,可以通过以下方式实现

1
2
3
this.ngZone.run(() => {
this.service.uploadEvent.emit(data);
});
12
何润斌

何润斌

12 日志
9 标签
GitHub E-Mail
© 2019 何润斌 | Site words total count: 11.8k