Hoe [(ngModel)] te gebruiken op contenteditable div’s in angular2?

Ik probeer ngModel te gebruiken om de bewerkbare invoerinhoud van div als volgt in twee richtingen te binden:

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

maar het werkt niet en er treedt een fout op:

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

Antwoord 1, autoriteit 100%

NgModelverwacht dat het gebonden element een eigenschap valueheeft, die divs niet hebben. Daarom krijg je de fout No value accessor.

U kunt uw eigen equivalente eigenschap en gebeurtenisgegevensbinding instellen met behulp van de eigenschap textContent(in plaats van value) en de gebeurtenis 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

Ik weet niet of de gebeurtenis inputin alle browsers wordt ondersteund voor contenteditable. Je kunt in plaats daarvan altijd binden aan een of andere toetsenbordgebeurtenis.


Antwoord 2, autoriteit 15%

Bijgewerkt antwoord (09-10-2017):

Nu heb ik de ng-contenteditable-module. De compatibiliteit met hoekige vormen.

Oud antwoord (2017-05-11):
In mijn geval kan ik eenvoudig doen:

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

Waar post– het is een object met eigenschap postTitle.

De eerste keer, na ngOnInit()en postvan de backend, stel ik this.postTitle = post.postTitlein mijn component in.


Antwoord 3, autoriteit 11%

Plunkr werkt hier http://plnkr.co/edit/j9fDFc, maar relevante code hieronder.


Het binden aan en handmatig bijwerken van textContentwerkte niet voor mij, het verwerkt geen regeleinden (in Chrome springt typen na een regeleinde de cursor terug naar het begin) maar ik kon om het werkend te krijgen met behulp van een contenteditable-modelrichtlijn van https://www.namekdev.net/2016/01/two-way-binding-to-contenteditable-element-in-angular-2/.

Ik heb het aangepast om meerregelige platte tekst te verwerken (met \ns, niet <br>s) met behulp van white-space: pre-wrap, en bijgewerkt om keyupte gebruiken in plaats van blur. Merk op dat sommige oplossingen voor dit probleem de gebeurtenis inputgebruiken die nog niet wordt ondersteund in IE of Edge op contenteditable-elementen.

Hier is de code:

Richtlijn:

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

Gebruik:

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"
  }
}

Tot nu toe alleen getest in Chrome en FF op Linux.


Antwoord 4, autoriteit 9%

Hier is een andere versie, gebaseerd op het antwoord van @tobek, die ook html en plakken ondersteunt :

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

Antwoord 5, autoriteit 4%

Ik heb met deze oplossingen gerommeld en zal nu de volgende oplossing in mijn project gebruiken:

<div #topicTitle contenteditable="true" [textContent]="model" (input)="model=topicTitle.innerText"></div>

Ik gebruik liever de sjabloonreferentievariabele boven het “$event” gedoe.

Verwante link:
https://angular.io/guide /user-input#get-user-input-from-a-template-reference-variable


Antwoord 6, autoriteit 2%

In contenteditableheb ik de tweerichtingsbindingbereikt met behulp van de blur-gebeurtenis en innerHTMLattribuut.

in .html:

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

In .ts:

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

Antwoord 7

Hier is een eenvoudige oplossing als u zich aan een string bindt, er zijn geen events nodig. Plaats gewoon een tekstvakinvoer in de tabelcel en bind daaraan. Maak vervolgens uw tekstvak transparant

HTML:

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

Antwoord 8

Voor mij was het voldoende om javascript te gebruiken, zonder het ts-object.
HTML:

<div
        id="custom-input"
        placeholder="Schreiben..."
</div>

TS:

  • om de invoerwaarde te krijgen: document.getElementById("custom-input").innerHTML

  • om de invoerwaarde in te stellen: document.getElementById("custom-input").innerHTML = "myValue"

En alles werkt perfect. Ik was genoodzaakt om een ​​div te gebruiken in plaats van een ionisch ion-textarea omdat ik problemen had met autosize. Met ion-textarea kon ik alleen autosize maken met js. Nu maak ik automatisch formaat met CSS, wat volgens mij beter is.

Other episodes