Ionic 4: Create shared component UP-TO-DATE example please

I cannot work out for the life of me how to make a shared component in Ionic 4. I’ve read loads of tutorials and they all seem out of date.

Please can someone give me a SIMPLE example of how to add a shared, top-level component and how to use it in a page?

Thanks

Presumably you’re referring to the lazy-loading stuff? If you’ve searched many forum threads on this topic here, you’re probably familiar with my opinion on the general topic, which is “it’s not worth the hassle”.

So if you want to join this team, get rid of all .module.ts files from your code, never use the Ionic generators, change all the garbage in the routing module to look like good old:

{ path: 'home', component: HomePage },

put all that back in the AppModule, along with adding HomePage to the declarations stanza there. Now, to make a component:

@Component({
  selector: "app-greeter",
  template: "<p>hello world</p>",
})
export class GreeterComponent {}

Also add it to AppModule.declarations, put <app-greeter></app-greeter> in a page, see “hello world”, be happy, move on to doing something far more interesting and productive than futzing around with lots of irritating boilerplate. Pet your cat, for instance.

However, if you are really one of the 0.001% of developers that is writing a massive app that is receiving accolades for absolutely everything aside from its poor startup performance, read on.

First, forget about “shared”. In lazy-loading land, it’s everybody for themselves. Do not try to put more than one thing in a module. Again, we’ll start with the blank starter template. Leave GreeterComponent as is, and give it a friend in greeter.module.ts:

@NgModule({
  declarations: [GreeterComponent],
  exports: [GreeterComponent],
})
export class GreeterModule {
}

Go to the module of any page that is incorporating this component (such as home.module.ts in our blank template), and add GreeterModule to the imports stanza. Again, done. See “hello world”. Rejoice.

Now if the component is doing anything aside from saying hello, you are going to have to worry about importing stuff like IonicModule and FormsModule into GreeterModule. This is the part where I decided all of this dependency management is something computers are more suited for doing than I am, which is why I no longer bother with any of this.

You’re wonderful, I have spent 1 day on this and decided that all the added complexity of lazy loading is utterly pointless.

So… I’ve got 2 pages now:

  • Home
  • Intro

I’ve got 2 module files left in the project:

  • app.module.ts
  • app-routing.module.ts

That what you meant by getting rid of all the other .module.ts crap?

Home and Intro.

App.routing.module:

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { HomePage } from './pages/home/home.page';
import { IntroPage } from './pages/intro/intro.page';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', component: HomePage },
  { path: 'intro', component: IntroPage },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

… and ny app.module.ts is like it was with Ionic 3 setup, everything loads now.

Look correct to you?

1 Like

More or less. You can even go the final yard and shift routes into app.module.ts, the corresponding RouterModule.forRoot line into AppModule's imports as just:

RouterModule.forRoot(routes)

…and toss app.routing.module on the flaming module trash pile as well.

1 Like

Legend.

Where can I donate you a beer?

I just want to say a big big thank you. For some reason most of the tutorials online have you create yet another differently named module just so you can share it. I was super confused by all but reading your answer made me understand the relationship between component and modules better and I created a module using the same naming convention as my existing, shared component and then imported that module everywhere instead of trying to import the component underneath.

You don’t have to answer this but I am wondering if one just can’t import a component but can import a module that is why we must wrap a module around a component and use that.

Thanks!
Ray

It’s one of those gray areas where “can” and “can’t” don’t really live. It’s more of the land of “easier” vs. “harder”, but maybe a 10,000-meter overview of the problem all this was designed to solve is in order.

JavaScript was built in about a week and a half by more or less one person, in the early days of the web when “web designers” were clamoring for ways to make websites more sparkly. It wasn’t intended for managing big codebases, and therefore isn’t great at it.

Compiled languages like C get written in source form in *.c files. The compiler turns those into object code *.o files. A linker mashes all those *.o files together into an executable. At that stage, especially when third-party libraries get involved, a naïve linker runs into a “dead code” problem, whereby a bunch of useless cruft gets put in the executable.

The modern webapp build process emulates a lot of this same dynamic. You write TypeScript, it gets transpiled into JavaScript, that JavaScript gets munged further to shorten the names of variables and functions and so on, and then all that stuff gets mashed together by a bundler like webpack.

In *.o files, every public entry point - any code that can conceivably be called from anywhere outside that file - is obvious. The compiler does this in order to make the linker’s job easier. That can’t be easily done in a webapp, because the “object code” format is still interpreted JavaScript, which doesn’t have proper access control built into the language like C does.

But developers still clamored for dead code removal, and therefore we the authors have to take responsibility for doing what a compiler should IMHO be doing. To make JavaScript amenable to the sort of static code analysis that is needed to say definitively “yep, we can safely leave this part out of the build entirely”, “nope, we need that around always”, or “well, we can stash this bit over here and avoid loading it until somebody clicks on the map tab” requires some help on the part of the author, and that’s where NgModule was born.

The Angular people decided (and this probably makes sense) that putting the intermodule entry point documentation stuff into the @Component decorator would be a bad idea, because it isn’t just individual components that need to get managed that way - pipes and services do as well, and in many cases, in groups.

If anybody’s still awake at this point, one last bit about overloaded terminology. There are two senses of “import” here, and to further complicate matters, “inject” often gets thrown into this blender as well.

When you import something at the top of a TypeScript file, that’s a source code level thing that says to the transpiler “yo, in order to properly process this file, you’re gonna need to read that one over there”. This can be individual components if you wish.

When you add something to the imports stanza of an NgModule, the transpiler doesn’t really care. It’s the bundler that takes that into account when deciding which of your code gets put in the same .js file that is loaded at runtime. This can’t be individual components, and must be other NgModules.

When you inject something into the constructor of a component or service, that’s a hint to the dependency injection system (DI) that operates at runtime. This predates the notion of NgModule and doesn’t work for individual components because the semantics don’t really make any sense for it to.

Hope that sort of answers your question.

1 Like

Awesome. Thanks much. Read the whole thing, will need to read again. Makes more sense now. I finally understood why my program worked fine in debug mode but falls apart when compiling for prod. The whole NgModule imports, declarations, entrycomponents are all a mystery to me and I have mostly been following tutorials and managed to jam things in places until they worked :blush: I am sure my CS professors would be pulling their hair out.

The good/bad thing is I still managed to finish an app without understanding all this but I think it is both time and I am finally ready to go read Angular documentations on this now.