角度和内容可编辑

问题描述 投票:0回答:3

我在网上搜索过,但找不到在 Angular 6/7 上使用

contenteditable
事件的方法。 Angular 似乎有一个混乱的解决方案,但该功能似乎并未延续到最新版本。

一个用例是在内容可编辑的

onChange
事件上,调用函数:

<div contententeditable="true" [change]="onNameChange(if.there.is.such.a.thing)">Type your name</div>

...

private name: string;

onNameChange(name) {
   this.name = name;
}

对此有什么想法吗?谢谢。

angular events contenteditable
3个回答
34
投票

您可以使用

input
事件,如下所示:

<div contenteditable (input)="onNameChange($event.target.innerHTML)">
   Type your name
</div>

这是 Stackblitz 演示


0
投票

只需在元素和事件输入上设置一个变量,从innerText获取值

<h2 contenteditable="true" #x (input)="item.title = x.innerText">{{item.title}}</h2>


0
投票

我使用 MutationObserver 来观察元素上的文本变化。 到目前为止效果很好:

import { isPlatformBrowser } from '@angular/common';
import {
  Directive,
  ElementRef,
  OnDestroy,
  OnInit,
  PLATFORM_ID,
  forwardRef,
  inject,
  input,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
  Observable,
  Subject,
  Subscriber,
  filter,
  fromEvent,
  map,
  take,
  takeUntil,
} from 'rxjs';
import { assert } from './utils/assert';

@Directive({
  selector: '[contentEditable]',
  standalone: true,
  host: {
    '[contentEditable]': 'contentEditable()',
  },
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ContenteditableComponent),
      multi: true,
    },
  ],
})
export class ContenteditableComponent
  implements ControlValueAccessor, OnInit, OnDestroy
{
  // IO
  ///////////////
  contentEditable = input.required<boolean | string>();

  // Private attrs
  protected disabled = false;
  protected onChangesFn: (value: string) => void = () => {};
  protected onTouchedFn: () => void = () => {};

  private el = inject<ElementRef<HTMLElement>>(ElementRef);

  private platformId = inject(PLATFORM_ID);
  private readonly onDestroy$ = new Subject<void>();

  constructor() {}

  ngOnDestroy(): void {
    this.onDestroy$.next();
    this.onDestroy$.complete();
  }

  ngOnInit(): void {
    assert(this.el.nativeElement);

    if (isPlatformBrowser(this.platformId)) {
      this.observeTextData(this.el.nativeElement)
        .pipe(
          takeUntil(this.onDestroy$),
          filter(() => !this.disabled)
        )
        .subscribe((text) => {
          this.onChangesFn(text ?? '');
          this.onTouchedFn();
        });
    }
  }

  writeValue(text: any): void {
    this.el.nativeElement.innerText = text;
  }
  registerOnChange(fn: (value: string) => void): void {
    this.onChangesFn = fn;
  }

  registerOnTouched(fn: () => void): void {
    fromEvent(this.el.nativeElement, 'focus')
      .pipe(take(1), takeUntil(this.onDestroy$))
      .subscribe(fn);
  }

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  private observeTextData(el: HTMLElement) {
    return new Observable((observer: Subscriber<MutationRecord>) => {
      // Create an observer instance linked to the callback function
      const mutationObserver = new MutationObserver((mutationList, _obsever) =>
        mutationList.forEach((m) => observer.next(m))
      );

      // Start observing the target node for configured mutations
      mutationObserver.observe(this.el.nativeElement, {
        subtree: true,
        characterData: true,
      });
      return mutationObserver.disconnect.bind(mutationObserver);
    }).pipe(
      filter((m) => m.type === 'characterData'),
      map((m) => el.innerText)
    );
  }
}

© www.soinside.com 2019 - 2024. All rights reserved.