【问题标题】:Angular 2 contenteditableAngular 2 内容可编辑
【发布时间】:2016-12-25 17:01:23
【问题描述】:

在 Angular 2 中,如何使用 contenteditable div 进行双向数据绑定?

<div class="editable" contenteditable="true">
    <h1>Text Field</h1>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In pharetra felis in sem porta feugiat.</p>
 </div>

【问题讨论】:

标签: html css angular


【解决方案1】:

我已经调整了 Isetty 的答案以使用 Angular 2.0 的发布版本,现在它可用。除了使用发布版本之外,我还添加了一个 keyup 事件并使用 textContent 而不是 innerText,因为这更适合我的应用程序。您可能希望更改这些内容。

import {Directive, ElementRef, Input, Output, EventEmitter, OnChanges} from "@angular/core";

@Directive({
    selector: '[contenteditableModel]',
    host: {
        '(blur)': 'onEdit()',
        '(keyup)': 'onEdit()'
    }
})

export class ContentEditableDirective implements OnChanges {
    @Input('contenteditableModel') model: any;
    @Output('contenteditableModelChange') update = new EventEmitter();

    constructor(
        private elementRef: ElementRef
    ) {
        console.log('ContentEditableDirective.constructor');
    }

    ngOnChanges(changes) {
        console.log('ContentEditableDirective.ngOnChanges');
        console.log(changes);
        if (changes.model.isFirstChange())
            this.refreshView();
    }

    onEdit() {
        console.log('ContentEditableDirective.onEdit');
        var value = this.elementRef.nativeElement.innerText
        this.update.emit(value)
    }

    private refreshView() {
        console.log('ContentEditableDirective.refreshView');
        this.elementRef.nativeElement.textContent = this.model
    }
}

【讨论】:

  • 大卫,你有没有机会得到一个工作版本的 plunker?
  • 我试图做一个与 TSLint 默认值兼容的版本,但我没有解决 no-input-rename 和 no-output-rename 的解决方案。请参阅:gist.github.com/zamber/f2d15337c245285d498d9a6b94de3117 我还添加了一些不错的东西,例如 enter 接受、esc 取消和发出全值更改,而不是每次按键。
【解决方案2】:

Angular 没有用于 contenteditable 情况的内置 ControlValueAccessor。我编写了一个很小的库,可以为 Angular 4 及更高版本正确实现它,它还可以与 Internet Explorer 11 一起使用 MutationObserver 后备:

https://www.npmjs.com/package/@tinkoff/angular-contenteditable-accessor

这是 ControlValueAccessor 必须拥有的代码:

registerOnChange(onChange: (value: string) => void) {
  ...
}

registerOnTouched(onTouched: () => void) {
  ...
}

setDisabledState(disabled: boolean) {
  ...
}

writeValue(value: string | null) {
  ...
}

请记住,您还应该注意危险的粘贴内容,因此明智的做法是在 (drop)/(paste) 事件中对其进行清理

