How to correctly implement a components.module.ts in Ionic

Continuing the discussion from Ionic 2… now 3… UGH… PLEASE, NEED HELP with Modules:

It was initially unclear to us how to configure an ionic 3 app to correctly use modules and/or lazy load pages.

We’re feeling very thankful that @LoLStats took the time to explain how to achieve that perfectly here: https://docs.google.com/document/d/1vGokwMXPQItZmTHZQbTO4qwj_SQymFhRS_nJmiH0K3w/edit

Later in that doc, he suggests that we should use this same concept and import and export all components into a single components.module.ts file. And then the same for pipes. Currently we’re running into several errors in trying to achieve this.

I have created a new ./components directory in my .src directory and created 3 components for use in my app, and rather than declaring and importing each of those components in my app.module.ts file, we would like to create a master components.module.ts and import/export them there as @LoLStats suggests …

Now… I realize i may be early in requesting help on this, but our team is just beyond late and we are anxious to move forward using Ionic 3 …can anyone provide an example showing how to achieve this yet?

If ionic 3 does not yet support this implementation BUT this WILL ultimately be the intended and suggested way in-which to import custom components … currently we can’t afford to wait and will likely just move fwd now and import all our components into the app.module for the time being … our question would then be … are we going to wind up having to do some serious re-factoring in-order to achieve this later? We can accept that, but we’re just trying to figure out if we are going to need to block out time for this down the line…

Any help at all greatly appreciated.

UPDATE: we really do not see how a single components.module that bundles ALL custom components could even work to any advantage in that if a page only requires ONE of the components, ALL components would be added to that page unnecessarily … UNLESS

HERES THE BIG QUESTION …

If in-fact the Ionic team IS SUGGESTING that we create a components.module and import ALL of our custom components into it … and then import that components.module into any pages that require any of the components within it …
DOES THIS SUGGEST that the build process will (eventually) REMOVE components that aren’t used EVERYWHERE the components.module is injected?

See below for the approach we went with for the time being…

6 Likes

I have a complimentary question to @disrupt’s:

Assuming HomePage is set as root, why do we want to lazy load it? Why not lazy load every page except the one that we’re certain will be loaded?

Thanks,
Ryan

2 Likes

It does.

That’s going to require a crystal ball. I’m not sure how (or even if) anybody is going to solve the code duplication problem, and what effect said solution would have on usage. My recommendation is that if you are importing your custom components in more than one page, hold off on using lazy page loading at all and throw everything in the app module.

If Indeed each component is only included in one page each (or two or three if they’re light and you’re willing to have their code included multiple times in your app binary), I had the syntax I documented in this post working for per-component modules, which I think is a better idea than an all-components module for mitigation of code duplication.

1 Like

@rapropos I would assume you don’t necessarily have to lazy load the home page? … just follow that approach for all pages that should?

That’s a more complicated question than it probably seems to (and should) be.

No, you don’t have to lazy load the home page. You don’t have to lazy load any page. It’s perfectly viable to throw everything in the AppModule as we have been doing before. Navigation remains using class constructors, not string literals.

If you want to start lazy loading any pages, you have to make sure of three key things:

  • they have their proper IonicPageModule.forChild() module and an @IonicPage decorator
  • everywhere you interact with navigation involving them you switch from class constructors to string literals
  • any custom components they include need to also be modularized and their modules explicitly imported in each lazy page that uses them. I do not believe it’s currently possible (although I would love to be proven wrong on this) to have custom components centralized.

@ryanlogsdon:

Assuming HomePage is set as root, why do we want to lazy load it?

One reason would be the fairly common use case where you have different landing (or “home”) pages depending on startup state. For example, if we have valid user credentials in storage, we go straight to a dashboard. If we don’t, we go to a login page. So there isn’t really a deterministic “home page” in that scenario, and it could make sense to lazily load one or the other depending on startup state.

5 Likes

Thought i’d show you what we are doing for the time being, albeit, we do NOT know if this is the correct approach:

We created a ./components directory in our ./src directory.

In it we have all of our custom components like this one for example:

./src/components/my-custom-component/
my-custom-component.html
my-custom-component.scss
my-custom-component.ts
my-custom-component.module // which is implemented like this:

import { NgModule } from '@angular/core';
import { MyCustomComponent } from './my-custom-component';

@NgModule({
  declarations: [
    MyCustomComponent,
  ],
  exports: [
    MyCustomComponent
  ]
})
export class MyCustomComponentModule {}

NOTE: we create all our custom components in this way but we do NOT import any of our component modules in our app.module. Instead, we only import a component module in the pages where they are needed like so:

./pages/my-page/
my-page.html
my-page.scss
my-page.ts
my-page.module // in it we import a component module like this:

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { MyCustomComponentModule } from '../../components/my-custom-component/my-custom-component.module';
import {MyPage } from './my-page';

@NgModule({
  declarations: [
    MyPage,
  ],
  imports: [
    MyCustomComponentModule,
    IonicPageModule.forChild(MyPage),
  ],
  exports: [
   //  MyCustomComponentModule, // UPDATE: exporting the component is not necessary
    MyPage
  ]
})
export class MyPageModule {}

