Динамическое добавление элементов в редактируемый div с помощью Angular и ANYWHERE

#html #angular #typescript #angular5

#HTML #angular #typescript #angular5

Вопрос:

Итак, я создаю HTML-интерфейс, в котором пользователь должен иметь возможность писать текст и отправлять его в качестве уведомления в наше мобильное приложение.

Я сталкиваюсь с некоторыми проблемами с текстом и динамическими вставленными элементами с использованием Angular 5;

Текст может содержать специальные элементы, такие как: номер телефона, местоположение и URL-адрес веб-сайта. Эти специальные элементы будут вставлены нажатием кнопки, которая открывает диалоговое окно, и для каждого из них отображаются его конкретные поля, такие как карты Google для определения местоположения и поля ввода для веб-URL и мобильного телефона. Это реализовано таким образом, чтобы фиксировать долготу, широту и номера телефонов на кнопке сохранения, чтобы добавлять их в качестве кнопок к полученному нажатию на устройствах.

В любом случае, описанное выше реализовано и может успешно работать, за исключением способа динамического добавления промежутков специальных элементов внутри div веб-интерфейса. Добавленные области должны иметь класс и событие щелчка, чтобы снова отобразить диалоговое окно для изменения данных. Также они могут быть вставлены в любое место внутри большого div в зависимости от выбора пользователя.

Ниже приведено изображение с описанием выше. введите описание изображения здесь

Синие промежутки — это те, которые следует динамически добавлять в редактируемый div, который может быть заполнен примерно 450 символами.

Итак, как решить проблему и включить функцию добавления интерактивных и разработанных разделов со значками внутри редактируемого содержимого div и иметь возможность на заключительном этапе извлекать данные?

Мой код приведен ниже, работает, но для определенной / предопределенной позиции:

Message.html

       <div id="myMessage" contenteditable="true" dir="ltr" [innerHTML]="contentEN | safeHtml" 
      style=" height: 80px;border: 1px solid #c1c1c1; padding: 7px;">
      </div>
      
      <ng-container  #vc>           
      </ng-container>
  

Message.ts

       @ViewChild('vc', {read: ViewContainerRef}) target: ViewContainerRef;
      
      createSpanPhone(spanIDNumber, phoneDescription, phoneValue ){
      
          // here the span Phone is created dynamically outside the div
          let phoneComponent = this.cfr.resolveComponentFactory(PhoneComponent);
          this.componentRef = this.target.createComponent(phoneComponent);
      }
  

PhoneComponent.ts

     import { Component } from '@angular/core';
    import { faPhone } from '@fortawesome/free-solid-svg-icons';

    @Component({
       selector: 'my-phone',
       template: '<span contenteditable="false" (click) = "test()" class="BAN_Tags_IN_Text"> <fa-icon 
                  [icon]="faPhone" class="faSpanIcon"> </fa-icon> <span class="phoneDesc" 
                  data-attr="EN">hello</span> <span class="phoneVal" ><b>12346</b></span>
                   </span>'
      })

   export class PhoneComponent  {
      faPhone = faPhone; // trying the icon

      constructor(){    
      }

     test(){
       console.log("Hiiii"); // trying the click event
     }
   }  
  

ViewContainerRef заполнен успешно, но мне нужно заполнить промежутки в div выше (id = MyMessage), а не в предопределенной позиции.

Ответ №1:

если ваш текст представляет собой простой текст (не содержит html-тегов, которые не могут быть заключены в <span> , — я хочу иметь в виду, что это разрешено, например <i> , или <b> , но нет <p> — вы можете создать такой компонент, как

 @Component({
  selector: "html-content",
  template: `
    <span class="inline" [innerHTML]="value"></span>
  `
})
export class HtmlComponent {
  @Input() value;
  constructor() {}

}
  

Директива, подобная

 @Directive({ selector: "[content]" })
export class ContentDirective {
  @Input() set content(textHtml: string) {
    this.viewContainerRef.clear();
    if (!textHtml) return
    //If not end with . or space, add an space
    if (textHtml.slice(-1)!=" " amp;amp; textHtml.slice(-1)!=".")
      textHtml =" "

    //gets the "words"
    //const parts = textHtml.match(/ ?S  | ?S ./gi);

     const parts = textHtml.match(/<?[^rntfv< ]  ?/gi);
    parts.forEach(h => {
      let space = false;
      let search = h.replace(/[ .;,:]/gi, "")
      let arg=null;

      //to allow pass arguments to the components in the way, e.g.
      //     <phone=arguments -be carefull! the arguments can not contains spaces
      //     
      if (search.match(/<phone=. /))
      {
        arg=search.split("=")[1].split(">")[0]
        search="<phone>"
      }
      if (search.match(/<location=. /))
      {
        arg=search.split("=")[1].split(">")[0]
        search="<location>"
      }
        
      switch (search) {
        case "<phone>":
        case "<location>":
          const factory =
            search == "<phone>"
              ? this.componentFactoryResolver.resolveComponentFactory(
                  PhoneComponent
                )
              : this.componentFactoryResolver.resolveComponentFactory(
                  LocationComponent
                );

          const phone=this.viewContainerRef.createComponent(factory);
          //if our component has "@Input() arg"
          (phone.instance as any).arg=arg||"";
          break;
        
        default:
          const factoryHtml = this.componentFactoryResolver.resolveComponentFactory(
            HtmlComponent
          );
          const html = this.viewContainerRef.createComponent(factoryHtml);
          html.instance.value = h;
          space = true;
          break;
      }
      //this allow write space or dot after the component.
      if (!space amp;amp; h.match(/. >[ ;,:.]/gi)) {
        const factoryDot = this.componentFactoryResolver.resolveComponentFactory(
          HtmlComponent
        );
        const html = this.viewContainerRef.createComponent(factoryDot);
        //we check if, after the component we has a "," or ";" or ":" or ". "
        html.instance.value = h.slice(h.indexOf(">") 1)
      }
    });
    //just for check the parts
    console.log(textHtml, parts);
  }
  constructor(
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) {}
}
  

Вы можете увидеть stackblitz без гарантии