In Angular 2 how can I make two way data binding with a contenteditable div?
Text Field
I've adapted Isetty's answer to work with the release version of Angular 2.0, now it is available. Apart from working with the release version, I've also added a keyup event and used textContent rather than innerText, because that suites my application better. You may wish to change these things.
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
}
}
the blur event and innerHTML attribute.
In .ts:
getContent(innerText){
this.content = innerText;
}
in .html:
<div (blur)="getContent($event.target.innerHTML)" contenteditable [innerHTML]="content"></div>
in angular 2 [(ngModel)] used for two way data binding.
the answer of your question is already here How to use [(ngModel)] on div's contenteditable in angular2? check this out and let me know if it is working for you or not.
To work properly, it is necessary to implement ControlValueAccessor
for the directive contenteditable
. See my solution: ng-contenteditable.
Please refer this code. It will work you i think.
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
}
}
For the extra inputs i found a link for you. https://www.namekdev.net/2016/01/two-way-binding-to-contenteditable-element-in-angular-2/
Angular 4/2 (Typescript) with dynamic editable:
// 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);
}
}