We are developing React Components as part of product development.
Now we want that other teams can reuse these components and develop something for different clients.
for example
We developed a UserInfo component, published on NPM repository and other teams can use it for different clients.
We are done with all basict things like NPM publish, then developrs can point newly component in package.json and can use it effectively.
But the problem I'm facing in component architecture, how a component can be designed, so that these can easily be customized by other developers.
Like a developer can have requirement for different view, some extra methods, or override existing methods.
My Approach - I feel that more improvements can be done
Approach#1
We wrote Web pack plugin to replace existing file by newly created file during build time.
Problems - Its a complete replace, so we cannot reuse or extend the existing file
Approach#2 - Tried to use ES6 Extend
The problem here is We are extending JS Call not "React.Component" so some unwanted behavior of methods and functions are observed.
There is also problem on customizing internal components, you can visualize internal component from image. i.e How to extend/customize Image Component, Address Component etc.
I am working hard to find the optimal solutions since last 2 months, expecting some help from community.
Related
Context:
I'm building a portal with next and its multi zones feature. This allows us to have multiple NextJS applications running independently by different teams, but as a single app.
The problem:
I need to make sure header, navigation and footer, in all zones, are consistent. For that, I want to have a component in 1 place, and share it across the zones.
Unfortunately, webpack modules federation is not yet supported natively by nextjs so we can't benefit from it.
I am searching on ways to find out how people using this feature are dealing with shared components.
My research and thoughts are limited to one single solution:
Create npm module for components I want to share, and import it in the zones, and then pass the data I need in the zones. This comes with a price: For components like header and footer, at least, when a new version of the package is released, we need to make sure all zones are updated with the same version. So... it's not perfect but seems to be the way.
I tried to check NextJS GitHub discussions, but unfortunately, the community there doesn't seem to be very active (someone asked it already there, and the only answer you can find there about it is mine).
I decided to give it a try asking this here since the audience is wider and maybe you guys could shed me some lights if there are better ideas.
I am sure you figured it out by now but i just want to leave some suggestions for people who are also dealing with it.
You can create your own component library that does automatic NPM delploys with tools like Bit and Lerna which contains your shared components. However this leaves us with the problem of data fetching since we don't want to hardcode that into the component because the data fetching source or method can change in the future. Normally you could maybe provide a hook or pre-build wrapper component because we also don't want every team to re-invent the wheel. But Since we are using Next i want to statically generate the menu and footer by calling our CMS in getStaticProps.
A possible solution would be to also export the data fetching logic with the menu package so every team can just import it and use it in the getStaticProps. This way we can make sure that the different teams use the same logic to get the data for the menu.
Currently, I am looking into a different solution which is putting our front-end projects into a mono repo. Specifically, I am looking at Turbo-repo since it was acquired by Vercel last year and thus plays really well with Nextjs. Because all the code is in the same repo different teams can just import it in every project.
Webpack 5 is supported by Next.js through a top level flag, take a look at this documentation: https://nextjs.org/docs/messages/webpack5
Also, checkout this package that lets you achieve Module Federation for Next.js apps: https://www.npmjs.com/package/#module-federation/nextjs-mf
Lastly, be sure to checkout the provided dashboard to better manage module federation: https://www.npmjs.com/package/#module-federation/dashboard-plugin
One of our business problem statement is, we have to create component such that any consumer can use them as a widget and embed in their website. And the thing is we already have those component made with ember.
We're not aware that which stack the consumer website going to have, that could be random. So we thought of converting these ember components to web components.
We did small POC where we were not able to create web component out of ember component using glimmer. But we're facing couple of problems
We're not able to pass objects/arrays to web components with glimmer (we tried using pass it through properties)
Somehow shadow DOM is not working when web component gets rendered
For using simple glimmer component I have followed https://glimmerjs.com/guides/using-glimmer-as-web-components
Gist:
Idea
The idea was to create framework agnostic component, so that it can be used inside different applications flawlessly. The component was already written in emberjs.
Solution
Solution is to write web-component for obvious, but we've our code already written in emberjs add-on. So we decided to migrate add-on project to latest ember, after we migrated add-on components to glimmerjs and converted that glimmerjs component to web-component (using #glimmer/web-component) and use it across.
We took an effort, and made it working. I'm sharing the current solution that we've applied.
As we were facing couple of challenges as I mentioned in my question. I'll answer those points one by one.
For passing object between two component, we're passing it by raising CustomEvent from child-component to parent-component
didInsertElement() {
this.element.dispatchEvent(
new CustomEvent('data-ready', { bubbles: true, detail: this })
);
}
After a research found that, glimmer does not support shadow DOM, they don't have any plan for supporting it yet. And re-analysing the requirement once we thought shadow-dom is good to have feature. We're using CSS specificity (traditional way) to isolate CSS specific to component :p
Though after applying above things we're in good shape. If in case you wanted to have a look at the code. Please check this github repository (initial look)
I am doing some research on how to split a huge single-page-monolith into a micro-frontend architecture.
The idea:
the page consists of several components which would be running autonomously
each component is managed by one dev-team
each team can change, update and deploy their components without breaking components of other teams
each team chooses its own toolstack
The reason
To efficiently develop large applications you need to have many people working on it. However the number of developers per app/team does not scale well. Parallel development of multiple independent apps by independent teams however can be scaled arbitrarily
With this in mind it is imperative that teams can choose their own toolstack and especially perform independent version-upgrades of third party-libraries (like angular, react, jquery). If this was not the case a framework-update would need to be compatible with every single component before you could deploy it to production.
Does this work with Angular?
While independent version-upgrades are necessary, it would be reasonable to restrict the teams to a few supported frameworks (Angular, React, Vue, Polymer...) and for now I try to build a demo purely consisting of Angular-Apps.
However even though Angular 5 is supposedly a platform-framework which supports huge multi-module apps, it seems to be almost impossible to have several independent angular-apps running in the same browser window.
I managed to bootstrap several Angular-Apps (different versions, each hosted on its own server) on a single webapp by utilizing HTML-Imports. However there are several global dependencies which need to be shared between apps
zone.js can only be started once
routing requires url-changes
Browser-stuff like cookies, sessionstorage, etc...
There are several articles in the net on how to bootstrap multiple angular-modules but they all refer to multiple modules in the same core-app, which in turn means they all are running on the same framework-version and an update means you have to rebuild and deploy the whole monolith.
Is there any solution other than "iframes" to get multiple Angular (5) Apps running on the same Page?
Instead of outright suggesting AGAINST this idea mainly due to separate stack requirements I will lay out the trade offs and provide some restrictions which will make this possible.
the page consists of several components which would be running autonomously
We all know this is offered out of box in Angular components with clear demarcation of inputs and output.
Small CAVEAT: When/If you pass objects for #Input and emit event objects with #Output() interacting components must agree on a defined interface upfront.
Workaround: Create another TypeScript project which just defines these artifacts. All other "component projects" would depend on a specific version of this.
each component is managed by one dev-team
Dev Teams can distribute the components just like other Angular projects in the opensource are doing. They can publish their artifacts to some npm repository. To develop attributable components I recommend you refer to Angular Material2 which may be overwhelming or you may use something like ngx-library-builder (based on Angular Team Member filipesilva/angular-quickstart-lib ) that each component team uses.
CAVEAT: Till this date angular team does not have a quick component library sharing project setup as evident in Angular CLI. But numerous developers have created some sort of library builders to fill the gap in Angular CLI.
each team can change, update and deploy their components without breaking components of other teams
Have your main project pull all the components and perform a periodic/change triggered build/deploy on some CI server. This is essentially producing AOT production builds with all the latest component releases.
As an added bonus you can have some abstract e2e tests built to do some automated integration testing ensuring side effects of one component does not break other components.
CAVEAT: It will be difficult to govern how each team develops the components i.e. they are doing optimal usage and disposition of memory, CPU, and other resources. e.g. what if one team starts creating subscriptions and does not remove them. Using some static code analysis can be useful but you will not have this source code available at this time - unless they also publish their source code.
each team chooses its own toolstack
This is a complete deal breaker unless if you mean "toolstack" as in developer tools such as IDEs and some of the "devDependencies". Although certain parts of "devDependencies" of each team must have the same exact versions of angular dev kits such as compilers etc.
At the least each team must use same Angular, RxJS etc.
Most importantly care should be taken that each of the team does not bootstrap any components - only the main projects will have a bootstrap module and that will bootstrap the root component. This will help answer your zone.js issue
Does this work with Angular?
If you recognize the limitations and provide governance I suggest go for it.
We asked ourselves the same question. Unfortunately, it seems that the answer to the question
Is there any solution other than "iframes" to get multiple Angular (5) Apps running on the same Page (edit: where each Angular app can use a different Angular version)?
currently is
No, unfortunately not, as long as you want to use Angular‘s change detection (which uses zone.js).
Due to zone.js Angular pollutes the global scope. Even worse zone.js patches an awful lot of browser APIs (https://github.com/angular/zone.js/blob/master/STANDARD-APIS.md) to detect when a zone is completed.
It’s only possible to use different versions of a framework in one page without side effects if the framework does not touch global scope (this seems to be true for React und Vue). Then you can bundle different framework versions with each application via Webpack (with separated scope and the downside that the size of each application bundle increases).
So, if you want to build a web page with Angular where different applications/modules should be integrated on one page, the only feasible solution currently is to create a deployment monolith (e.g. bundle different modules from different teams in one Angular application by a CI/CD system as bhantol explained in his answer).
It seems that the Angular team is also aware of the problem and that they might tackle it with following major versions. See robwormwald’s answer on the following Github issue regarding the Angular Elements roadmap: https://github.com/angular/angular/issues/20891
Hopefully there will be more information by the Angular team on that topic when the next major version Angular 6 is release at the end of march.
It seems that there has been a solution for your needs for a while now. Take a look at Single-SPA project HERE.
It's a wrapper SPA project for your micro SPA projects.
Take a look at Angular Elements (Custom Elements). https://moduscreate.com/blog/angular-elements-ngcomponents-everywhere/
The new Ionic version (4) is totally based on it to be able to be used on every version of Angular and on every JS frameworks.
For that, they created https://stenciljs.com/ that will help you to create Custom Elements.
But if all teams are using Angular, each of them can create a library using ngm for example.
Yes you can.
However you don't really want to get stuck in a framework when you are writing web components, because the whole point of a web component is to be reused possibly anywhere.
I share your vision, it's definitely better for the quality of the software as well as the productivity of developers to work on focused features rather than large apps when it comes to implementation.
Finally, what you are looking for right now is not Angular, but StencilJS. Stencil is a compiler for web components who also generate a virtual DOM inside your component to improve it's UI performances. Check it out ;-)
Other than Iframe there is one more option is currently available is SPA framework, you can get the sample code from here
https://github.com/PlaceMe-SAS/single-spa-angular-cli-examples
https://github.com/joeldenning/simple-single-spa-webpack-example
The only way to integrate different Angular Apps apart from IFrames and communication via postMessage is by "Deep-Linking" them and exchanging information using URL-Parameter. All Angular apps are then located on their own browser tab. But for common services and components you may want to implement a "Shared Kernel" used by all of your Angular apps, therefore your a stuck on a certain Angular version range. When you want this shared kernel the NGModule approach is the recommended way to go. You can mix Angular 2 and Angular 5 versions in one app because they are backward compatible. There ist no urgent need for the teams to stuck on the same version, until the Angular Team introduces breaking changes. Despite of that your technical debts grow, when you are not updating. The frequency for technical-updates is definitly higher in the Angular/Javascript area.
I'm very new to React, just experimenting with it. I'd like to know what are some common patterns, or libraries o built-ins for handling communication among components. For example, I have am input component and a "list" component that updates from the server according to what is in the input controller. Think of an autocomplete box. Since components have presentation logic, What if the two can't be "besides"? They're in different parts of the page and hence two different controllers.
Also, what if I have a login / logout button that works via Ajax? I imagine a lot of different components across the page reacting to the login / logout action reconfiguring themselves accord to a global "logged" state and the data retrieved from the server for the specific user that has logged in.
What is the best way that React "reacts" to those changes? Thanks
You should checkout Flux and Dispatcher.
It's kind of like a pub/sub system but without the problems of a pub/sub system. The advantage is that all events flow in one direction which makes the architecture much simpler and scalable.
If you haven't already, you should checkout Facebook's official React documentation. They have a really thorough tutorial that covers 90% of the scenarios you'll run into, including best practices for component interaction. They're also really good about building from no knowledge on up. Only takes about 20 minutes to go through: https://facebook.github.io/react/tutorial/tutorial.html
As mentioned in another answer, Redux is an amazing library for handling app state and keeping components separate that shouldn't know about each other. Basically, you can have parent and child components, but if you ever have a component with over 2 levels of children, you should consider using Redux (or Flux) to handle state between unrelated components. The problem redux solves is just breaking up those dependencies and still allowing components to have a single source of truth. Their official documentation is also really good: http://redux.js.org/
We're impressed with the integration and best practices that BoilerplateJS provides but the documentation is definitely lacking, especially for new RequireJS users.
We're a team of 5, each with different skill sets and one of the attractive points of BoilerplateJS is the ability to isolate UI components.
From the sample scaffolding, it's clear how we can unit-test each component separately. However, we're unclear how we can do this during development:
Developer A creates component structure and view model (tested) and passes it to Developer B
Developer B develops CSS and possibly animation for the component
Developer A and/or B integrate the component into the rest of the website and further test integration
How is it possible to achieve (2)? i.e. allow designers and developers to work on an isolated component - what is the recommended way to load the component so it can developed/debugged/tested?
About CSS
A UI component has roughly 3 parts: Structure (HTML), Presentation (CSS), Behavior (JS). A common way of handling is developers focusing on the Structure and Logic where designers work on the presentation.
This is how we developed the sample application of boilerplatejs. For example, the Menu, Theme and Localization components were developed by developers as a simple 'unordered lists' which looked like below exactly when they completed it (just delete the theme css link via Chrome Developer Tools and you will see the same):
Then designers took the ugly UI and created a theme that position and render these lists in a professional manner (we developed 2 themes stored at src/modules/baseModule/theme). It is of course hard for the developers to just deliver something that ugly, but they need to trust the ability of the designers to do their job. I'm sure you use a source control tool that allows different team members to work on the same component even simultaneously.
If you want the theming to be a prominent feature, I recommend minimizing component specific CSS files. Otherwise you might not be able to create different themes that completely changes the layout and look-n-feel of your components. Downside of not having component local css is the fact that components are not really self contained without 'presentation'. I'm still struggling to answer this question properly, any ideas/help is appreciated! See my related question on this below:
global CSS theming combination with component specific local stylesheets
Anyway there are several ways you may add CSS to your components, have a look at this question where these different ways are discussed.
Adding external CSS file to a BoilerplateJS project
Now about embedding components...
If you want the components embedded in to some other webpage, you can use the DOMController of boilerplate for that. For example, lets say you need to embed the 'departments (src/modules/sampleModule1/departments)' component to some other website. You will have to add a DomController in addition to already existing UrlController (UrlController respond to browser URL changes) to the module (src/modules/sampleModule1/module.js).
//crate a dom controller that searchs within whole document body
var domController = new Boiler.DomController($("body"));
domController.addRoutes({
//look for elements with id 'department_comp' and embed the department component
'#department_comp' : new DepartmentComponent(context),
});
domController.start();
Now on your webpage or on external site place a div or a section element for the DomController to embed department.
<section id="department_comp"></section>
Of course there are two things you need to take care of:
1) Your web page needs to have boilerplatejs runtime in it. This means all your third party JS libraries and theme CSS file should be statically added to the web page. (We are working around this, with v0.2-stable we expect to release a bootstrapper that can do all that with a single script declaration)
2) If your component uses JSON services from a different domain, you will have to address cross domain HTTP requests either with JSONp or CORS. But if your REST services are hosted on the same domain, you dont have to worry about this.