web-dev-qa-db-fra.com

Ionic3 - Erreur d'analyse de modèle dans le composant personnalisé

J'ai travaillé sur un projet pour me tenir au courant de toutes les modifications apportées à Ionic3 récemment. Il est principalement basé sur l'application de conférence de démonstration de Ionic-Team .

J'essaie toujours de pencher angulaire/ionique, j'avais la plupart de l'application en cours d'exécution dans ionic2. Le chargement paresseux de components.module partagé m'a gâché pendant un certain temps. Mais maintenant, j'ai rencontré un problème en utilisant les composants intégrés d'Ionic dans mes composants personnalisés. J'essaie de créer un composant personnalisé qui utilise une carte ionique pour afficher les informations qui lui sont transmises via un @Input.

L'erreur qu'il donne:

Error: Template parse errors:
'ion-card-header' is not a known element:
1. If 'ion-card-header' is an Angular component, then verify that it is part of this module.
2. If 'ion-card-header' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
<ion-card>

[ERROR ->]<ion-card-header>
    <h3>{{ recipe.title }}</h3>
</ion-card-header>
"): ng:///ComponentsModule/RecipeCardComponent.html@3:2
'ion-card-content' is not a known element:
1. If 'ion-card-content' is an Angular component, then verify that it is part of this module.
2. If 'ion-card-content' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
</ion-card-header>

[ERROR ->]<ion-card-content>
    <p>{{ recipe.description }}</p>
</ion-card-content>
"): ng:///ComponentsModule/RecipeCardComponent.html@7:2
'ion-card' is not a known element:
1. If 'ion-card' is an Angular component, then verify that it is part of this module.
2. If 'ion-card' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
[ERROR ->]<ion-card>

<ion-card-header>
"): ng:///ComponentsModule/RecipeCardComponent.html@1:0

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { IonicStorageModule } from '@ionic/storage';
import { HttpModule } from '@angular/http';

import { UserDataProvider } from '../providers/user-data/user-data';
import { RecipeDataProvider } from '../providers/recipe-data/recipe-data';

import { ComponentsModule } from '../components/components.module';
import { PipesModule } from '../pipes/pipes.module';

import { MyApp } from './app.component';
import { IntroPage } from '../pages/intro/intro';
import { RecipeListPage } from '../pages/recipe-list/recipe-list';
import { FavoratesPage } from '../pages/favorates/favorates';
import { CookbooksPage } from '../pages/cookbooks/cookbooks';
import { QuickTimerPage } from '../pages/quick-timer/quick-timer';
import { ShoppingListPage } from '../pages/shopping-list/shopping-list';
import { SettingsPage } from '../pages/settings/settings';
import { AboutPage } from '../pages/about/about';
import { DatabaseProvider } from '../providers/database/database';
import { UtilitiesProvider } from '../providers/utilities/utilities';


@NgModule({
declarations: [
    MyApp,
    IntroPage,
    RecipeListPage,
    FavoratesPage,
    CookbooksPage,
    QuickTimerPage,
    ShoppingListPage,
    SettingsPage,
    AboutPage
],
imports: [
    BrowserModule,
    HttpModule,
    IonicModule.forRoot(MyApp, {}, {
    links: [
        { component: IntroPage, name: 'IntroPage', segment: 'intro' },
        { component: RecipeListPage, name: 'RecipeListPage', segment: 'recipe-list' },
        { component: FavoratesPage, name: 'FavoratesPage', segment: 'favorates' },
        { component: CookbooksPage, name: 'CookbooksPage', segment: 'cookbooks' },
        { component: QuickTimerPage, name: 'QuickTimerPage', segment: 'quick-timer' },
        { component: ShoppingListPage, name: 'ShoppingListPage', segment: 'shopping-list' },
        { component: SettingsPage, name: 'SettingsPage', segment: 'settings' },
        { component: AboutPage, name: 'AboutPage', segment: 'settings' },
    ]
    }),
    IonicStorageModule.forRoot({
    name: '__mise',
    driverOrder: ['indexeddb', 'websql', 'localstorage']
    }),
    ComponentsModule,
    PipesModule
],
bootstrap: [IonicApp],
entryComponents: [
    MyApp,
    IntroPage,
    RecipeListPage,
    FavoratesPage,
    CookbooksPage,
    QuickTimerPage,
    ShoppingListPage,
    SettingsPage,
    AboutPage
],
providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    UserDataProvider,
    RecipeDataProvider,
    DatabaseProvider,
    UtilitiesProvider
]
})
export class AppModule {}

