Angular 2 contenteditable

В 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>

Ответ 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
    }
}

Ответ 2

Пожалуйста, ознакомьтесь с этим кодом. Он будет работать, я думаю.

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/

Ответ 4

Для правильной работы необходимо реализовать ControlValueAccessor для директивы contenteditable. См. Мое решение: ng-contenteditable.

Ответ 5

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);
    }

}