Skip to content
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

NgModule on Large Projects #10552

Closed
jpsfs opened this issue Aug 7, 2016 · 144 comments
Closed

NgModule on Large Projects #10552

jpsfs opened this issue Aug 7, 2016 · 144 comments

Comments

@jpsfs
Copy link

jpsfs commented Aug 7, 2016

I'm submitting a ... (check one with "x")

[X] feature request / proposal

I've been reading about NgModule and I want to lay out some use cases that I'm not entirely sure the current proposal (https://docs.google.com/document/d/1isijHlib4fnukj-UxX5X1eWdUar6UkiGKPDFlOuNy1U/pub) takes into consideration.

Context

I'm part of a team building an Enterprise Framework (based on Angular 2). This framework will then be the base for other apps within the same ecosystem.

We have divided the framework into smaller projects/modules (think of them as separated npm packages). These modules are sets of controls (that are then reused across other modules) or pages that use those controls.

Example

A quick example can be:

Controls Module

import {Component} from "@angular/core";

@Component({
   selector: "my-combobox",
   ...
})
export class MyComboBox{

}

Checklist Module
// Checklist module depends on Controls module. Controls is treated as a 3rd party module.

import {Component} from "@angular/core";
import {MyComboBox} from "controlsmodule/components/mycombobox";
// Please note that we are only loading a specific component within the module, not all components inside that module.

@Component({
     selector: "my-checklist-page",
     directives: [MyComboBox, ...],
     ...
})
export class ChecklistPage{

}

Bootstrap doesn't know both Controls and Checklist modules. They are lazy loaded depending user's interaction. In this case, if the user navigates to a Checklist, the ChecklistPage component will be loaded and then the MyComboBox will also follow (because of the import made by ChecklistPage)

Checklist module has other dozen components. Each depending on other dozen components from multiple modules.

It's impractical (not to say nearly impossible) to have all components imported into the NgModule declaration. We are talking of several hundreds of components that might be used during app run time.

Also, the application needs to be modular and lazy load when possible. Different interactions within the app will lead to completely different modules being loaded.

Expected/desired behavior

The current solution, with component scoped directives, works like a charm for this use case. Not sure how this will play out with NgModule.

More than that, currently we can clearly see the dependencies needed by each component (in this case ChecklistPage). Making maintenance that much more easy.

Having all needed components imported into a NgModule, and then using them indistinctly on several components seems a fantastic solution for small applications. I feel that on a long term development, with several iterations across multiple years, with team rotation, ... having each component explicitly state on what it depends without looking into the template (and having compilation errors when something is missing) is a great advantage.

Conclusion

Please let me know if I was clear in my explanation. The goal of this issue is to raise awareness about this situation and get feedback from you on how to proceed.

We are available to show you our current work, we have several hundred Angular 2 components across 8 projects developed in the last year (since alpha 27).

@qdouble
Copy link

qdouble commented Aug 7, 2016

It doesn't appear to me that ngModules prohibits the setup you're going for, have you played with it yet? You can have several different modules and do lazy loading, etc. http://plnkr.co/edit/NAtRQJBy50R19QAl90jg?p=info

For something more similar to what you appear to be trying to do, keep an eye material2's transition: https://github.com/angular/material2/pull/950/files

@jpsfs
Copy link
Author

jpsfs commented Aug 7, 2016

Hi @qdouble,

Thank you for the quick reply.
Looking at https://github.com/jelbourn/material2/blob/ecbb4f42e0473899f6ad15d8e4ed8f262ded7a99/src/components/button-toggle/button-toggle.ts, are you saying that in order to achieve the same functionality that we have now, we need to declare a NgModule on each component? (that is what was added at the end of the file right?)

Also, it doesn't cover the maintenance issue I've mentioned on my initial statement. Having the dependencies of each component/directive declared on its decorator is, for me, a great advantage that I would like to keep.

@qdouble
Copy link

qdouble commented Aug 7, 2016

@jpsfs if you did want to create individual scope for each and every component, then I suppose you'd have to create different ngModules for each. While this may create more code in your case, I suppose it will create less code for the vast majority of other people by scoping my module instead of per component.

As far as the second issue, you could declare the ngModule and component right next to each other, so while it'd add an extra 3 or 4 lines of code to your files, I don't think that it creates a huge issue.

I'd say the vast majority of use cases don't require scoped directives per component, but in the case that it does, ngModules still supports that for a few more lines of code.

@jpsfs
Copy link
Author

jpsfs commented Aug 7, 2016

@qdouble Thanks.

I'm not sure this is a or/or situation, I can see both scenarios working together, no need to remove the functionality we already have. If someone wants to use modules, I can see that as a great addition. Meanwhile, the framework could work without modules (as it is working today). I believe that even the offline compilation issue can be sorted out with things as they currently are.

I'm letting this open to see if someone has something to add to the matter.

@qdouble
Copy link

qdouble commented Aug 7, 2016

@jpsfs understood, if I was in your situation, I'd definitely prefer they left both options open :)

They did write the reason for deprecating component directives in the doc you posted, as far as it creating two scopes, them thinking ngModule scope is small enough and that it's more in line with ES6 model.

A team member also mentioned before that it's generally problematic to have two different ways to do things... and in the long term, I could see the issue here....if you have some projects where people are using ngModules and other projects where there aren't, that creates more maintenance, training and compatibility issues.

Never know what direction this project will go in until it's final, so we'll see if they take what you're saying into consideration.

@sirajc
Copy link

sirajc commented Aug 7, 2016

Even I am currently working on designing the Architecture for a huge Enterprise App.
Unlike your situation @jpsfs , I am excited with NgModules and pretty much based my app architecture around NgModule.

Each Module will have its own set of routes and component dependencies. We can never create a piece of functionality with just one component, it needs Routes, at least one Smart Component and few Dumb Components and Services to go with it. Plug this all into a Module and you are good to go.

Coming to lazy loading, instead of loading code for each component, it looks good that each NgModule code will be loaded at once, so your functionality is fully usable once downloaded.

Creating hierarchy of Modules is much simpler as well, and it provides great Plug and Play feature for free.

@choeller
Copy link
Contributor

choeller commented Aug 7, 2016

We ware also currently working on an application with quite a lot of components (Not hundreds but dozens). There is no architectural need for us to split this application into several (lazy-loaded) modules, but now importing all those components into the bootstrap-file and passing them to declarations somehow feels wrong and breaks encapsulation of the component. As @jpsfs said, before it was very clear which components and directives were used by another component. So I would also appreciate to have the choice:

  • If it's a pretty commonly used directive declare it at the module
  • If it's something like TaskListItem just import it in TaskList.

Maybe some core-member can tell more about the decision of deprecating the second approach. Having worked with it for several month now, it feels pretty good ;)

