IonicPage can't bind to custom components - Advice Needed

Firstly, top job on shipping Ionic 3. Having a bit of a play about with it but struggling to get to grips with importing custom components.

If I initialise a page, and it’s corresponding module as per the docs (see below), I get an error stating that my page template cannot bind to the properties of my custom components.

Error Output:

`core.es5.js:1085 ERROR Error: Uncaught (in promise): Error: Template parse errors:
Can’t bind to ‘locations’ since it isn’t a known property of ‘location-search-input’.

  1. If ‘location-search-input’ is an Angular component and it has ‘locations’ input, then verify that it is part of this module.
  2. If ‘location-search-input’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA’ to the ‘@NgModule.schemas’ of this component to suppress this message.
  3. To allow any property add ‘NO_ERRORS_SCHEMA’ to the ‘@NgModule.schemas’ of this component`

I simply reference my custom component in my markup as follows:
<location-search-input [locations]="searchLocations"></location-search-input>, which worked perfectly well prior to upgrading and switching to @IonicPage decorators.

Just for clarity here’s a snippet of my custom component, in which locations is declared as a property/input.

@Component({selector: 'location-search-input', templateUrl: './location-search-input.component.html'})

export class LocationSearchInput {

  @Input() locations: any[] = [];

  constructor(public navController: NavController, private googlePlacesService: GooglePlacesService) {
  }

}

I have a feeling I am perhaps supposed to declare/import my custom component in the page’s Module, but then again I’m not sure. Any advice would be greatly appreciated.

Page Module - as per docs

import {NgModule} from "@angular/core";
import {IonicPageModule} from "ionic-angular";
import {BrowsePage} from "./browse.page";


@NgModule({
  declarations: [BrowsePage],
  imports: [
    IonicPageModule.forChild(BrowsePage),
  ],
  entryComponents: [
    BrowsePage,
  ]
})
export class BrowsePageModule {
}

Thanks!

1 Like

Especially when you have custom components, I would recommend avoiding lazy page loading for the time being and just go back to throwing everything in a single app module. See app-scripts #867 for more context.

1 Like

@rapropos, Yes I did run into some issues, but in the end I opted for the approach recommended in the Ionic 3 lazy loading google doc,

One module that imports all component classes, no individual modules for each component
components/
components.module.ts (ComponentsModule)
imports and exports all components the user creates

component1/
   component1.ts
   component1.html

component2/
  component2.ts
  component2.html

In my case, this resulted in a components.module.ts that looks like the following:

import {NgModule} from "@angular/core";
import {IonicPageModule} from "ionic-angular";
import {GoogleMapsLocationSearchInput} from "./google-maps-location-search-input/google-maps-location-search-input";
import {ProductCardComponent} from "./product-card/product-card";
import {Ng2FlatpickrComponent} from "ng2-flatpickr/ng2-flatpickr";

@NgModule({
  imports: [
    IonicPageModule.forChild(GoogleMapsLocationSearchInput),
    IonicPageModule.forChild(ProductCardComponent),
    IonicPageModule.forChild(Ng2FlatpickrComponent),
  ],
  declarations: [
    GoogleMapsLocationSearchInput,
    ProductCardComponent,
    Ng2FlatpickrComponent
  ],
  exports: [
    GoogleMapsLocationSearchInput,
    ProductCardComponent,
    Ng2FlatpickrComponent
  ]
})
export class ComponentsModule {
}

This ComponentsModule is then added to the imports : [ ] of each page module that requires it.

Converting my existing app/website to this new procedure has had quite an effect on bundle size, something we are trying hard to reduce (25+ page app)

1 Like

I’m curious whether you benchmarked it simply using Angular 4 and Ionic Native 3 before doing all the lazy page loading stuff. I would be surprised if the overall bundle size wasn’t smaller than what you have now, because I bet all the code in your ComponentsModule is duplicated in every page module chunk that imports it. That’s what happened to me and why I gave up on lazy page loading. Angular 4 and Ionic Native 3 on their own, without code splitting by page, do a pretty significant job of reducing bundle size.

@rapropos You are correct. I cannot recall if the size difference was before or after I update to Angular4 and Ionic 3.

My bundle went from 6.9Mb to 5.4Mb on simple ionic-serve.

You are also correct, that upon investigating the generated 1-n.main.js that are lazy loaded, that they all contain the code for my components, amongst other things.

I have invested quite a bit of time to get setup with @IonicPage and re-done the routing in the app for this, is there a way I can continue with my new structure gaining the benefits of the routing aspects, but disable the lazy loading so that it is simply bundled together?

@mhartington, I used your https://github.com/mhartington/star-track-ionic repo for guidance in my migration from v2-v3, perhaps you know if @IonicPage is usable without the lazy loading aspect?

I guess this might depend on what you consider “the benefits of the routing aspects” to be.