app.component.ts

import { Component, ViewChild } from '@angular/core';
import { Nav, Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

import { Storage } from '@ionic/storage';

import { UserDataProvider } from '../providers/user-data/user-data';
import { RecipeDataProvider } from '../providers/recipe-data/recipe-data';

import { IntroPage } from '../pages/intro/intro';
import { RecipeListPage } from '../pages/recipe-list/recipe-list';


export interface PageInterface {
title: string;
name: string;
component: any;
icon: string;
logsOut?: boolean;
index?: number;
tabName?: string;
tabComponent?: any;
}

@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;

pages: Array<{title: string, component: any}>;

// List of pages that can be navigated to from the left menu
// the left menu only works after login
// the login page disables the left menu
appPages: PageInterface[] = [
    { title: 'intro', name: 'IntroPage', component: 'IntroPage', icon: 'help' },
    { title: 'Recipes', name: 'RecipeListPage', component: 'RecipeListPage', icon: 'list' },
    { title: 'Favorates', name: 'FavoratesPage', component: 'FavoratesPage', icon: 'star' },
    { title: 'Cookbooks', name: 'CookbooksPage', component: 'CookbooksPage', icon: 'folder' },
    { title: 'Quick timer', name: 'QuickTimerPage', component: 'QuickTimerPage', icon: 'help' },
    { title: 'Shopping list', name: 'ShoppingListPage', component: 'ShoppingListPage', icon: 'help' },
    { title: 'Settings', name: 'SettingsPage', component: 'SettingsPage', icon: 'help' },
    { title: 'About', name: 'AboutPage', component: 'AboutPage', icon: 'help' },
];

rootPage: any;

constructor(
    public platform: Platform, 
    public statusBar: StatusBar,
    public storage: Storage, 
    public splashScreen: SplashScreen,
    public userData: UserDataProvider,
    public recipeData: RecipeDataProvider
) {

    // Check if the user has already seen the tutorial
    this.storage.get('hasSeenTutorial')
    .then((hasSeenTutorial) => {
        if (hasSeenTutorial) {
        console.log('HasSeenTurorial = true');
        this.rootPage = RecipeListPage;
        } else {
        console.log('HasSeenTurorial = false');
        this.rootPage = IntroPage;
        }
        this.platformReady()
    });

    // load the recipe data
    this.recipeData.init();

}

openPage(page: PageInterface) {
    this.nav.setRoot(page.name);
}

openTutorial() {
    this.nav.setRoot(IntroPage);
}

listenToLoginEvents() {
    // this.events.subscribe('user:login', () => {
    //   this.enableMenu(true);
    // });

    // this.events.subscribe('user:signup', () => {
    //   this.enableMenu(true);
    // });

    // this.events.subscribe('user:logout', () => {
    //   this.enableMenu(false);
    // });
}

enableMenu(loggedIn: boolean) {
    // this.menu.enable(loggedIn, 'loggedInMenu');
    // this.menu.enable(!loggedIn, 'loggedOutMenu');
}

platformReady() {
    // Call any initial plugins when ready
    this.platform.ready().then(() => {
    this.splashScreen.hide();
    });
}

isActive(page: PageInterface) {
    let childNav = this.nav.getActiveChildNavs()[0];

    // Tabs are a special case because they have their own navigation
    if (childNav) {
    if (childNav.getSelected() && childNav.getSelected().root === page.tabComponent) {
        return 'primary';
    }
    return;
    }

    if (this.nav.getActive() && this.nav.getActive().name === page.name) {
    return 'primary';
    }
    return;
}
}

recette-list.ts

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';

import { RecipeDataProvider } from '../../providers/recipe-data/recipe-data';
import { UserDataProvider } from '../../providers/user-data/user-data';

import { ComponentsModule } from '../../components/components.module';


@IonicPage()
@Component({
    selector: 'page-recipe-list',
    templateUrl: 'recipe-list.html',
})
export class RecipeListPage {

    recipeList: any = [];
    things: any = ["One", "Two", "Three"];

    constructor(
        public navCtrl: NavController, 
        public navParams: NavParams,
        public recipeData: RecipeDataProvider,
        public userData: UserDataProvider
    ) {}

    goToRecipe(recipeId: any){

    };

    printRecipeList(){
        console.log(this.recipeList);
    }


    ionViewDidLoad() {
        console.log('ionViewDidLoad RecipeListPage');
        this.recipeList = this.recipeData.recipeList;
    }

}

recetteListe.html

