Dynamic plugins in angular

26 views
Skip to first unread message

Константин Александрович Прутков

unread,
Nov 14, 2018, 10:11:57 AM11/14/18
to Angular and AngularJS discussion
The question is how one can achieve similar "thing" in Angular 2+ to that I managed to do in AngularJS. What the "thing" is I will describe below.

Question description

So the process that I created in AngularJS can be described as follows:
  • We want to inject a custom dynamic content into a page, let's call it a plugin
  • That content is not defined in the web-client, instead it is being retrieved from a server in a structured form
That is we receive from the server html and js files that are necessary for the plugin to function and we load and display on the page. On the client I create an AngularJs directive to hold the necessary logic if the plugin.

Here is the code example for javascript  plugin code:
() => {
   
return {
        templateUrl
: 'directive-template-name',
        restrict
: "E",
        controller
: ['$scope', ... /* other dependecies */, function ($scope, ...) {
           
/* directive code */
       
}]
   
};
}

Here is the code example for plugin loader code:
// evaluate js function and compile directive
var diretiveFactory = eval(content.jsFile);


// AngularJS CompileProvider
 
CompileProvider.directive.apply(null, [content.name, diretiveFactory]);    
// append html code to element using angular
var wrapper = angular.element("#" + wrapperName);
wrapper
.html(requestedDirective[0].htmlContent);
   
// initialize dynamic content
var wrapperScope = wrapper.scope();
this.$compile(wrapper)(wrapperScope);

As one can see, this method allows to load a valid javascript and "compile" it into a AngularJS directive and then display it on the page at will.

Angular 2+


The problem with new angular is that concept of the directive, as far as I understand, is replace by a concept of component. Moreover, all client-side code is now written in typescript and transpiled into javascript for the client's browser. As far as I understand, all dependencies are being resolved in the traspilation process.

That creates some problems that prevent from creating dynamic plugins for Angular 2+:
  • A do not know how to inject dependencies into a dynamically created component as I do not know what exactly do I need when the transpilation happens.
  • Let's say I want several components to co-exist simultaneously (for example a parent dynamic components that holds children componets) and be able to communicate, in the case of AngularJs I could solve this problem by using scope variable. In case of Angular 2+ components are isolated so how to I go about this problem.
  • Moreover, I do not know how one can go about creating ViewContainerRef to hold the dynamically created component in evaluated javascript code. (See code examples below)
I will provide code examples of I got so far below. Content loader typescript code:
//some imports
import { Component, ComponentRef, Compiler, Injector, NgModule, NgModuleRef, ViewContainerRef} from '@angular/core';
   
// plugin content structure
class DynamicPageModel {
    name
: string;
    htmlFile
: string;
    jsFile
: string;
}
   
export class DynamicContentService {
   
    constructor
(
       
private _compiler: Compiler,
       
private _injector: Injector,
       
private _m: NgModuleRef<any>
   
) { }
   
   
public LoadDynamicContent(container: ViewContainerRef, content: DynamicPageModel) {
       
// evaluate js function and compile component
       
var tmpCmp = Component({
           
template: content.htmlFile,
            selector
: content.name
       
})(eval(content.jsFile)());
       
var tmpModule = NgModule({declarations: [tmpCmp as any]})(class { });
       
var factories = this._compiler.compileModuleAndAllComponentsSync(tmpModule)
       
var f = factories.componentFactories[0];
   
       
var cmpRef =  f.create(this._injector, [], null, this._m);
   
       
// insert component into the container
        container
.insert(cmpRef.hostView);
   
}
}

For reference to what is going on in this piece of code, I got it from here.

Plugin example javascript code:
() => {
   
function dynamicMain() {
       
this.componentName = 'Dynamic Example Main';
   
}
   
return dynamicExampleMain;
}

As one can see, this is quite simple dynamic plugin that is unable to use any services or other components as it is unable to inject them into itself.

Expected answer

As an answer to my question I would like to hear a solution(s) to the problems I encountered when trying to create dynamic plugins for the Angular 2+.
If you know a better approach to creating dynamic plugins (remember: they are retrieved from a server) at run time in Angular 2+, I would like to hear it too.

Константин Александрович Прутков

unread,
Nov 14, 2018, 10:11:58 AM11/14/18
to Angular and AngularJS discussion

Sander Elias

unread,
Nov 14, 2018, 10:28:02 AM11/14/18
to Angular and AngularJS discussion
Hi Константин,

There are several paths available to solve this issue. There is a way to dynamically compile things in your browser. There is info on how to do this now on the docs site. Ping me if you cant find it. This method is the most difficult to use.
The next way is using custom elements, and this is the way I would go now. Then there are some things in the pipeline, which becomes available when ivy comes to angular. (no timeline on that, its ready when its ready)
When this is fully in, you can do something similar to your directive solution from angular 1

Regards
Sander
Reply all
Reply to author
Forward
0 new messages