@armstrjare
Copy link

To echo @choeller's point, it does feel strange moving away from the ability to provide component encapsulation.

My specific concern is that now the component names/selectors leak across the whole app, whereas before you could re-use selectors for different components by including specific directives as appropriate.

Now all selectors would need to be unique per component, right? Or am I misunderstanding how this works?

I felt that the original functionality matched the similar benefits provided by the CSS shadow-DOM emulation, in that we could worry less about selector collisions etc. across large apps. That was a great advantage IMO.

@DaSchTour
Copy link

My first thought about ngModule was "Oh, that's like in angular 1". As great as angular 1 already was, angular 2 is so much better in so many points. The best point for me was, that Components create some kind of dependency tree. I have a main component with a router which defines several entrypoints with it's own component. And every component know what it needs, no reason for the main component to know what any of the components at the end of the tree needs.
Now we are back at the good old times of angular 1, where we have a giant module definition.
Remeber the times your app entrypoint looked like this?

angular.module("myApp")
.controller("…")
.controller("…")
.controller("…")
.controller("…")
.controller("…")
.controller("…")
.component("…")
.component("…")
.component("…")
.component("…")
.component("…")
.component("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.service("…")
.service("…")
.service("…")
.service("…")
.service("…")
.service("…")

I thought this belongs to the past. I started to work with ng-metadata to improve old angular 1 projects and prepare for migration. I really love the fact, that it has a dependency tree and not a global "what might appear in this app" list.

@damiandennis
Copy link

This makes reusable components more difficult. I don't get how this makes things better having everything in a global/module scope, I think the ng2 team have caved to ng1 users that don't want change.

@qdouble
Copy link

qdouble commented Aug 10, 2016

@DaSchTour @damiandennis I understand the criticism of this architecture, however, referring to it as some sort of global scope is inaccurate, the methodology they are suggesting you take is to have feature modules: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#feature-modules

@DaSchTour
Copy link

@qdouble Well, so in the end it's just changing all Components to Modules. Although this is announced as change to reduce boilerplate code, it introduces a loot of need boilerplating.

While till RC4 a component vor every "page"/view of the application was enough, know I'll have to create a module, a component and routing for every view. I get the intention. But I somehow have the impression, that it's designed to make some things easier while not concerning many other points. And even with the feature module pattern I have to cut them very small to avoid dependency hell, by adding everything that might be needed because I can't see which part of my application needs which components.

In the end the modules are that small, that they have the similar repetitive dependency lists like current components.

In the end, it doesn't solve what it was designed for and only adds a lot of work. I have the feeling that there is some mismatch between design and reality here.

@damiandennis
Copy link

developers are lazy/short of time and take shortcuts. This encourages developers to take the quick route of just including everything in bootstrap. At least with components there is some requirement to include your dependencies. Yes they may also be lazy and create a single component for the whole application but that would be easier to fix as the dependencies are all in that component not mixed up between the bootstrap file and each component file within the application.

@qdouble
Copy link

qdouble commented Aug 10, 2016

@DaSchTour if each and every single component needs it's own scope, then yes it will create more boilerplate...but I'm taking it that the ng team is of the opinion that for most people, creating a new module for every feature section is enough and a few components would be able to live in each feature space.

Now obviously, there's no one fits all solution and having component level directives may be simpler for some people. However, it appears that a lot of the comments here are implying that they want you to just create an application with one huge ngModule tree...

I think it's more productive if the criticism is based around their actual design suggestions rather than a straw man design pattern that they aren't suggesting (i.e. creating an enterprise app that's just one huge ngModule)

@DaSchTour
Copy link

@qdouble the design pattern is simple. It's use a dependency tree instead of moving dependencies to a global, module scope. I guess the main point is, that reusable components now have to be modules, even if they are very small and have only very little functionality. Angular material2 is a very good example. A Button is a module, including one component. Maybe it's a general misconception of a many developers, that a module is something that contains more than just a button. And now let's think a step further. Just following the ideas from this article https://angularjs.blogspot.com/2016/08/angular-2-rc5-ngmodules-lazy-loading.html I find my self at the point, that I have a lot of modules that import a list of angular material2 modules and each of this modules consisting of a single component.
In fakt this is exactly what angular material2 does.

Nobody really get's the point, why we now have to wrap "all" of our components into modules. Or maybe we have to see this as a split of declaration. Component dependencies are now a module and component definition are as they were.

I guess the point is, that ngModules are not just a nice addition to make things easier but we are forced to change everything. Maybe somebody should make a clear explanation why not both can coexist.

@qdouble
Copy link

qdouble commented Aug 10, 2016

@DaSchTour well yes, I agree that if every single component you create needs it's own module, then using ngModules creates more boilerplate... I simply take it that the team doesn't think that most people will need that level of separation for every component.

I find my self at the point, that I have a lot of modules that import a list of angular material2 modules and each of this modules consisting of a single component.

You'd use shared modules for this: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#shared-module

Now, I'm not really sure either why they think that it's necessary to totally remove the ability to have a component scope, but for me, some of the criticism is making using ngModules more difficult than it actually is or making the design seem more sloppy than it actually is.

I think the criticism of removing component scoped directives/pipes to be perfectly valid. However, I don't think it's necessary to make it seem like there aren't good design patterns for ngModules.

@DaSchTour
Copy link

DaSchTour commented Aug 10, 2016

@qdouble I think nobody doubts that there are good design patterns for ngModules. But from what I understand modules are just a wrapper around a set of components, directives and services to expose them are a unit to the application. That's valid and a great idea. But why do I have to define the dependencies (that may only exist inside the module) for the module and not for the component.

Taking the example of @choeller
The module TaskDashboard has the following things

  1. Tasklist Component
  2. Taskitem Component
  3. Taskfilter Component
  4. Task Service

The Taskitem component is only needed inside the Tasklist and the Tasklist depends on Taskitem Component. The Taskfilter doesn't need the Taskitem Component. Now I don't have the Taskitem as a dependency in the Tasklist. Next step is, that I create a TaskSearch module. I add the following things.

  1. Tasklist Component
  2. Tasksearch Component
  3. Task Service

Well, I missed the Taskitem Component and it's broken. Tasklist always depends on Taskitem, but this dependency is hidden in the module. Reusing components is made more difficult and creates an additional source of errors.

Okay, while further reading through the modules guide I found this line

Components, directives and pipes must belong to exactly one module.

So the example exactly shows the concern that is raised here. There are can't be any shared component. So from my example I would have to split everything into modules.

While working on the strange and not elusive errors that come up when using ngModule I also found, that extending components now doesn't work as nice as before. I had a set of components with the needed dependencies that I could simply extend. Now I have to take care of importing the dependencies in the module I include my extended component in.

@qdouble
Copy link

qdouble commented Aug 10, 2016

@DaSchTour in you example, Taskitem should be a part of the Tasklist Module... so that would be two components in one module logically, not one for each.

As @sirajc pointed out, a typical design pattern would be to have one top smart component, followed by additional dumb components...so in a typical real world app, most modules would consist of a few components (whether by the smart/dumb component pattern or by related feature pattern), not just one component per module unless you are just building 3rd party components or something.

@choeller
Copy link
Contributor

@qdouble @DaSchTour It's true that the new architecture is not neccessarily meaning that you list all your components in one file, but looking at the app we are building I would currently pretty much go with this statement of @DaSchTour

I have the feeling that there is some mismatch between design and reality here.

So we have quite a bunch of components representing small units on the page like TaskListItem that are 100% bound to a special view. Creating Modules for each of those pages would be a total overkill. In our case there are very few reasons for splitting into multiple modules but way more reasons for encapsulating components.

tl;dr
It's cool to be able to define some dependencies on module level, but it's really sad, that we are not able to define dependencies on a component level any longer

@sirajc
Copy link

sirajc commented Aug 10, 2016

@DaSchTour, In material2 button component is tied to module to make it standalone. Those who needs to use button can import ButtonsModule. Also if you see the Module code, it contains two compoents MdButtonand MdAnchor, wrapping then in ButtonsModule makes things easier to use

@arciisine
Copy link

I'm wondering if its possible to create some hybrid component/module object that merges the two concepts into one to prevent duplication of files. Essentially being able to declare a component as a module once, and then import it as a module, as needed. It would honor the current approach, but minimize boilerplate.

@damiandennis
Copy link

@choeller I agree that having modules is a good addition, removing dependencies at the component level however just seems wrong.

@kylecordes
Copy link

Piling on to what others wrote above, I think the core idea here is not to experience the misery of a huge project with a huge module with hundreds of components in it. Rather, it is to build an application out of a reasonable number of medium-sized NgModules. Big enough that they are not trivial, small enough that you don't have a huge number of them; divided along fault lines that would tend to facilitate reuse and modularity in the old-fashioned computer science sense of high cohesion and low coupling.

By doing so, modules should turn out to be quite a useful concept to have them play on large projects.

@sirajc
Copy link

sirajc commented Aug 10, 2016

Thats very well summarized @kylecordes. NgModules helps in nice composition of Application. Small reusable modules makes up whole application.
Other benfit includes ambient availability of directives. Earlier we used to add ROUTER_DIRECTIVES in whole application. Now RouterModule.forRoot() does that for us.
BrowserModule, CommonModule, FormsModule makes more sense than including directives through each component.

MaterialModule on the other provides everything and if you need finer control then ButtonsModule etc will help us.
That's the beauty of composition. Embrace it and create your Symphony

@sirajc
Copy link

sirajc commented Aug 10, 2016

On top of this is LazyLoading. If not for NgModules how can one define the no of components and services that needs to go together to create one Routable unit. You cant download loose files as this will lead to huge no of network requests. Else you need to create a bundler where you list all dependent files ro create one bundle. NgModule does this with intuitive syntax.

@arciisine
Copy link

@kylecordes

Piling on to what others wrote above, I think the core idea here is not to experience the misery of a huge project with a huge module with hundreds of components in it. Rather, it is to build an application out of a reasonable number of medium-sized NgModules. Big enough that they are not trivial, small enough that you don't have a huge number of them; divided along fault lines that would tend to facilitate reuse and modularity in the old-fashioned computer science sense of high cohesion and low coupling.

Wouldn't those fault lines be very different for application developers vs library developers. Library developers should be in the minority, but that seems to be the main complaint. When building a reusable framework, the goal of wanting to minimize the module size leads to a lot of extra noise.

@bryanforbes
Copy link

@sirajc

On top of this is LazyLoading. If not for NgModules how can one define the no of components and services that needs to go together to create one Routable unit. You cant download loose files as this will lead to huge no of network requests. Else you need to create a bundler where you list all dependent files ro create one bundle. NgModule does this with intuitive syntax.

I was able to easily do this with the old router simply using a component that functioned as a container for a section of my application. This module brought in only the direct components it needed, which would bring in their own dependencies. Any decent module loader would be able to load the dependency chain (via imports) of that component without including every sub-dependency in the top-level component. The same goes for bundlers which should use some sort of module resolution logic. In short: there's no reason, from a lazy-loading perspective, why a developer should need to declare all contained dependencies in top-level component.

@rosslavery
Copy link

rosslavery commented Aug 11, 2016

It really feels like ngModule is a solution in search of a problem...

There's something to be said for being able to open up any given component, glance at it's directives array, and know exactly what components/directives it depends upon. Is it more verbose having to import a button component throughout the numerous components that use it? Yes, but I'd rather have some extra boilerplate and have things be explicit and immediately scannable than searching up the component/module tree to see how the heck component Y is using component X in it's template without ever importing it.

The argument being made above is that should someone desire to keep components isolated from each other, they could arguably create a module for each component -- but then at that point you're writing even more boilerplate than the original component ever had.

There are obviously other benefits to ngModule I haven't covered, and while there are benefits to being able to bundle sections of an application into feature modules, it really feels like a step backwards when it comes to clarity and having components being encapsulated pieces of code that don't leak scoping all over the place.

Part of the "hard sell" that the ng2 team had to make to the community from ng1 was "yeah, you're going to have to import and declare your directives for each component, but trust us, you'll appreciate being more explicit as your application grows". I am a big believer in suffering through a bit of repetition for the sake of readability and scannability in a large-scale team environment where a team member may only be working on a very small subset of components at any given time.

At the very least, I don't see any reason why ngModule and the directives/pipes property can't co-exist. The alternative is that every single component in every single application written for ng2, up to RC5 is now using deprecated code that needs to be non-trivially refactored. These really aren't the kind of changes that I'd expect this late into development...it's pretty disconcerting honestly.

@qdouble
Copy link

qdouble commented Aug 11, 2016

ngModules does essentially require a rewrite of most applications if they are to be structured properly... but the biggest misconception is that they are forcing a particular scope level when the reality is your modules can be as big or as small as you want them to be.

If you absolutely need to create a new module for every single component it results in more boilerplate = valid.

Most application should require you to create a module for every single component = invalid (especially if you are using smart/dumb component patterns and feature patterns)

@DaSchTour
Copy link

I think that time will show if introducing NgModules was a good idea. I have the impression that Angular 2 is currently just made to reach certain design goals. Nobody has thought about things like teams with different skill levels, code that is getting older and that is passed through generations of developers. In an ideal world it may look like a great idea. But in reality NgModules introduces a lot of traps and obfuscation that will cause a lot of trouble in the phase of training and introduction into a new project. It's simply not that simple and intuitive as it was with declaration on component level. Dependencies are hidden away in modules and it's just a matter of time you have to start researching for the module you component is in.

@poke
Copy link

poke commented Sep 27, 2016

Having made the transition of a larger application to NgModules recently, I think that they are mostly a good thing (now that I’m done with it, it makes certain sense). I actually think that the major problem is that they require a different component structure, something that likely didn’t fit well with how you structured your application before (at least that was the case for me).

However, I feel like they shouldn’t exist as the sole solution. If you look at how components are very commonly built, then you realize that very often, they are composes of smaller highly specialized subcomponents. For these subcomponents, which have no purpose outside of that container component, having to maintain them in the higher scoped module is really tedious and can quickly yield to scoping problems.

I would really appreciate if there was a mechanism in addition to NgModules that allowed components to define other components or directives as locally scoped dependencies which only apply within that exact component (just like how the directives listing worked before modules).

@iyobo
Copy link

iyobo commented Sep 27, 2016

I like @poke 's suggestion of having an alternative for those who would rather not go Full NgModules.

@CaptainCodeman
Copy link

NgModules were added in RC5 to solve problems with getting compilation and lazy loading to work - so hardly part of the original master-plan which is why they are not a great fit for some use-cases (especially if you were building an app based on the original "components are king" design).

Of course they fix or enable some things but bring their own complications along as well - more things for people to learn and design around which is especially challenging when all the best-practices and approaches haven't been worked out yet. Early adoption can bring lots of pain, I learnt that from the ride to RC4.

I liked the original component-centric plan more which is probably why I'm finding Polymer / Web Components a better fit now. Modules just feels like a half-step back to Angular 1.

@damiandennis
Copy link

At first I was resistant to this change of removing pipes and directives but getting used to it now and it does not seem as bad as I first thought.

@ggaborx
Copy link

ggaborx commented Sep 28, 2016

Can anyone show me how NgModules works in a really complex app where multiple component requires multiple others at multiple levels? All tutorials, examples and repos I saw only shows the easy ways where a module encapsulates it's own stuffs and publish (exports) some of them to others.

In real projects there are many cross-required dependencies usually with a hierarchical chain. Its somehow false to prove a concept of something by an example designated exactly for that.

@for all author of Angular2: can you please tell us honestly what are the negative parts of NgModules? I know you can write a book about all good things. Its fine. But I always need to deal with the hidden bad things which are wouldn't clear if you won't talk about them.

@poke
Copy link

poke commented Sep 28, 2016

Can anyone show me how NgModules works in a really complex app where multiple component requires multiple others at multiple levels?

One way to do this would be to create a dependency module just for those components: Let’s assume you have three sets of components A, B, and C which contain multiple components each and each set has those which are somewhat related. So these three sets would work well for three separate modules.

Now, components in each of those sets require multiple components from a set D. Those components in D are only used for the components in those three sets. Since they are used in all of them, you cannot just add the components to those modules (since components may only be part of a single module). At this point, you could merge A, B, C, and D into a gigantic module, so all the dependencies are there. But that’s of course very messy. Instead, you just create a new module for D that just contains those dependencies. This module does not do anything other than providing access to those modulew. Now you can import that module in each of those three other modules, and are able to use the components. But because the components are still “private”, you do not reexport the module or import the module D in some other module.

Since module imports only affect the module itself, this allows for some sort of scoping without polluting other modules. Of course, it requires you to create more modules but that’s just how it works.

@tb
Copy link

tb commented Sep 28, 2016

I can share my current setup. I have used 3 modules in app: CommonModule, AppModule and TestModule.

  • CommonModule imports & exports most common stuff like HttpModule, FormsModule, MdInputModule etc
  • AppModule imports BrowserModule, CommonModule, app.routing and individual components
  • TestModule imports & exports BaseModule, but overwrites some providers, like XHRBackend with MockBackend

I have introduced this setup to simplify TestBed.configureTestingModule,
so that I have to import TestModule and then just single component like:

TestBed.configureTestingModule({
  imports: [ TestModule ],
  declarations: [ MyFormComponent ]
});

@aluanhaddad
Copy link

One additional downside that became glaringly clear as I migrated from RC-4 to release was that NgModules impose a heavy penalty for simple refactorings where a component simply needs to be split up. With NgModules, you have to change the containing module, which might be several levels up the conceptual component tree, or promote the component by wrapping it in an NgModule and then removing it from its parent NgModule. Refactoring components is essential.

This is directly related to @poke's point

I would really appreciate if there was a mechanism in addition to NgModules that allowed components to define other components or directives as locally scoped dependencies which only apply within that exact component (just like how the directives listing worked before modules).

@emilio-martinez
Copy link
Contributor

I strongly disagree. A modular architecture as it is proposed by Angular 2 is easier to scale, adjust, and refactor where necessary. Sure, from RC4 to RC5 there was a bit of adjusting, but if anything, to me NgModules have proven to allow for a much more flexible application.

Angular is opinionated and it certainly may not be one size fits all, but NgModules is just as certainly not the point of failure to architect a smart, modern and highly-performant application.

@iurii-kyrylenko
Copy link

@emilio-martinez: In my opinion, NgModule would never be introduced, if Angular 2 weren't so slow at bootstrapping, when involving JiT. All other 'improvements' like 'scaling, adjusting, and refactoring' are arguable, as this discussion shows.

@kylecordes
Copy link

It's been quite a while now and many of us have had time to fully absorb NgModule into our work. I think it is now clear that as quite a few people have described, once you are past the speedbump of moving up to the new module system, it enables some pretty great things. For anyone who rides this thread having trouble absorbing NgModule, I suggest scrolling all the way through and reading everything from @robwormald and @wardbell especially.

I believe we will all find a year from now that many Angular 2+ applications make pervasive, seamless use of modules, lazy loading, and AOT. I believe it will be completely routine for most or nearly all applications to use these things to implement the "progressive application" vision where even large complex applications have a near instant initial load time and then lazily load (or optimistically lazily preload) the functionality they need. The result is actually quite amazingly slick, and achieved at remarkably low cost for the individual application developer: NgModule basically is that cost, and it is an irritating transition but then only a very modest amount of ongoing work to use.

@aluanhaddad
Copy link

aluanhaddad commented Sep 29, 2016

@kylecordes I hope you're right and I think that's the correct attitude to have.

@iurii-kyrylenko that is very true.

I do have a problem with angular compiling my JavaScript as it's supposed to TypeScript compiling my JavaScript, but that's a separate issue.

@DaSchTour
Copy link

@kylecordes I think there is a lot more to consider than just the transition. Modules introduce a lot of complexity and a lot of additional possibilities to add bugs to the own application. The biggest problem is obfuscation of dependencies. Which will cause a lot of trouble in the next years of developing with angular 2.

@emilio-martinez
Copy link
Contributor

@aluanhaddad I believe Angular uses a tsc wrapper to compile. It's nice because you can even implement it into a task runner workflow, for example.

@iurii-kyrylenko bootstrapping speed is also hard to determine based on RC4. A lot of the work that was done from then to final release has been cleanup and optimizations. In my experience Angular compiled JIT runs quicker than RC4 did anyway.

@DaSchTour can you elaborate on the bugs you've found while working with NgModule?

@DaSchTour
Copy link

@emilio-martinez it's not bugs in NgModule but bugs that will arise because of missing imports or duplicate service instances that would have been omitted or found in an earlier stage of development. It's about importing things in the place I use them and not somewhere I don't see if it's needed or used and in what place it is needed and used.

Just think about TypeScript working this way. I have a base file for my module, let's call it index.ts it looks like this.

import {foo} from bar;
import {StartComp} from start;

StartComp.boot();

Than we have a file called start.ts that looks like this.

export class StartComp {
   public static boot() {
      foo()
   }
}

That is what Angular is doing with NgModules. With some magic I have imported something into a module and it appears on the other end of my application. You have to know that foo is imported in index.ts and by running StartComp from index the imports there can be used in the component.

The dependencies are hidden and need additional investigation to find them.

@iurii-kyrylenko
Copy link

@emilio-martinez

In my experience Angular compiled JIT runs quicker than RC4 did anyway.

I have a medium complexity project, based on MEAN stack and Angular 2 final. It takes about 10 second to complete bootstrap in JIT mode on my Android device. I consider this as a significant delay. Currently I can't use AOT compilation without modifying my source code (issues with private members, elvis operator ...).

Does anyone have any info on performance AOT+lazy load for a real life projects?

@aluanhaddad
Copy link

aluanhaddad commented Sep 30, 2016

I believe Angular uses a tsc wrapper to compile. It's nice because you can even implement it into a task runner workflow, for example.

@emilio-martinez this is exactly what I do not want. I want to compile my code using whatever version of TypeScript I please, for example 2.1.0-dev which has down level emit for async/await. I don't want Angular to be in charge of compiling my TypeScript files, this should not be delegated to a framework, it is the role of a language. Without getting further off topic I would not have touched @script with a ten-foot pole, thank goodness it's dead.
Workflow wise, I use JSPM and at times Webpack, not a traditional task runner, and I let my IDE handle my linters.

Another issue is that I have written decorators that abstract over angular decorators and I now understand that aot will ignore them. Considering how heavy angular's of decorators is, I was very disappointed to learn that the framework would not fully support decorators, treating them, during AOT, as static annotations when they are in fact a runtime construct in the underlying language.

@robwormald
Copy link
Contributor

robwormald commented Sep 30, 2016

@aluanhaddad its important to understand that there are two distinct steps occurring during AoT compilation - the first is generating new typescript code, the second is transpiling that code to ES5/6. ngc does both as a convenience, but there's nothing inherent in AoT that requires that. Inside of google, we do both, and so that case is the priority. Anyone could implement a compiler host to support code generation in any environment they wanted.

We do not (yet) support abstracting on top of angular's built in decorators, but if you wanted to use something like https://www.npmjs.com/package/core-decorators, that would be fine.

@robwormald
Copy link
Contributor

@iurii-kyrylenko suggest you watch the day one keynote of AngularConnect, where LucidCharts talked about their experience with AoT. See https://youtu.be/xQdV7q3e_2w?t=1411

IMHO - everybody's #1 goal should be getting on AoT compilation as soon as possible. The performance is simply unbeatable.

@kylecordes
Copy link

@robwormald I haven't looked at what options are available when calling ngc - so this might already be covered. I think that those options could ease the concerns like the ones voiced here, if they make it obvious that NGC is fulfilling the first purpose as its main reason to exist in the second purpose as a convenience/optimization. If the documentation or help shows how to separately run off-the-shelf tsc for those who prefer to do so, that could further ease the concerns?

@robwormald
Copy link
Contributor

@kylecordes i don't think the documentation is going to cover how to implement your own compiler host any time soon. Its an advanced use case, and thus would require some self-directed learning to implement. We implemented a similar thing for the CLI here https://github.com/angular/angular-cli/tree/master/packages/webpack

@kylecordes
Copy link

@robwormald Ah, I don't mean implementing your own compiler host. I just meant a two-line "build process" - first calling ngc to emit some generated typescript, then calling tsc yourself to compile all of the typescript (your source plus the generated source) to JavaScript. This provides a quick reassurance that the typescript code is merely being compiled to JS by the off-the-shelf typescript compiler.

@aluanhaddad
Copy link

aluanhaddad commented Oct 1, 2016

@robwormald Thank you for your reply.
Regarding NGC, what I want to know is whether I can control the TypeScript compiler version and settings in the TS -> TS pass. Can I pass TypeScript to NGC or do I have to use a specific version which wraps a specific TypeScript version? How coupled are they?

Regarding decorators, is there an issue tracking support for user defined decorators that abstract over Angular decorators? The https://www.npmjs.com/package/core-decorators is an orthogonal set of decorators, but I have decorators which enforce patterns and conventions in my Angular apps by wrapping Angular decorators. An obvious use case for this is to automatically create and enforce package wide component name prefixes, but there are others as well.

Since NGC does not support this, how does it know which decorators are Angular specific?
Does it match angular decorators by name?
I hope not because that would violate JavaScript lexical scoping.
A simple scenario
awesome-component-decorators.ts

import { Component } from '@angular/core';
import template from './awesome-component.html';
import style from './awesome-component.less';

export const awesomeComponet = <T extends new (...args) => any>(target: T) =>
  Component({template, styles: [style], selector: snakeCase(target.name) })(target);

consumer.ts

import { awesomeComponet } 'app/shared/awesome-component-decorators';

@awesomeComponent 
export class AnAwesomeComponent { }

@awesomeComponent 
export class AnotherAwesomeComponent { }

@samudrak
Copy link

samudrak commented Jun 23, 2017

@jpsfs Have you found any solution to load Components dynamically with out adding component declarations to root application module ? .

I am also part of a angular 1.X to Angular 4 migration project . This project has large number of components that are reused in different application that are lazy loaded on based on application context.

As per my understanding in Angular 4

We have to add component dependencies into root @NgModule declarations as below.

import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
import {Component, NgModule} from "@angular/core";
...
...
@NgModule({
imports: [BrowserModule ], // import Angular's BrowserModule
bootstrap: [BootStrapComp], // indicate the bootstrap component
declarations: [com1, comp2 , comp5 ...... Comp n ] // register our component with the module
})
export class AppModule {}

platformBrowserDynamic().bootstrapModule(AppModule);

But in our case we don't wanted to root NgModule to know about component dependencies in compile time . Rather we wanted components to be loaded dynamically in Run time .
Did you find any good solutions that can bootstrap application without adding all components into root NgModule declarations ( And we don't wanted to have one NgModule for each component too:) )

@azarus
Copy link

azarus commented Oct 21, 2017

@DaSchTour

Remeber the times your app entrypoint looked like this?

Well for that some of us used a build scripts to automatically require these modules and add them to the app module.

I'm looking for something similar and easy solution in angular2.

@mlakmal
Copy link

mlakmal commented Oct 21, 2017

@samudrak you cannot lazy load just the component if u want to use Angular aot support. You will need to setup lazy module for each component and lazy load the module. We use similar approach in our application...

@aluanhaddad
Copy link

aluanhaddad commented Oct 22, 2017

With dynamic imports support in ECMAScript and now in TypeScript (with full type checking orthogonal to loading), the lazy loading use case is fairly arbitrary. Of course hindsight is 20/20 and there was no way to know that would happen.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 12, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests