Create Dynamic Plugin elements on page

Hello,

We have been tasked with creating a plugin system for one of the apps we are working on written using @ionic/angular 4.2.0 .

As part of the UI, we need to inject web components written by the plugin developers. I’ve managed so far, to create a dynamic JavaScript loader that allows us to, per component, dynamically load the javascript files that contain the plugins.

This allows me to use the custom element tags directly in our templates e.g.
<fancy-button [(productId)]=“1”>

We’d like to create the elements dynamically on the page if possible (have a tag that is an “injection point”)

However, I can’t figure out how to create the components and bind the data to them.

I was attempting to use ViewChild to append elements, but this wasn’t working,

My second attempt was to use getElementById and use the innerHTML on a div, but this didn’t work either.

  const ComponentList = ['fancy-counter'];
  const container: HTMLElement = document.getElementById('sales-channel-entry-point');
  const elementString: string[] = [];
  for (let i = 0; i < ComponentList.length; i++) {
    elementString.push(`<${ComponentList[i]}></${ComponentList[i]}>`);
  }
  this.innerHtmlVar = elementString.join();
  console.log(this.innerHtmlVar);


<div [innerHTML]="innerHtmlVar"></div> 

Is this possible in the ionic framework?

Thanks in advance,

Trevor

See: Dynamic Component Loader

Thanks for the the reply.

This seems to require that the component is compiled into the full project and the type referenced directly in code. (in the app.module.ts under entryComponents)

Would it be possible to load an external JavaScript file like they do with the example at : https://alligator.io/angular/using-custom-elements/

This example, unfortunately, requires that you actually put the element in the template directly ( and I can’t figure out a proper way to do that either.).

Thanks.

Do you have access to the third-party plugins at build time or only at runtime?

Have you looked at Angular Elements, Angular components packaged as custom elements, a web standard for defining new HTML elements in a framework-agnostic way.

Also, TypeScript 2.4 added support for dynamic import().

We only have access to them at runtime.

The ultimate goal is to allow developer plugins to be added to certain anchor spots in our existing app. (similar… well… exactly like someone like shopify or weebly does).

We currently manage products and inventory and would like to be able to add “drop in plugins” that would allow us to interact with the online shopping giants and push products up to them. However, we were hoping to do it in a way that would allow people to drop them in if they wanted to develop them.

To load the JavaScript files, we added a service that created script tags on the fly.

 for (let i = 0; i < dynamicScripts.length; i++) {
            const node = document.createElement('script');
            node.src = dynamicScripts[i];
            node.type = 'text/javascript';
            node.async = false;
            node.charset = 'utf-8';
            document.getElementsByTagName('head')[0].appendChild(node);
        }

This let us access them via the tags directly on the page, but not create the elements dynamically.

That dynamic import() looks promising. If I can get the component to render where it should.

Thanks again.

Thanks for all your help @robinyo

Almost have what we want working. Just in case somebody is curious

  1. Create a custom element using the example found at: https://alligator.io/angular/using-custom-elements/
    This contains the template and all the logic for the plugin that we need
  2. Use the script above to load the JavaScript file dynamically per page.
  3. Create a Typescript file in the same location as the component above that simply adds the tags
   export function render(container: HTMLElement) {
        container.innerHTML += '<weebly-component [(product-id)]="productId"></weebly-component>';
    }
  1. Use the dynamic import statement to import the tag writer
  const el = document.getElementById('pleaseJustWork');
      if (el != null) {
        import('../../../../assets/custom-components/widget').then(widget => {
          widget.render(el);
        });
      }

Last thing to try to figure out is to bind the properties on the component. I think I may have to find a way around that as i suspect that angular doesn’t like me trying to do that out of the proper lifecycle.

Take another look at Angular Elements.

I looked up my notes re a plugin framework for Angular and found a few interesting links: