angular ionic2 contenteditable

¿Cómo usar[(ngModel)] en div''s contenteditable en angular2?



ionic2 (6)

Aquí hay otra versión , basada en la respuesta de @ tobek, que también admite html y pegar:

import { Directive, ElementRef, Input, Output, EventEmitter, SimpleChanges, OnChanges, HostListener, Sanitizer, SecurityContext } from ''@angular/core''; @Directive({ selector: ''[contenteditableModel]'' }) export class ContenteditableDirective implements OnChanges { /** Model */ @Input() contenteditableModel: string; @Output() contenteditableModelChange?= new EventEmitter(); /** Allow (sanitized) html */ @Input() contenteditableHtml?: boolean = false; constructor( private elRef: ElementRef, private sanitizer: Sanitizer ) { } ngOnChanges(changes: SimpleChanges) { if (changes[''contenteditableModel'']) { // On init: if contenteditableModel is empty, read from DOM in case the element has content if (changes[''contenteditableModel''].isFirstChange() && !this.contenteditableModel) { this.onInput(true); } this.refreshView(); } } @HostListener(''input'') // input event would be sufficient, but isn''t supported by IE @HostListener(''blur'') // additional fallback @HostListener(''keyup'') onInput(trim = false) { let value = this.elRef.nativeElement[this.getProperty()]; if (trim) { value = value.replace(/^[/n/s]+/, ''''); value = value.replace(/[/n/s]+$/, ''''); } this.contenteditableModelChange.emit(value); } @HostListener(''paste'') onPaste() { this.onInput(); if (!this.contenteditableHtml) { // For text-only contenteditable, remove pasted HTML. // 1 tick wait is required for DOM update setTimeout(() => { if (this.elRef.nativeElement.innerHTML !== this.elRef.nativeElement.innerText) { this.elRef.nativeElement.innerHTML = this.elRef.nativeElement.innerText; } }); } } private refreshView() { const newContent = this.sanitize(this.contenteditableModel); // Only refresh if content changed to avoid cursor loss // (as ngOnChanges can be triggered an additional time by onInput()) if (newContent !== this.elRef.nativeElement[this.getProperty()]) { this.elRef.nativeElement[this.getProperty()] = newContent; } } private getProperty(): string { return this.contenteditableHtml ? ''innerHTML'' : ''innerText''; } private sanitize(content: string): string { return this.contenteditableHtml ? this.sanitizer.sanitize(SecurityContext.HTML, content) : content; } }

Estoy tratando de usar ngModel para vincular de dos maneras el contenido de entrada contento de div de la siguiente manera:

<div id="replyiput" class="btn-input" [(ngModel)]="replyContent" contenteditable="true" data-text="type..." style="outline: none;" ></div>

pero no funciona y se produce un error:

EXCEPTION: No value accessor for '''' in [ddd in PostContent@64:141] app.bundle.js:33898 ORIGINAL EXCEPTION: No value accessor for ''''


Aquí hay una solución simple si lo que está vinculando es una cadena, no se necesitan eventos. Simplemente coloque una entrada de cuadro de texto dentro de la celda de la tabla y vincúlela. Luego formatee su cuadro de texto a transparente

HTML:

<tr *ngFor="let x of tableList"> <td> <input type="text" [(ngModel)]="x.value" [ngModelOptions]="{standalone: true}"> </td> </tr>



Trabajando Plunkr aquí http://plnkr.co/edit/j9fDFc , pero el código relevante a continuación.

El enlace y la actualización manual de textContent no funcionaba para mí, no maneja los saltos de línea (en Chrome, escribir después de que un salto de línea salta el cursor al principio) pero pude hacerlo funcionar usando una directiva de modelo contento de https://www.namekdev.net/2016/01/two-way-binding-to-contenteditable-element-in-angular-2/ .

Lo modifiqué para manejar texto sin formato de varias líneas (con /n , no con s) usando white-space: pre-wrap keyup , y lo actualicé para usar keyup lugar de blur . Tenga en cuenta que algunas soluciones a este problema utilizan el evento de input que aún no es compatible con IE o Edge en elementos contenteditable .

Aquí está el código:

Directiva:

import {Directive, ElementRef, Input, Output, EventEmitter, SimpleChanges} from ''angular2/core''; @Directive({ selector: ''[contenteditableModel]'', host: { ''(keyup)'': ''onKeyup()'' } }) export class ContenteditableModel { @Input(''contenteditableModel'') model: string; @Output(''contenteditableModelChange'') update = new EventEmitter(); /** * By updating this property on keyup, and checking against it during * ngOnChanges, we can rule out change events fired by our own onKeyup. * Ideally we would not have to check against the whole string on every * change, could possibly store a flag during onKeyup and test against that * flag in ngOnChanges, but implementation details of Angular change detection * cycle might make this not work in some edge cases? */ private lastViewModel: string; constructor(private elRef: ElementRef) { } ngOnChanges(changes: SimpleChanges) { if (changes[''model''] && changes[''model''].currentValue !== this.lastViewModel) { this.lastViewModel = this.model; this.refreshView(); } } /** This should probably be debounced. */ onKeyup() { var value = this.elRef.nativeElement.innerText; this.lastViewModel = value; this.update.emit(value); } private refreshView() { this.elRef.nativeElement.innerText = this.model } }

Uso:

import {Component} from ''angular2/core'' import {ContenteditableModel} from ''./contenteditable-model'' @Component({ selector: ''my-app'', providers: [], directives: [ContenteditableModel], styles: [ `div { white-space: pre-wrap; /* just for looks: */ border: 1px solid coral; width: 200px; min-height: 100px; margin-bottom: 20px; }` ], template: ` <b>contenteditable:</b> <div contenteditable="true" [(contenteditableModel)]="text"></div> <b>Output:</b> <div>{{text}}</div> <b>Input:</b><br> <button (click)="text=''Success!''">Set model to "Success!"</button> ` }) export class App { text: string; constructor() { this.text = "This works/nwith multiple/n/nlines" } }

Solo probado en Chrome y FF en Linux hasta ahora.


NgModel espera que el elemento enlazado tenga una propiedad de value , que los div s no tienen. Es por eso que obtiene el error de No value accessor .

Puede configurar su propia propiedad equivalente y enlace de datos de eventos utilizando la propiedad textContent (en lugar del value ) y el evento de input :

import {Component} from ''angular2/core''; @Component({ selector: ''my-app'', template: `{{title}} <div contenteditable="true" [textContent]="model" (input)="model=$event.target.textContent"></div> <p>{{model}}` }) export class AppComponent { title = ''Angular 2 RC.4''; model = ''some text''; constructor() { console.clear(); } }

Plunker

No sé si el evento de input es compatible con todos los navegadores para contenteditable . Siempre se puede vincular a algún evento de teclado.


Respuesta actualizada (2017-10-09) :

Ahora tengo un módulo ng-contenteditable . Su compatibilidad con formas angulares.

Antigua respuesta (2017-05-11) : en mi caso, puedo hacer lo siguiente:

<div contenteditable="true" (input)="post.postTitle = $event.target.innerText" >{{ postTitle }}</div>

Donde post : es un objeto con propiedad postTitle .

La primera vez, después de ngOnInit() y obtener la post desde el backend, configuro this.postTitle = post.postTitle en mi componente.