使用 Angular 和 ANYWHERE 将元素动态添加到可编辑的 div

Posted

技术标签:

【中文标题】使用 Angular 和 ANYWHERE 将元素动态添加到可编辑的 div【英文标题】:Dynamically add elements to the editable div with Angular and ANYWHERE 【发布时间】:2021-01-24 07:32:48 【问题描述】:

所以,我正在创建一个 html 界面,用户应该能够在其中编写文本并将其作为通知推送到我们的移动应用程序。

我在使用 Angular 5 的文本和动态插入元素方面遇到了一些问题;

文本可以包含特殊元素,例如:电话号码、位置和网站 URL。这些特殊元素将通过按下打开对话框的按钮插入,并为每个元素显示其特定字段,例如用于位置的谷歌地图和用于 Web URL 和移动电话的输入字段。以这种方式实现是为了在保存按钮上捕获经度、纬度和电话号码,以便将它们作为按钮添加到设备上接收到的推送中。

不管怎样,上述实现并且可以成功运行,除了在 Web 界面的 div 内动态添加特殊元素的跨度的方式。添加的 Span 必须有一个类和一个单击事件才能再次显示对话框以修改数据。根据用户的选择,它们还可以插入到大 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) 中填充 span,而不是在预定义的位置。

【问题讨论】:

【参考方案1】:

如果您的文本是简单文本(没有不能被 &lt;span&gt; 包围的 html 标签,-我的意思是允许的,例如 &lt;i&gt;&lt;b&gt;,但不是 &lt;p&gt; - 你可以创建一个类似的组件

@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)!=" " && textHtml.slice(-1)!=".")
      textHtml+=" "

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

     const parts = textHtml.match(/<?[^\r\n\t\f\v< ]+\ ?/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 && 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 without warranty

【讨论】:

以上是关于使用 Angular 和 ANYWHERE 将元素动态添加到可编辑的 div的主要内容,如果未能解决你的问题,请参考以下文章

使用Anywhere开启一个nodejs静态文件服务器

使用 TypeScript ES6 模块和 RequireJS 注册 Angular 元素

Angular:使用 NgIf 删除组件、内部表单和元素?

使用angular2在table1中单击元素时如何将元素聚焦在table2中

将样式应用于 Angular 2 中动态创建的元素

Angular:如何使用 *ngFor 将指令绑定到元素