New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
POC: Angular migration path #17767
Closed
Closed
POC: Angular migration path #17767
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…JS asset w/o being specified in an event.
…w defer merged asset (that uses defer in the script element). not yet integrated w/ ExampleAngular library.
… defer + not using angular until ready
diosmosis
added
the
RFC
Indicates the issue is a request for comments where the author is looking for feedback.
label
Jul 14, 2021
…d components. completely untested.
The PoC now contains more downgraded components, including content-block and enriched-headline, both of which require transcluding from angularjs to angular. |
sgiehl
added
the
Do not close
PRs with this label won't be marked as stale by the Close Stale Issues action
label
Aug 6, 2021
fyi closing these for now. We'll still have the code just in case |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
Do not close
PRs with this label won't be marked as stale by the Close Stale Issues action
RFC
Indicates the issue is a request for comments where the author is looking for feedback.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
EDIT: broke the formatting by accident
Description:
This is a complete (for some definition of "complete") PoC of using Angular to replace AngularJS directives & components in Matomo, while still running alongside AngularJS. It can be used as a base to compare w/ the other PoCs that are coming and to see a possible Angular migration path.
Current State
There are JS errors when using the selector and I didn't test everything (just the absolute minimum functionality):
(Also a note re my knowledge of Angular: I'm somewhat familiar w/ the concepts and way of working in the framework, but I wouldn't call myself an expert. It's been several years since I worked with Angular last.)
Core Concepts
Components vs MV-whatever
Components in Angular encapsulate UI state and are primarily state driven. They are different from AngularJS directives, which are primarily mechanisms to manipulate the dom based on data. Components are designed (by me at least) by thinking about defining the state that's managed first (the list of sites displayed, the currently selected site, the search term), defining the input used to vary how the component behaves/looks, the output to allow outside elements to know when something in the component changes. Then filling out the implementation details.
Contrast w/ angularjs where it's more imperative. There's the input data, then the logic to render it and do things when data changes. Thinking about the interface and having hard boundaries between directives and the state they manage isn't considered. Nothing is there to prevent the creation of side effects in the input data.
Observables
Observables are a bit like promises, but instead of emitting one value to .then(), they can (potentially) emit any number of values. So they're a stream of data. They're useful in a UI context because they let you define a UI by thinking about how data flows through it.
Some definitions:
observable: a stream that can be observed. we can listen to and act on the data that comes through.
observer: something we can send values to.
subject: both an observable and an observer. we can emit values to a stream, and act on the values we send. we do both in different places in the code, which is what makes this useful.
For example:
In this workflow, change detection (determining when a property changes, figuring out what needs to be re-rendered and then re-rendering) isn't used. As in, we never need to use $watch, $apply, $timeout or even think about those things. (That's not to say change detection is not used at all, templates are still re-rendered when @input() values for example change, but we don't just $watch an expression, check it over and over in the digest cycle and re-render things when it changes).
There is a learning curve for observables, and you don't have to use them to use Angular. It's definitely possible to just do things imperatively.
And a quick note: we can't get an observable for @input values. This was a requested feature when I last used Angular several years ago, and is still an open issue in Angular. But it's easy to workaround if needed (as done in this PoC).
Modularization
Angular is a modern framework library, so it is expected it will be compiled and built. Getting around this is harder than figuring out a way to work w/ it (in my experience).
This poses a challenge for the way Matomo achieves modularization since we can't just include some JS files that use Angular directly to work like we do with AngularJS. Well, we probably could but it would be a lot slower. We also can't use TypeScript making the code far more verbose.
In the PoC, every plugin can have it's own angular library in the angular subfolder. The library is compiled via a command and we load the .umd.js file. (NOTE: this can be automated so users don't have to use the getJavaScriptFiles event). When a plugin is deactivated, the angular library will no longer be loaded.
There are two commands to facilitate this:
NOTE: it is required to use this commands, using ng (the Angular CLI tool) directly will not work. The library it generates will not go into a plugin directory, it will go elsewhere, and it will be incorrectly generated. There appears to be a bug somewhere since Angular will not recognize some of the generated library files because the entrypoint is in a sibling subfolder and not in the parent folder of the actual library files. This is why the libraries don't have a public_api.ts file which is generated with ng generate library.
And ng build depends on a root angular.json file that holds a manifest of every library in the plugin. But the number of angular projects depends on the plugins that have them. So the file needs to be generated dynamically before ng build is used.
Sidenote: we can also write a watch command to make development a bit easier.
Migration Path
After the bugs in this PR are fixed, Matomo could be converted to Angular one component at a time.
Models and services can be converted when a component uses them. We can delete them only if they are not shared among other angularjs code. If we keep converting they will eventually all be removed since the dependent code will slowly decrease to zero.
Twig templates should also be replaced. Instead, we output pages w/ an Angular component to encapsulate the UI. This'll make it easier to get rid of the AngularJS directives, since we'll be able to use the Angular version in more places.
Maintaining backwards compatibility
Angular does not allow using attribute directives when downgrading components (they expected everyone to switch to AngularJS components I'm guessing). But we can still provide backwards compatibility by creating a thin wrapper around a component (an example in this PR).
This also makes it possible to provide two way binding in AngularJS to avoid breaking things, without using it in Angular (which somewhat defeats the purpose of using Angular). We can listen to events in the adapter and set values we bind to the adapter.
Before releasing Matomo 5, we can get rid of all the adapters, downgrades and get rid of AngularJS completely.
Migration Phases
Phase 1 - convert all angular directives and related code to Angular. Each converted directive/component should have an associated adapter that keeps existing code backwards compatible. This should be done in all plugins as well. When a higher level component that uses other components is converted, we'd change the use of the dependent components to Angular, slowly removing angularjs code.
Phase 2 - convert all twig templates that use angular into Angular components and just render the new component in twig. Ideally, we'd only use twig to generate the containing HTML.
Phase 3- remove shared angularjs files and with the Matomo 5 release, remove all angularjs code + downgraded adapters.
Other implementation details/challenges
Pros & Cons