I am trying to establish a microfronted architecture using Angular as the base framework and setting up hosts and remotes via Webpack Module Federation.
I have a few scenarios up and running, mostly working with the guidelines provided in this series by angulararchtitects.io I managed to build a shell application that fetches remote components at runtime and plugs them into some sort of dashboard page, which is amazing :)
However, some components need an initial parameter from the host/shell application, i.e. some ID for which the content should be loaded. I had hoped to pass this in via an input parameter. The blog suggests that this is possible - the case "set property" in the highlighted text seems to be exactly my scenario.
However, when I try to use code like the second commented one, I can't compile as compInstance is of type "unknown". This also makes sense because I am loading the component at runtime and the code can't possibly know anything about it beforehand, right? But how then would it be possible to interact with the remote component as the text suggests? Or is there another way to achieve this? Maybe to "blindly" set data on the remote component, leading to runtime errors if I used wrong inputs etc. (something like compInstance.set['initialId']
)?
The comments and link by Jake Smith pointed me in the right direction, I think. I managed to make this work in the following way:
export interface HasInputProperty {
myInput: string;
}
// Checks to assert the required interaction points
export function checkForInputProperty(obj: unknown): obj is HasInputProperty {
return hasProperty(obj as Object, 'myInput');
}
// Helper function (is handy if (in the real world) multiple checks are in order)
function hasProperty(obj: Object, property: string) {
return (typeof obj === 'object' && obj !== null) && property in obj;
}
const imported = await import('my-external-feature/Component');
const ref = this.someViewContainer.createComponent(imported.Component);
// In order to interact with the remote component we first verify that it has the needed properties
const component = ref.instance;
if (checkForInputProperty(component)) {
component.myInput = 'Here we can add the desired input';
} else {
alert('Noooo - did the other team change the contract without telling us?');
}
This works fine now and I quite like the idea to define a minimal contract and handle differing results. From some quick test it also seems like all the Angular magic for change detection upon changing input variables seems to work just fine, so "valueChanges" from outside are handled just like always.
However, in another place I recently used the way suggested by ghosh, namely to trigger some CustomEvent via DOM (in the shell) and setup a listener inside the component (Remotely integrated). There it was more suitable because it was used in a service, no component involvded. It has a similar effect, but I needed to retrieve some inital value from the session storage, as the service was only instantiated after the first (initializing) event was sent, so only subsequent changes could be listened to.
So if an initial param to a plugin-component is needed, I think the approach above is a valuable alternative. Nice to have some tools in the tool-box :)
Thanks for all the valuable inputs!