Angular Modules - aakash14goplani/FullStack GitHub Wiki

Topics Covered
What are Modules
Analyzing App Module
Services and Modules


What are Modules

  • Modules are ways of bundling Angular building blocks together, that would be components, directives, services, pipes. You have to group that all together into such Angular modules so that Angular is aware of these features because Angular doesn't automatically scan all the files in your project, instead you need to tell Angular what is a component, which components do you have and you can then bundle them together into modules.

  • Every Angular app needs to have at least one module, that app module, you can't have an Angular app without it.

  • So Angular analyzes these NgModules to understand your application and its features, you use them to define all the core building blocks - components, directives, services, pipes and so on and I just mentioned requires at least to one such module, the app module but you may split it into multiple modules.

  • You also have core Angular features that are wrapped into modules, things like the forms module, so Angular itself also provides certain features grouped together into such modules, so that you don't have to declare like 10 different forms directives, instead that forms module gives you all these directives.

  • And important also, you can't use a certain feature or building block without including it in a module. How you include it if you add it to declarations or providers depends on the feature you are talking about.


Analyzing App Module

  1. declarations[]:

    • declarations is in the end an array of all the components, directives and custom pipes you're using in your application.
    • So all these things have to go into declarations, otherwise you can't use them in your templates or in your routes.
  2. imports[]:

    • The imports array allows you to import other modules into this module, it is important to split your app into multiple modules.
    • We can import our custom modules plus the ones that ships with Angular like the forms module, the reactive forms module and so on.
  3. providers[]:

    • The providers array here is where we define all the services we want to provide.
  4. bootstrap[]:

    • The bootstrap array is important for starting your app.
    • It defines which component is available right in that index.html file and typically, you only have one component there.
    • You could have multiple components which is why bootstrap is also an array, you could define multiple components but each component would then kind of be detached from the other components.
  5. entryComponents[]:

    • The entry components is important for components you create in code that allows Angular to be aware of this component when it needs to create it programmatically.

Routing Module

  • We do have one other module right and that's the app-routing.module which is simply there to hold our route configuration.
  • We could absolutely add this into our app module, we only outsourced it because that's quite a lot of code and it keeps our app module a bit leaner.
  • The app-routing module simply imports the router module Angular offers us and uses a special method offered by that module, which then takes our route configuration to configure the Angular router with our route config and then something important happens, we export that router module here from the app routing module.
  • We need to do that because every module works on its own in Angular, they don't communicate with each other. If you declare a component in a certain component, in a certain module like app module, then we can only use it in here, nowhere else.
  • So we can use the selector in our other components we declared here but if we would have another module with other components, then we could not use selectors of components defined here in components of that other module.
  • And therefore here, we have to export the router module with our route configuration, so that it really is not just available in this module which would not be helpful at all but that it is available in the app module.

For smaller applications, you can absolutely leave app the way it is, i.e. with just one module, it's certainly manageable but the bigger your application grows, you might want to split your code to have leaner modules that are easier to manage, easier to understand and also to enhance performance.


Services and Modules

  • This section describes where you can provide services and how that affects the instances of the services you're working with.

  • Now we can provide services in the

    1. app module
    2. app component or other components
    3. load services or add services to providers of any eager loaded module
    4. load services or add services to lazy loaded module
  • Now when you provide a service in the app module or with provided in root in @injectable, the service is available application-wide and with the service, I also mean the same instance of that service is available application-wide, you're always working with the same single instance.

  • If you provide a service in a component, no matter if that's the app component or another component, then that service is only available for dependency injection inside of that component tree and then all these components will again share the same instance.

    • If you provide the service in multiple sibling components of your application, then all these sibling component trees have access to the service but each component tree has access to its own service instance.
  • Now the interesting thing is if you do add a service to providers of an eager loaded module, you might think that that service is only available inside of that module and that every module where you add a service to its providers has its own instance of that service but actually, that's not the case.

    • If a module is eager loaded, everything is bundled together initially and therefore, any services you add to providers in an eager loaded module, it will be available application-wide with one and the same instance. It has the exact same effect as adding a service to providers of the app module or as adding provided in root in @injectable.
  • A huge difference can be seen if you add a service to providers of a lazy loaded module though. There the service is only available in that loaded module and it gets its own instance.

    • If you do provide a service both in the app module and in the lazy loaded module, then the service is of course available application-wide but the lazy loaded module will get a separate instance, not the same instance you use in the rest of the app.
    • Sometimes this can be desirable but in other cases, it might lead to a strange behaviour where you would have expected to work with one and the same instance when you didn't.
  • The reason for that is that:

    • when you do provide in the app module or in @injectable, you use the root injector, so the root dependency injection mechanism of Angular.
    • When you do provide it in a component, you use the component specific injector, which is not the root injector and therefore the entire application does not has access to that.
    • In an eagerly loaded module, again the root injector is used automatically
    • with a lazy loaded module, a separate child injector is created by Angular for that module and therefore, that module has its own instance of that service.
  • Therefore, the default should be that you use the app module or again, provided in root in @injectable.

  • You of course might have scenarios where a component tree needs its own service, for example if that component tree somehow has a service that only matters for these components, it doesn't matter application-wide, then there is no need to provide the service application-wide, you can instead provide it in that component.

  • Eagerly loaded modules should pretty much never provide services, you should avoid this because the effect is the same as in the app module but it's kind of unexpected and it's harder to detect services there and therefore other developers diving into your code might look for a certain service, might look for it in the app module, not find it there and therefore be confused whether this service is now intended to be available to the entire app or only to that module.

  • Now in a lazy loaded module, you can of course add a service to providers but only do this if you deliberately want to have a separate instance of that service there, otherwise you can have strange bugs.

  • So as a rule, generally you want to ensure that services are always available application-wide by using @injectable, provided in root or by adding them to providers in the app module unless you have a strong reason for adding them only in a component tree or in a lazy loaded module, never add services in an eager loaded module.

modules and services