ウェブサイト検索

javascript - Angular 2/4/5/6 - カスタムコンポーネントの innerHTML をコンポーネントクラス側で変換する


私は次の基本コンポーネントを作りたいです: card.component がユーザーがその innerHTML に定義したものをコンポーネントクラスに取り込み、それを変換してテンプレートに表示できるようにするコンポーネントです。

これはユーザーがこのコンポーネントを使用する方法です:

<app-card>hello world, this is a sample text</app-card>

ここに、StackBlitz.com 上でフォークして動作させることができるベースプロジェクトがあります。

https://stackblitz.com/edit/angular-cmdpgh?file=src%2Fapp%2Fcomponents%2Fcard%2Fcard.component.ts

こちらがコードのプレビューです:

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'app-card',
    templateUrl: 'card.component.html',
    styleUrls: [ 'card.component.css' ],
})
export class CardComponent implements OnInit {

  private content: string;

  ngOnInit() {
    // please, override this to get the real innerHTML on: 'app.component.html'
    let innerHTML = 'testing';
    this.content = CardComponent.complexStrTransform(innerHTML);
  }
  private static complexStrTransform(str: string): string {
    // complex transformations here
    let result = '';
    for (let i = 0; i < str.length; i++) {
      let c = str[i];
      result += c;
      result += c.toUpperCase();
      result += '|';
    }
    result = '!!!' + result + '!!!';
    return result;
  }
}
<mat-card>
    <mat-card-content>
        {{content}}
    </mat-card-content>
    <mat-card-actions>
        <button mat-button>LIKE</button>
        <button mat-button>SHARE</button>
    </mat-card-actions>
</mat-card>

次の場所で:{{content}}、コンポーネントは次の結果を表示する必要があります:complexStrTransform(...).

私はいくつかの場所で、コンポーネントのテンプレートで innerHTML を置きたい場所を指定するために <ng-content></ng-content> を使用できると見たことがありますが、ここではそれが最適な方法ではないと思います。なぜなら、その innerHTML を非常に複雑な変更で変換する必要があり、それはコンポーネントのクラスでしか行えないからです。

何か提案はありますか?

ご注目いただきありがとうございます。

回答 #1

正確にこれがあなたの必要としているものかはわかりませんが、あなたの例を使って一般的なコンポーネントのやり取りを行うための小さな解決策を書いてみました。

  1. あなたのカードコンポーネントで、

メインアプリコンポーネントから後で使用できる入力を追加してください。

 @Input('complexInput') complexInputContent: string;

次に、追加されたグローバル入力に innerHTML を初期化してください。

 let innerHTML = this.complexInputContent;
  1. アプリコンポーネントを以下のように更新してください,

<app-card  [complexInput]="contentDiv.innerText">


<div #contentDiv contenteditable="true" >

hello world, this is a sample text

</div>


</app-card>

これは動作する解決策(プレビュー)です: https://angular-k5wjbp.stackblitz.io/

編集可能なコード: https://stackblitz.com/edit/angular-k5wjbp

参考文献: https://angular.io/guide/component-interaction

回答 #2

Dilushaの前回の回答は仕事の一部を済ませています。もう一つの部分は、あなたのtransform関数をパイプとして実装し、少しDOMのトリックを使うことです。こちらがStackblitzでの例です。

まず、あなたの transform 関数を Pipe として作り直し(そしてコンポーネントをリファクタリング)してください:

import { Component, Pipe, PipeTransform } from '@angular/core';

@Component({
  selector: 'app-card',
  templateUrl: 'card.component.html',
  styleUrls: ['card.component.css'],
})
export class CardComponent { }

@Pipe({ name: 'complexStr' })
export class ComplexStrPipe implements PipeTransform {
  transform(str: string): string {
    // complex transformations here
    let result = '';
    for (let i = 0; i < str.length; i++) {
      let c = str[i];
      result += c;
      result += c.toUpperCase();
      result += '|';
    }
    result = '!!!' + result + '!!!';
    return result;
  }
}

次に、ユーザーが簡単にコンテンツを挿入できるように、コンポーネントにng-contentタグを追加します。同時に、display: noneを使用してスタイルでそれを非表示にし、コンポーネントがアクセスしてパイプを使って表示できるようにテンプレート参照変数を割り当てます:

<mat-card>
    <mat-card-content>
        {{inner.innerText | complexStr}}
    </mat-card-content>
    <mat-card-actions>
        <button mat-button>LIKE</button>
        <button mat-button>SHARE</button>
    </mat-card-actions>
</mat-card>
<div #inner style="display: none">
    <ng-content></ng-content>
</div>

これでコンポーネントは望んだ通りに動作します:

<app-card>hello world, this is a sample text</app-card>

これは明らかに少しごまかしですが、元のユーザーコンテンツは依然としてDOMに存在しています。しかし、作業はかなり効率的に行えます。