【讨论】:

    【解决方案3】:

    请参考此代码。我想这对你有用。

    app.ts

    @Component({
        selector: 'test-component'
    })
    @View({
        directives: [ContenteditableModel]
        template: `
            <h1 contenteditable="true" [(contenteditableModel)]="someObj.someProperty"></h1>
            {{someObj | json}}
        `
    })
    export class TestCmp {
        someObj = {someProperty: "startValue"}
    }
    

    contenteditableModel.ts:

    import {Directive, ElementRef, Input, Output} from "angular2/core";
    import {EventEmitter} from "angular2/src/facade/async";
    import {OnChanges} from "angular2/core";
    import {isPropertyUpdated} from "angular2/src/common/forms/directives/shared";
    
    @Directive({
        selector: '[contenteditableModel]',
        host: {
            '(blur)': 'onBlur()'
        }
    })
    export class ContenteditableModel implements OnChanges {
        @Input('contenteditableModel') model: any;
        @Output('contenteditableModelChange') update = new EventEmitter();
    
        private lastViewModel: any;
    
    
        constructor(private elRef: ElementRef) {
        }
    
        ngOnChanges(changes) {
            if (isPropertyUpdated(changes, this.lastViewModel)) {
                this.lastViewModel = this.model
                this.refreshView()
            }
        }
    
        onBlur() {
            var value = this.elRef.nativeElement.innerText
            this.lastViewModel = value
            this.update.emit(value)
        }
    
        private refreshView() {
            this.elRef.nativeElement.innerText = this.model
        }
    }
    

    对于额外的输入,我为您找到了一个链接。 https://www.namekdev.net/2016/01/two-way-binding-to-contenteditable-element-in-angular-2/

    【讨论】:

    • 谢谢!您的代码有效,但我有两个问题:-my someObj.someProperty 包含很多 html(10-15 个标签)。在我修改某些内容后,整个 html 变为纯文本 - 编辑文本我使用文本编辑器 - Summernote 这是我的 json 对象的一部分:[ { "dim": 4, "id": 1, "moduleType": { "id": 1, "name": "text-module", "content": "

      Text Field 1

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. in pharetra felis in sem porta feugiat.

      " } },moduleType.content 就是我要编辑的内容
    【解决方案4】:

    in angular 2 [(ngModel)] 用于双向数据绑定。

    你的问题的答案已经在这里How to use [(ngModel)] on div's contenteditable in angular2? 看看这个,让我知道它是否适合你。

    【讨论】:

      【解决方案5】:

      要正常工作,需要为指令contenteditable 实现ControlValueAccessor。查看我的解决方案:ng-contenteditable

      【讨论】:

        【解决方案6】:

        具有动态可编辑功能的 Angular 4/2 (Typescript):

        // Imports
        import { Component} from '@angular/core';
        
        @Component({
            selector: 'discussion',
            template: `                                 
            <div class="details">
                <p class="time">Wednesday 14 Nov, 2016 10.13PM</p>
                <p class="text" name="discussion" 
                    [contentEditable]="editable" 
                    [ngClass]="{ 'editable': editable }" 
                    (blur)="uDiscussion()" 
                    (click)="eDiscussion($event)" 
                    (input)="discussion = $event.target.innerText" 
                >{{ discussion }}</p>
            </div>
            <div class="dropdown">
                <a href="#" data-toggle="dropdown" class="dropdown-toggle">
                    <i class="fa fa-ellipsis-v"></i>
                </a>
                <ul class="dropdown-menu">
                    <li><a  (click)="eDiscussion($event)" >Edit</a></li>
                    <li><a  (click)="dDiscussion()" >Delete</a></li>
                </ul>
            </div>`,
            styles: [`.editable {
                white-space: pre-wrap;
                border: 1px solid coral;
                width: 200px;
                min-height: 20px;
            }`]
        })
        
        export class DiscussionComponent {
        
            constructor(){}
        
            public discussion: string = "Test string";
            public editable: boolean = false;
        
            dDiscussion(){
                console.log("delete");
            }
        
            eDiscussion(event: any){
                // on click this will set 'contentEditable' to true 
                // and add 'editable' class for styling.
                this.editable = true;
            }
        
            uDiscussion(event: any){
                // on blur set 'contentEditable' to false 
                // and remove class 'editable' and log new values
                this.editable = false;
                console.log("this.discussion");
                console.log(this.discussion);
            }
        
        }
        

        【讨论】:

          【解决方案7】:

          blur 事件和 innerHTML 属性。

          在 .ts 中:

          getContent(innerText){
            this.content = innerText;
          }
          

          在 .html 中:

          <div (blur)="getContent($event.target.innerHTML)" contenteditable [innerHTML]="content"></div>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-01-16
            • 1970-01-01
            • 2017-09-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-02-05
            相关资源
            最近更新 更多