Tips for integrating dynamic external components into Angular applications

I have encountered an issue with my Angular application.

My goal is to create an Angular application written in TypeScript and built with (aot).

The objective is to create a user dashboard with various widgets, each widget being an Angular component.

While my app already includes some widgets, I want to be able to extend them through a marketplace or manual creation.

The marketplace should be able to download files (js/ts/bundle..??) into a designated folder.

Subsequently, my app should be capable of loading and instantiating these new widgets (= ng component).

This is the folder structure in production:

  |- index.html
  |- main.f5b448e73f5a1f3f796e.bundle.js
  |- main.f5b448e73f5a1f3f796e.bundle.js.map
  |- .... (all other generated files)
  |- externalWidgets
      |- widgetA
            |- widjetA.js
            |- widjetA.js.map
            |- assets
                 |- image.png
      |- widgetB
            |- widjetB.ts
            |- widjetB.html
            |- widjetB.css

When loading the user page, the database indicates the presence of widgetA. Therefore, the objective is to dynamically load files and instantiate the included component.

I have attempted different solutions, such as using "require" and "System.import," but they have both failed when the path needs to be dynamically generated.

Is it possible to achieve this? I am open to changing my code structure and modifying external widgets (for instance, widgetB has not been transpiled yet...).

In essence, I am seeking a "plugin system" for an Angular4/webpack application.

Answer №1

I am currently following the same approach and have elaborated on the process in this presentation at NgConf.

It is essential to grasp that Webpack is unable to load modules dynamically that are unknown during the build phase. This limitation is linked to how Webpack constructs the dependency tree and gathers module identifiers during the build. This aligns with the purpose of Webpack as a modules bundler rather than a modules loader. To overcome this, a module loader like SystemJS is the most suitable option.

Furthermore, each plugin should be packaged as a module, with all exported components included in the entryComponents of that module.

During runtime, loading the module enables access to the declared components within it. Even though a module is not strictly necessary, it serves as a packaging unit in Angular. When obtaining a module, the approach varies based on whether the module was compiled with AOT or not.

If AOT was used for compilation, simply retrieve the exported factory class from the module and instantiate the module:

System.import('path/to/module').then((module)=>{
    const moduleFactory = module.moduleFactoryClassName;
    const moduleRef = moduleFactory.create(this.parentInjector);
    const resolver = moduleRef.componentFactoryResolver;
    const compFactory = resolver.resolveComponentFactory(AComponent);
}

For modules compiled without AOT, it is necessary to compile it using the JIT compiler:

System.import('path/to/module').then((module)=>{
    const moduleFactory = this.compiler.compileModuleSync(module.moduleClassName);
    const moduleRef = moduleFactory.create(this.parentInjector);
    const resolver = moduleRef.componentFactoryResolver;
    const compFactory = resolver.resolveComponentFactory(AComponent);
}

Once these steps are completed, dynamic components can be incorporated wherever needed using the methods outlined in this article: Understanding Dynamic Components in Angular

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the capabilities of Angular 2.0.0 by testing a component integrated with a

Here's a code snippet of a component in Angular: import {Component, OnInit} from '@angular/core'; import {Route, Router} from '@angular/router'; @Component({ selector: 'app-index', templateUrl: './index.compone ...

The error message is stating that the module located at C://.. does not have an exported member named "firebaseObservable"

Trying to follow an Angular/Firebase tutorial for a class but encountering issues. The FirebaseListObservable is not being imported in my component even though I have followed the tutorial closely. I've looked at similar questions for solutions but ha ...

The sole coding platform that fails to acknowledge the "ng" command is Visual Studio Code

Many users have encountered issues where the computer does not recognize 'ng,' but my problem differs from that. Interestingly, every software with a command shell recognizes 'ng' except for VS Code. IntelliJ? Works fine. Git bash? N ...

What steps should I take to enable the camera view in ngx-scanner?

I am currently working on an app that utilizes a QR code scanner. To implement this, I am using the ngx-scanner component, which is a modified version of Google's ZXing scanning library designed for Angular. However, I am encountering an issue where ...

Upon running the code, no errors appear on the console. However, my project isn't functioning properly and I'm encountering errors on the web console

ReferenceError: require is not defined when trying to access external "url" at Object.url in webpack_require (bootstrap:83) at client:6 importing from webpack-dev-server client index.js(http://0.0.0.0:0) vendor.js:219506 dynamically imp ...

Issue with Angular 6.1.0: Scroll position restoration feature malfunctioning

RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' }) In the latest version 6.1.0, there is a new feature that aims to restore the scroll position after navigation. Unfortunately, I encountered an issue where the scroll restorat ...

Mapping objects in Typescript to create a union of objects

I have been working on some TypeScript code and I seem to be having trouble getting it to work as expected. It would be greatly appreciated if someone could help me understand what I'm doing wrong or suggest a different approach. Let's assume I ...

Handling Click and Mouse Events with React [react-sortable-hoc, material-ui, react-virtualized]

I have come across an interesting example that I would like to share with you. Check out this live working example on Stackblitz When the delete button on the red bin icon is pressed, the onClick event handler does not get triggered (sorting happens inst ...

There was an issue encountered while attempting to differentiate an object in the Angular datatable results. The data table only accepts arrays and iterables

Looking to retrieve user data from an API using the httpClient post method in Angular 5, I faced a challenge as I couldn't properly display the retrieved information in a datatable. Below are snippets of the code that I have experimented with: User ...

When running ng serve with Angular, an unexpected error arises: The workspace does not have the 'production' configuration set

When attempting to serve my Angular (9.1.12) application locally in production mode using ng serve --configuration=production, I encountered the following error: An unhandled exception occurred: Configuration 'production' is not set in the worksp ...

Executing a method of another component in Angular2

In my Angular2 application, I have created a Header component that is rendered within the main App component. Now, I am faced with the challenge of placing a submit button from another Form component into the Header. How can I achieve this? I find myself ...

The RxJS Map operator may struggle to detect changes in an array that has been

EDIT: My question has been flagged as a duplicate. Despite my efforts to search for a solution, I failed to consider the full context of the issue and wasted hours using incorrect keywords. I have acknowledged the helpful response provided once the mistake ...

Understanding the process of reading cookies on the Angular2 side that have been set by the C# server

I'm struggling to understand how the angular code can access the cookie that was set on the server side. I found the following code snippet on GitHub. This C# function sets the cookie in the Response object with the last line of code. My question is, ...

Error: Cannot locate 'import-resolver-typescript/lib' in jsconfig.json file

Issue: An error occurred stating that the file '/Users/nish7/Documents/Code/WebDev/HOS/frontend/node_modules/eslint-import-resolver-typescript/lib' could not be found. This error is present in the program because of the specified root file for c ...

What is the correct way to include a JSON file in TypeScript?

Currently, I'm attempting to reference a JSON path and save it as a constant. My initial approach involved creating a reference with a string and then sending it back as a response using res.send, which successfully returned the string. However, I am ...

Component designed to be reusable across multiple different Angular 8 applications

I have multiple components with similar logic. Take for example: import { Component, OnInit } from '@angular/core'; import { Rule } from '@models'; import { ConfirmationDialogComponent } from '@core'; import { RulesSaveCompo ...

"Encountering a perplexing problem with css-loader in

const { merge } = require('webpack-merge'); const sass = require('sass'); module.exports = (config, context) => { return merge(config, { module: { rules: [ { test: /\. ...

Switching a div class in Angular 6 with a button click: A step-by-step guide

In this scenario, I have a div with the class name old. There is also a button that, when clicked, should toggle the div's class name between old and new. I am looking for a solution using Angular 6. Below is the code snippet: I am relatively new to ...

Having trouble establishing a connection with mongoose and typescript

When attempting to establish a connection using mongoose, I consistently encounter the errors outlined below. However, if I use MongoClient instead, everything functions as expected. import connectMongo from '../../lib/connectMongo' console.log( ...

Sharing API types from a NestJs backend to a Vue frontend (or any other frontend) application: Best practices

In my development setup, I have a VueJs and Typescript Front End app along with a PHP server that is in the process of being converted to NestJs. Both of these projects reside in the same Monorepo, allowing me to share types for DTOs between them. I am ex ...