1
votes

Looking for the "Angular way". Using Angular 7 but if you know the approach in a different version that would be very helpful as well. New to Angular but not new to Javascript or programming.

I have a lot of text and I will most likely need to use some form of pagination but I am not there yet. I want to turn each word into a link. The text does change and will come from a server

Example of text received:

Bacon ipsum dolor amet hamburger cow short loin, pancetta ham venison chislic buffalo filet mignon turducken short ribs pork chop landjaeger tongue burgdoggen. Turducken pork chop pork belly sausage strip steak pork buffalo chuck cow short ribs pastrami. Spare ribs kielbasa swine ball tip. Pork belly fatback jerky boudin corned beef. Beef ribs flank pork, pork chop spare ribs cupim tenderloin pork belly ham hock tri-tip.

Example of generated html that needs to "compiled" into angular:

<span (click)='onClick("Bacon")'>Bacon</span> <span (click)='onClick("ipsum")'>ipsum</span> <span (click)='onClick("dolor")'>dolor</span> <span (click)='onClick("amet")'>amet</span> <span (click)='onClick("hamburger")'>hamburger</span> <span (click)='onClick("cow")'>cow</span> <span (click)='onClick("short")'>short</span> <span (click)='onClick("loin")'>loin</span>, <span (click)='onClick("pancetta")'>pancetta</span> <span (click)='onClick("ham")'>ham</span> <span (click)='onClick("venison")'>venison</span> <span (click)='onClick("chislic")'>chislic</span> <span (click)='onClick("buffalo")'>buffalo</span> <span (click)='onClick("filet")'>filet</span> <span (click)='onClick("mignon")'>mignon</span> <span (click)='onClick("turducken")'>turducken</span> <span (click)='onClick("short")'>short</span> <span (click)='onClick("ribs")'>ribs</span> <span (click)='onClick("pork")'>pork</span> <span (click)='onClick("chop")'>chop</span> <span (click)='onClick("landjaeger")'>landjaeger</span> <span (click)='onClick("tongue")'>tongue</span> <span (click)='onClick("burgdoggen")'>burgdoggen</span>. <span (click)='onClick("Turducken")'>Turducken</span> <span (click)='onClick("pork")'>pork</span> <span (click)='onClick("chop")'>chop</span> <span (click)='onClick("pork")'>pork</span> <span (click)='onClick("belly")'>belly</span> <span (click)='onClick("sausage")'>sausage</span> <span (click)='onClick("strip")'>strip</span> <span (click)='onClick("steak")'>steak</span> <span (click)='onClick("pork")'>pork</span> <span (click)='onClick("buffalo")'>buffalo</span> <span (click)='onClick("chuck")'>chuck</span> <span (click)='onClick("cow")'>cow</span> <span (click)='onClick("short")'>short</span> <span (click)='onClick("ribs")'>ribs</span> <span (click)='onClick("pastrami")'>pastrami</span>. <span (click)='onClick("Spare")'>Spare</span> <span (click)='onClick("ribs")'>ribs</span> <span (click)='onClick("kielbasa")'>kielbasa</span> <span (click)='onClick("swine")'>swine</span> <span (click)='onClick("ball")'>ball</span> <span (click)='onClick("tip")'>tip</span>. <span (click)='onClick("Pork")'>Pork</span> <span (click)='onClick("belly")'>belly</span> <span (click)='onClick("fatback")'>fatback</span> <span (click)='onClick("jerky")'>jerky</span> <span (click)='onClick("boudin")'>boudin</span> <span (click)='onClick("corned")'>corned</span> <span (click)='onClick("beef")'>beef</span>. <span (click)='onClick("Beef")'>Beef</span> <span (click)='onClick("ribs")'>ribs</span> <span (click)='onClick("flank")'>flank</span> <span (click)='onClick("pork")'>pork</span>, <span (click)='onClick("pork")'>pork</span> <span (click)='onClick("chop")'>chop</span> <span (click)='onClick("spare")'>spare</span> <span (click)='onClick("ribs")'>ribs</span> <span (click)='onClick("cupim")'>cupim</span> <span (click)='onClick("tenderloin")'>tenderloin</span> <span (click)='onClick("pork")'>pork</span> <span (click)='onClick("belly")'>belly</span> <span (click)='onClick("ham")'>ham</span> <span (click)='onClick("hock")'>hock</span> <span (click)='onClick("tri")'>tri</span>-<span (click)='onClick("tip")'>tip</span>.

