I have a project that I need to add an Ionic Page Component from remote server on runtime. Firstly, I need to transpile the component from typescript before and deploy to a server. After that, on a button click event, I need to download the component and register that as ionic page. It would be very helpful if I can get help in generation of js from typescript file. The main problem on js generation is, I'm using webpack and webpack is changing all the imports into its own function calls and it contains random(?) numbers in it.
Second problem is that I can't get and register a page in runtime. I'm using lazy loading but couldn't find a way to register that page. I tried the following code but didn't work:
let injector = ReflectiveInjector
.fromResolvedProviders([], this.vcRef.parentInjector);
// 1. Create module loader
let loader = new SystemJsNgModuleLoader(this.compiler);
loader.load("http://example-website.com/test-module")
.then((nmf:NgModuleFactory<any>) => {
// 2. create NgModuleRef
let ngmRef = nmf.create(injector);
let page = ngmRef.instance.page;
// 3. Create component factory
let cmpFactory = ngmRef
.componentFactoryResolver
.resolveComponentFactory(page);
// 4. Create the component
let componentRef = this.span.createComponent(cmpFactory, 0, injector,[]);
// 5. Init the component name field.
componentRef.instance.name = "TestPage";
// 6. Refresh the component area.
componentRef.changeDetectorRef.detectChanges();
componentRef.onDestroy(()=> {
componentRef.changeDetectorRef.detach();
});
});
I'm using ionic 3, angular 5 with typescript 2.4.2.
Related
I am working on a web application which can host mini-apps (or modules) developed in vanilla Js, HTML, CSS.
The host application dynamically loads (using fetch API) the mini-apps (or modules) into its pages and then I want these mini-apps to independently request for their data or do whatever they want to. I want these mini-apps isolated from the host scripts and styling but the host should be able to execute functions of these mini-apps (or modules).
Example: The dashboard of Microsoft Azure portal. It has widgets which can be selected, customised and placed by the user, and after loading of the host dashboard these widgets independently fetch for their data. Also, the period and auto-refresh time can be controlled by the host application.
Priorities:
• Modules should be able to execute their own JS scripts.
• If possible then everything should be in vanilla js (or Stenciljs / Vue.js)
Current File Structure:
main.html
js (dir)
style (dir)
modules (dir)
• module-one
| module.html
| module.css
| module.js
• module-n
...
I have tried creating custom HTML element and then appending HTML and CSS of module to shadowDom. But I still don't know how to get its JS working. If I insert module.js dynamically to main.html then somehow I need to change roots of all module.js appended in host application from
document to shadowRoot
Example:
//module.js
const sayHelloBtn = document.getElementById('sayHello');
sayHelloBtn.addEventListener('click', () => {console.log('Hello')});
//module.js after appending to host (main.html)
const mod = document.querySelector('module-one').shadowRoot;
const sayHelloBtn = mod.getElementById('sayHello');
sayHelloBtn.addEventListener('click', () => {console.log('Hello')});
Please let me know if further elaboration or clarification on question is required. Thank You :)
Problem Update:
I get a json of all the modules from an API and dynamically create custom elements with shadow root . I fetch the module files using the fetch API and then append them to the custom elements. I am able to successfully append the .html and .css to respective custom elements but cannot run JS inside shadow DOM. If I dynamically import the JS globally to the main.html then some how I need the JS to access the elements in its shadowRoot and also the functions should not conflict with other module's js functions with same name.
I have tried creating classes in each module.js which holds its respective methods, variable and a init() which does all the module's initialisation.
//module.js
class ModuleAbc {
constructor(host = document) { // shadowRoot is passed when instantiating from the host application
this.docRoot = host;
console.log('docRoot set: ', this.docRoot);
}
init() {
console.log('initialising module at host: ', this.docRoot);
const docRoot = this.docRoot;
const btn = docRoot.getElementById('cm'); //this element is inside shadowRoot
btn.addEventListener('click', () => {
console.log('Hello from mod 1!');
});
}
}
Now I do not know how to call init() of all the classes because their names are different for every module.
//HOST JS (main.js)
let mod_name = 'ModuleAbc';
const mod = new mod_name(module_root); //THIS DOESN'T WORK
mod.init();
Problem is resolved using Estus Flask's suggested solution
I have 3 apps on ports 4200, 4201 and 4202.
On my first app (on 4200) I have a route to other apps which loads their corresponding modules, a ProfileModule, and I'm creating components based on this module's exported components.
Note that my apps have the same module and exported component names.
This works perfectly but for some reason, when I change my route from an app to the other one, when components have already been loaded once, webpack retrieves the wrong module.
What I'm doing here is:
I'm routing to my second app (which is on 4201), it loads my ProfileModule from second-app
I'm routing to my third app (which is on 4202), it loads my ProfileModule from third-app
I'm going back to my second-app and it loads my ProfileModule from third-app instead of second-app
I guess it is due to the module names, but shouldn't it work if it's on a different remote?
Here is how I get my modules:
async function lookupExposedModule<T>(
remoteName: string,
exposedModule: string
): Promise<T> {
// Initializes the share scope. This fills it with known provided modules from this build and all remotes
await __webpack_init_sharing__("default");
const container = window[remoteName] as Container; // or get the container somewhere else
// Initialize the container, it may provide shared modules
await container.init(__webpack_share_scopes__.default);
const factory = await container.get(exposedModule);
const Module = factory();
return Module as T;
}
I found a solution here.
But in my case, I had the same names for different remotes in my webpack config.
I have an AspNet app using typescript with onsen ui.
When i called component from onsen, i used these ref
// <reference path="TypeScriptHelper/Onsen/OnsPageController.ts"/>
But i want to use a javascript lib called devexpress that has no .d.ts defintion
How can i called these controls from my typescript file ?
The script are inserted in the index.html
But when i call them in my typescript file like this :
DevExpress.Dashboard.ResourceManager.embedBundledResources();
// Creates a new Web Dashboard control with the specified ID and settings:
var dashboardControl = new DevExpress.Dashboard.DashboardControl(document.getElementById("container"), {
// Configures an URL where the Web Dashboard's server-side is hosted:
endpoint: "https://demos.devexpress.com/services/dashboard/api",
workingMode: "Viewer",
extensions: {
"dashboard-panel": (control) => new DevExpress.Dashboard.DashboardPanelExtension(control)
}
});
They are not recognized
Any idea why and how to fixe it ?
Thx in advance,
my issue is this:
whenever I'm using the following syntax inside angular ->
let myCmp = 'test1';
let cmp = require('./components/'+myCmp+'/bootstrapCmp.component.ts');
I'm getting all the components that inside the components folder within my final bundle, not just 'test1';
(I'm using the angular 2 WebPack starter pack of the AngularClass team -
https://github.com/AngularClass/angular-starter
Had anyone this issue too?
the component I'm loading / so is all other ones are basic angular 2 components.
thanks in advance, I'm struggling with it for too much ^^
hope the solution i found for myself will help other developers :
in the webpack.common.js file i've added new file to my entry object which i called : boot.js .
so the structure is like this :
entry: {
main:['./src/polyfills.browser.ts','./src/assets/boot.ts','./src/main.browser.ts']
}
the boot.ts file, contains an object with all the relavent components that i will be using (references).
while the bunlder (wepback) is running, whenever i need some component i'm going to this object i've created inside the boot.ts file, the final bundle file contains now only the components i want.
Here is the setup of a website I'm working on:
I have multiple types of pages. Each page have a (known) number of js views(components), encapsulated in modules. For each page type I create its own entry point. Previously I used RequireJS and rjs for bundling, so I had a special module (called 'loader'), which could load and initialize each view.
So basically, I have such a structure
-news.js
-homepage.js
-components
|
-header.js
-menu.js
-search.js
-article.js
-loader.js
and my news.js is
require('components/header');
require('components/search');
require('components/article');
var loader = require('components/loader');
loader.initAll();
and my homepage.js is
require('components/header');
require('components/search');
require('components/menu');
var loader = require('components/loader');
loader.initAll();
Each component looks like
<div class="js-component" data-component="components/article"></div>
And inside loader.js I would like to see something like
module.exports = {
initAll: function () {
// get all components on the page
var components = document.querySelectorAll('.js-component');
components.forEach(function(component) {
component.name = component.getAttribute('data-component');
// next is obviously not working with webpack, but is what I need
require(component.name).init();
})
}
}
How should I organize this with webpack?