Well, the ability to declare a ‘segment’ as the url path to nav to the page.
Previously we had a deeplinkerConfig that we had to maintain in the app.module.ts, but it became quite busy, and also didn’t seem to work as cleanly. More often than not we would see URLs becoming a stacked version of the paths.
For example:

Page 1: Segment - '/pageOne'
Page 2: Segment - '/pageTwo'

Sometimes when naving back and forth the url path would become /pageOne/pageTwo, wheras this doesn’t happen since moving to @IonicPage.

The routing is working well for us, the only downside as you mentioned, is that all components are being imported on each page, even when they are not needing, causing a bit of overhead. I know you said you had this issue too. Did you remove @IonicPage altogether, or can you still use @IonicPage, but have them all imported / declared in the main app.module.ts?

I did completely get rid of @IonicPage, but I don’t think you have to. You should be OK just declaring all your components in the AppModule. Since they’re not pages, there shouldn’t ever be any need for them to interact with the NavController, so the issue of class names as strings versus class constructor functions as nav parameters shouldn’t come up for you.

Hey, I’m sharing some concerns as well.
What I’m pushed to assume is that we should treat custom components as IonicPage?
I’m fairly new and I probably wrongly assumed that anything declared in the app.module would be usable in every child component/page?

I went with bundling the components in a single module as advised in the doc, without IonicPage decorator. Then if I call the ComponentsModule in the imports of the app, it all breaks.

By keeping them separated and called in the declarations of app.module, it all works, although I get errors while using Ionic components inside my customs. Plus can’t use custom directives.

Oddly it seems that nobody is talking about components in Ionic3

1 Like

Can you provide a minimal accessible repo to reproduce this?

I’ll try to describe the steps.

  1. Generate component with cli
  2. That component would be only a wrapper or other ionic ones, such as a grid
  3. Component imported in app.module, added to declarations
  4. added tag to app.html
    App works, although I get an error in Vscode for every component:

severity: 'Error’
message: '‘ion-grid’ is not a known element:

  1. If ‘ion-grid’ is an Angular component, then verify that it is part of this module.
  2. If ‘ion-grid’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA’ to the ‘@NgModule.schemas’ of this component to suppress this message.'
    at: '1,1’
    source: ‘Angular’

So I’m definitely missing something as in error 1. but nothing I found so far solved that.

I’ve never used VSCode, so can’t speak to that. Did you delete the “component.module.ts” file that the generator makes and remove the IonicPage decoration from its class?

I tried both those things, as in doing as before ionic3. Oddly enough doing the same thing in another app not yet converted(only updated an build with errors), was not showing errors.

What I just tried, and it worked, was removing those components, serve, no more errors, adding back the parts, still no errors… cool.

The main question stays. How to handle custom components, without adding them and duplicating the code.

adding: Same goes for custom directives, if I import them in the app.module, wouldn’t I be able to use them in the children of app?

Thanks for the help @rapropos

@pjhartin you can bind that. the main advise is import custom component in your pageModule.ts inside

@NgModule({
  declarations: [ 
       your custom component
]})

Hi @pjhartin -

I did try the same approach, I am using lazyloading approach in ionic 3. Tried to use a third party plugin name “ngx-uploadcare-widget” but it said the UCwidget is not defined and please import it into the page… after several hours of debugging I found that it had to be moved to a shared component and then import into individual pages, by doing this all the errors were fixed and app was working fine on browser and on debug build. but when I did ionic cordova build --release --prod the app seems to be hung in the sense it was showing me blank white screen, since we cant debug prod I was left clueless on what is happening, later in one of the forms I learnt that we can add a call back function to this.navCtrl.setRoot(‘HomePage’). When I did that and generated a build I was able to see an error saying (ReferenceError APP_VERSION is not defined for Uploadcare) and now I again i have no idea what to do :(, The team at uploadcare says it could be an issue with Ionic lazyloading and they asked me to check in the Ionic forum, so I posting this. If you / anyone here have encountered a similar issue or understands this problem request you to please help me in fixing this as I have been stuck here since a month and not sure what to do.

Fyi:

//Shared Components Module

import { NgModule } from ‘@angular/core’;
import { UcWidgetComponent } from ‘ngx-uploadcare-widget’;

@NgModule({
declarations: [UcWidgetComponent],
imports: [],
exports: [UcWidgetComponent]
})
export class ComponentsModule {}

App.module.ts file

//Shared Component
import { ComponentsModule } from ‘…/components/components.module’;

@NgModule({
declarations: [
MyApp,
],
imports: [
HttpClientModule,
HttpModule,
BrowserModule,
IonicModule.forRoot(MyApp),
ComponentsModule,
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
],

//Home Module (home.module.ts file)

import { ComponentsModule } from ‘…/…/components/components.module’;

@NgModule({
declarations: [
HomePage,
],
imports: [
IonicPageModule.forChild(HomePage),
ComponentsModule,
],
})
export class HomePageModule {}

Thanks in advance!