I can do this in vanilla js but I guess I am having some issue with HTML binding? I read a few blogs that said Angular pipes are not efficient for large amounts of things and I do have a rather large amount of things. Not sure it matters but my text is unicode (not English). Whitespace within the text must be preserved. On the click, I do need a few things to happen with other static functions/components

Code is good but just pointing me in the right directions with some keywords or concepts is definitely enough.

I feel like the top solution here might be an option but the answer is 2 years old: Compile dynamic HTML in Angular 4/5- something similar to $compile in Angular JS

edit... this is where I'm at with the code. RIP formatting. The MessageComponent does work. But now I need to figure out how to dynamically add a million view containers, I guess

//import { Component } from "@angular/core";
import { DomSanitizer } from '@angular/platform-browser';

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

import {
  Component,
  ViewChild,
  ViewContainerRef,
  ComponentFactoryResolver,
  ComponentRef,
  ComponentFactory
} from '@angular/core';
//import { MessageComponent } from './message.component';

@Component({
    selector: 'app-word',
    template: "<span (click)='onClickMsg(message)'>{{message}}</span>"
})
export class MessageComponent {
    @Input() message: string;
    onClickMsg(msg: any) { 
      alert("msg="+msg); 
    }
}

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {

  @ViewChild('messagecontainer', { read: ViewContainerRef }) entry: ViewContainerRef;

  title = "CodeSandbox";
  textMap;
  text = `
  Bacon ipsum dolor amet hamburger cow short loin, pancetta ham venison chislic buffalo filet mignon turducken short ribs pork chop landjaeger tongue burgdoggen. Turducken pork chop pork belly sausage strip steak pork buffalo chuck cow short ribs pastrami. Spare ribs kielbasa swine ball tip. Pork belly fatback jerky boudin corned beef. Beef ribs flank pork, pork chop spare ribs cupim tenderloin pork belly ham hock tri-tip. 
  `;

  constructor(private sanitized: DomSanitizer,
    private resolver: ComponentFactoryResolver) { 
    };


  componentRef: any;

  createComponent(message) {
    this.entry.clear();
    const factory = this.resolver.resolveComponentFactory(MessageComponent);
    const componentRef = this.entry.createComponent(factory);
    componentRef.instance.message = message;
  }

  ngOnInit() { 
    this.textMap = this.textToDict(); 
    this.createComponent("m");
  }
  textToDict() { 
    // var url = this.text.replace(/\w+/ug, function(word) {
    //   if (word.match(/\s+/)) return word;
    //   console.log("word=", word); 
    //   var x =  ''.concat('<button (click)=\'onClick("',word,'")\'>',word,'</button>');
    //   return x ; 
    // });

    // return this.sanitized.bypassSecurityTrustHtml(url) ; 
  }

  destroyComponent() {
    this.componentRef.destroy();
}
}

edit 2...

this works enough it might be the start of a possible solution

  ngOnInit() { 
    this.textMap = this.textToDict(); 
    this.createComponent("m");
    this.createComponent("    f");
  }
1

1 Answers

0
votes

You don't need to compile components. All you need is to create span elements and to register a listener on the parent component. Here's a quick solution (demo). It should be improved in terms of performance, use the renderer, etc., but it should give you the idea.

@Component({
  selector: 'my-text',
  template: ''
})
export class TextComponent implements OnInit, AfterViewInit {
  @Input() text: string;
  spans: Array<HTMLSpanElement>;

  constructor(private element: ElementRef<HTMLElement>) {
  }

  ngOnInit() {
    this.spans = this.text.split(' ').map(text => {
      const span = document.createElement('span');
      span.innerText = text;
      return span;
    });
  }

  ngAfterViewInit() {
    this.spans.forEach(span => {
      this.element.nativeElement.appendChild(span);
      this.element.nativeElement.appendChild(document.createTextNode(' '));
    })
  }

  @HostListener('click', ['$event.target'])
  onClick(target: HTMLElement) {
    console.log('clicked on ' + target.innerText);
  }
}