Steps to create working custom components

I’m new to Ionic, I’m using v4, I’m new to Angular and I’m new to Typescript too, so please bear with me and forgive me if I make dumb questions. I’m on Linux.

I’ve started a new app with

$ ionic start
[...]
Project name: comptest
Framework: Angular
Starter template: tabs
[...]

First things first, I try it:

$ cd comptest
$ ionic serve -l
[...]
? Install @ionic/lab? Yes
[...]

the browser opens up and the app works ok. Now I try to generate a new custom component:

$ ionic generate component mySpan1
[...]
[OK] Generated component!

That created some files such as my-span1-component.ts:

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

@Component({
  selector: 'app-my-span1',
  templateUrl: './my-span1.component.html',
  styleUrls: ['./my-span1.component.scss'],
})
export class MySpan1Component {

  constructor() { }

}

I edited the selector and made it look like my-span1 instead of app-my-span1, but I didn’t touch anything else, not even the html <p> tag that in future will become a <span> instead for obvious reasons. Then I tried to use my-span1 in tab1 page, so I edit tab1.page.html and add my-span1 tag:

<ion-content>
    <my-span1></my-span1>
    <ion-card class="welcome-card">
      <img src="/assets/shapes.svg" alt="" />

Now tab1 doesn’t work anymore. In the browser console I get the error:

‘my-span1’ is not a known element

Google is my friend, and it leads me here: Can’t get ionic v4 CLI generated component to work

I’ve tried to follow the suggestions in that thread, but when it came down to creating a new module I entered the unknown (for me) territory. I tried ionic generate module components, and it worked, then I declared MySpan1Component in that new module:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MySpan1Component } from '../my-span1/my-span1.component';

@NgModule({
  declarations: [
    MySpan1Component
  ],
  imports: [
    CommonModule,
  ]
})
export class ComponentsModule { }

Then I imported the new module into the app module, like this:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ComponentsModule } from './components/components.module';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, ComponentsModule],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

But I still get the same error in the console, e.g. 'my-span1' is not a known element.

There the thread ends, its author solved the problem with that, but the same is not true for me. And I don’t know what to do next.

Can you please guide me step by step (assuming I’m missing only 1 step or so), or hand me a link to some docs that guide me step by step?

Thanks in advance.

These are very totally opposite-of-dumb questions, that expose the fact (IMHO) that there are some parts of the typical Ionic development experience that cross the line from being useful to being “too cute for their own good”.

So some guidelines I have found useful:

  • ground yourself in Angular. go through the Tour of Heroes. yes, it seems long and daunting at first, but it really will contain the answers to 90% of the questions you are going to have.
  • maybe eventually you will find the generators useful. I’ve been working with Ionic for almost 4 years, and I’m yet to hit that point, though. I think there is real value in organizing your project and making the files yourself, because you understand how the pieces fit together and learn to recognize common situations where they aren’t, such as the one you’ve hit here.
  • which leads me to lazy loading. I think it causes way more problems than it solves, especially for non-colossal apps and people who are just starting out. so this is how I would solve your current problem:

A. Generate a new project with the “blank” starter instead of the “tabs” one.
B. Delete every file under src/app/ with module in its name, except for app.module.ts.
C. Replace the import of AppRoutingModule in app.module.ts with:

RouterModule.forRoot([
      {path: '', redirectTo: 'home', pathMatch: 'full'},
      {path: 'home', component: HomePage}
    ])

D. Add HomePage to the declarations stanza of AppModule while you’re there
E. Run ionic serve and make sure the world is your oyster.
F. Stop the ionic serve process
G. Now make a span1 directory and three files in it:

span1.component.ts

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

@Component({
  selector: 'my-span1',
  templateUrl: './span1.component.html',
  styleUrls: ['./span1.component.scss'],
})
export class Span1Component {}

span1.component.scss

empty for now, but must exist

span1.component.html

<span>hello world</span>

H. Put a <my-span></my-span> in your app.component.html somewhere
I. Add Span1Component to the declarations stanza of your app module, right next to HomePage.
J. Run ionic serve again and hopefully hello world will appear as well

If you decide instead that you really really want the lazy loading stuff, you are going to have to add MySpan1ComponentModule to the imports stanza of Tab1PageModule. Then you’re going to have to remember to do something similar absolutely every single time you want to use component X in component Y. That should fix things within your existing project, though.

2 Likes