<ion-header>
    <ion-navbar>
        <button ion-button menuToggle>
        <ion-icon name="menu"></ion-icon>
        </button>
        <ion-title>Recipe List</ion-title>
    </ion-navbar>
</ion-header>


<ion-content padding>
    <recipe-card *ngFor="let recipe of recipeList; let i = index" [recipe]="recipeList[i]"></recipe-card>
</ion-content>

components.module.ts

import { NgModule } from '@angular/core';
import { RecipeCardComponent } from './recipe-card/recipe-card';
import { TimelineComponent } from './timeline/timeline';
import { TimerComponent } from './timer/timer';
@NgModule({
    declarations: [RecipeCardComponent,
    TimelineComponent,
    TimerComponent],
    imports: [],
    exports: [RecipeCardComponent,
    TimelineComponent,
    TimerComponent]
})
export class ComponentsModule {}

recette-card.ts

import { Component, Input } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
// Providers
import { UserDataProvider } from "../../providers/user-data/user-data";
import { RecipeDataProvider } from "../../providers/recipe-data/recipe-data";
// Model
import { Recipe } from "../../models/recipe-model";
// Pages
import { RecipeDetailPage } from "../../pages/recipe-detail/recipe-detail";


@Component({
    selector: 'recipe-card',
    templateUrl: 'recipe-card.html'
})
export class RecipeCardComponent {

    @Input() recipe: Recipe;

    constructor(
        public navCtrl: NavController,
        public navParams: NavParams,
        private userData: UserDataProvider,
        private recipeData: RecipeDataProvider
    ) {}

    delete(){
        console.log('dalete recipe');
        // this.recipeData.delete(this.recipe.recipeId);
    }

    goToRecipeDetailPage() {
        console.log('goToRecipe() recipe');
        this.navCtrl.Push('RecipeDetailPage', this.recipe);
    }

}

recette-carte.html

<ion-card>

    <ion-card-header>
        <h3>{{ recipe.title }}</h3>
    </ion-card-header>

    <ion-card-content>
        <p>{{ recipe.description }}</p>
    </ion-card-content>

</ion-card>

pour être complet, voici mon ionic sys info:

cli packages: (...)

    @ionic/cli-utils  : 1.12.0
    ionic (Ionic CLI) : 3.12.0

global packages:

    cordova (Cordova CLI) : 7.0.1

local packages:

    @ionic/app-scripts : 3.0.0
    Cordova Platforms  : none
    Ionic Framework    : ionic-angular 3.7.1

System:

    Node : v6.10.2
    npm  : 3.10.7
    OS   : Windows 10

Misc:

    backend : pro

Si j'utilise le code suivant pour recette-card.html cela fonctionne très bien.

<h3>{{ recipe.title }}</h3>
<p>{{ recipe.description }}</p>

Mais si j'inclus le code suivant, c'est l'icône ionique qui déclenche l'erreur d'analyse du modèle.

<h3>{{ recipe.title }}</h3>

<p>{{ recipe.description }}</p>

<button ion-button icon-left>
    <ion-icon name="home"></ion-icon>
    Left Icon
</button>

Il semblerait donc que l'accès aux composants imbriqués ionic intégrés au sein de votre propre composant personnalisé soit ce qui ne va pas. Cela pourrait être quelque chose de flagrant à voir avec le chargement paresseux ou le module de composants partagé, mais pour la vie de moi je ne peux pas le voir. Toute aide serait géniale :) Merci d'avance.

8
aidansmyth

Pour répondre à ma propre question :) | J'espère que cela aidera quelqu'un d'autre.

Pour utiliser les composants intégrés Ionics dans vos propres composants personnalisés, vous devez importer IonicModule dans votre components.module.ts et assurez-vous d'ajouter IonicModule aux importations @NgModule. Voici un avant/après.

avant

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

import { RecipeCardComponent } from './recipe-card/recipe-card';
import { TimelineComponent } from './timeline/timeline';
import { TimerComponent } from './timer/timer';

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

après

import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';

import { RecipeCardComponent } from './recipe-card/recipe-card';
import { TimelineComponent } from './timeline/timeline';
import { TimerComponent } from './timer/timer';

@NgModule({
    declarations: [RecipeCardComponent,
    TimelineComponent,
    TimerComponent],
    imports: [IonicModule],
    exports: [RecipeCardComponent,
    TimelineComponent,
    TimerComponent]
})
export class ComponentsModule {}
18
aidansmyth

Ajouter **IonicModule** dans ComponentsModule sans forRoot ()

imports: [IonicModule]
7
prasanthnv