And then in the template my-page.html …

<my-custom-component></my-custom-component>

We have tested this and it works … Can anyone confirm this implementation is OK?

9 Likes

I would say I think it’s about as OK as you can get, with the ongoing caveat that if any other pages are importing MyCustomComponentModule, they will get a duplicated set of all of its code that will increase your overall app binary size. I don’t believe there is any need for ProfilePageModule to export MyCustomComponentModule, although it probably won’t cause any actual damage to do so.

2 Likes

Hey thanks SO much … yeah, obviously i’d like to avoid increasing the app binary size if possible, but is it also safe to assume, while the app binary size might be larger, since we are lazy loading the component only when needed, we should still see faster app load times which is the whole reason for taking this approach in the first place?

And you are correct btw, exporting the component module from the page module is not necessary.

1 Like

Obviously every app is going to see different results here. With the one that I tried converting everything to lazy loading, I saw no measurable benefit to startup time and a roughly 1/3rd balloon in app binary size. That app may be atypical, because it contained several fairly heavy custom components that are included in several different pages, including one that is the main landing page for registered users, so it would be the first page seen in most interaction with the app.

I appreciate all the work that the webpack and Ionic folks have done with code splitting and lazy loading, and don’t want to be seen as just throwing shade on it. The code duplication is pretty insidious, though, because you won’t necessarily really see how it is affecting things unless you look fairly carefully.

1 Like

Agreed … It will be interesting to see if we run into the same result performance wise vs binary size …

Thanks again for taking the time to respond.

HERES THE BIG QUESTION … If in-fact the Ionic team IS SUGGESTING that we create a components.module and import ALL of our custom components into it … and then import that components.module into any pages that require any of the components within it …
DOES THIS SUGGEST that the build process will (eventually) REMOVE components that aren’t used EVERYWHERE the components.module is injected?

1 Like

It seems the Ionic team is working a lot on dead code removal and latest ionic-app-scripts are going to be released with manual treeshaking enabled by default. So, I assume that build process will remove the unused code.

1 Like

Mike has an example here: https://github.com/mhartington/star-track-ionic

2 Likes

All what you have to do is to wait till ionic team finishes the part of lazy loading as it’s now as i know is still in development and monitoring mode, however they stated that app loading time is much better now, but still this is actually affecting the bigger ionic projects and the code readability in a team work environment.

I’d love to help if you need any

1 Like

we are following the instructions from https://docs.google.com/document/d/1vGokwMXPQItZmTHZQbTO4qwj_SQymFhRS_nJmiH0K3w/edit which works… until today… for a component which is used in a popover (inside another component) I was getting an error (…could not find the module… make sure to include it in entrypoints) so after a couple of hours of fiddling around and pair coding, eventually I’ve created a normal module and imported it in the app.module.ts. That means that I have a single components.module.ts that import all the components except 1 which is imported in the app.module.ts.

The point that I’d like to make is that the code is getting very complicated and there is too much code to understand to do so little…

Is Ionic going in the traditional Java way with a lot of boiler plate ?

Your point here is very interesting, and I would first to clarify one thing here before digging down into details:-
In modern scripting languages you have two patterns FP (Functional Programming) and OOP (Object Oriented Programming), you can find details for this topic in this URL.
Ionic Platform is based on Angular 4 as it’s their core and Angular 4 is following the pattern of OOP (Object Oriented Programming) like JAVA programming but in some small differences, since Angular 4 are basically using Microsoft Typescript engine to make the OOP pattern happen.

Regarding your issue here in terms of code maintainability you have to read this article from Ionic team member Mike, in which he puts the lazy loading and the latest framework updates into great deal of understanding, in which you will choose your design pattern for the code you are writing and minimize the efforts as mush as possible for later code changes.

http://blog.ionic.io/ionic-and-lazy-loading-pt-2/

Reactive programming (e.g., Observables) feels much closer to functional programming than to OOP. Also, Dependency Injection is a way to get away from some structural problems created by OOP. If you program Angular/TypeScript asynchronously, I think it’s a lot closer to a strongly typed functional language than it is to an Object Oriented language. That’s the main theoretical reason I use interfaces instead of classes to define objects in Ionic. The flavor of the framework is to localize information and structure as much as possible. And that seems especially important if you implement lazy loading.

2 Likes

following the Option 2 from the that blog (https://github.com/mhartington/lazyLoad2-components/tree/common-modules) how do you use a component inside another coponent ?
If I export the component in components.module.ts I get "Error: No component factory found for MyComponent. Did you add it to @NgModule.entryComponents?". (adding it to entryComponents is not helping)

Similar error if I create a module for that component and import it in components.module.ts

any ideas ?

I don’t see an “Option 2” in the link you provided, but I can tell you that at one point I had the strategy described in this post working for subcomponents.

by “Option 2” I refer to the previously cited blog post

Here are the attempts: importing a module in the components.module.ts and exporting a component

Your links are broken for me.