2
votes

I have an app I’m re-developing in ionic which I did in ionic-v1 a year or so ago. I have multiple template types which a user will pick from and I would like to make a dynamic component which loads its templateURL based off the user configuration value. So everything is working as far as loading the component and getting the right template except when I go to run the app, it gives template errors as though it doesn’t know ngModel is an angular attribute. Here’s what I have:

import { Compiler, Component, AfterViewInit, OnInit, Injector, ViewChild, NgModule, NgModuleRef, ViewContainerRef } from '@angular/core';
import { NavController } from 'ionic-angular';

import { SomeService } from '../../app/services/app.services';

@Component({
    template: `<ng-container #dynamicvc></ng-container>`
})
export class DynamicHome implements OnInit {
    @ViewChild('dynamicvc', { read: ViewContainerRef }) container;
    ehi: any;
    sponsorGroups: any[];
    baseUrl: string;
    selectedSegment: string;

    constructor(private compiler: Compiler, private injector: Injector, private moduleRef: NgModuleRef<any>, private someSvc: SomeService, private navCtrl: NavController) {
        let vm = this;

        vm.selectedSegment = 'home';
    }

    ngAfterViewInit() {
        let vm = this;

        const MaterialCardsSpookyHomeComponent = Component({ templateUrl: './materialcardsspooky/materialcardsspooky-home.html' })(class MaterialCardsSpookyHomeComponent { });
        const MaterialCardsSpookyHomeModule = NgModule({ declarations: [MaterialCardsSpookyHomeComponent]})(class MaterialCardsSpookyHomeModule { });

        let moduleToUse = null;
        switch (vm.someSvc.template.toLowerCase()) {
            case 'materialcardsspooky':
                moduleToUse = MaterialCardsSpookyHomeModule;
                break;
        }

        if (moduleToUse) {
            vm.compiler.compileModuleAndAllComponentsAsync(moduleToUse).then((factories) => {
                const f = factories.componentFactories[0];
                const cmpRef = f.create(vm.injector, [], null, vm.moduleRef);
                cmpRef.instance.ehi = vm.ehi;
                cmpRef.instance.sponsorGroups = vm.sponsorGroups;
                cmpRef.instance.baseUrl = vm.baseUrl;
                cmpRef.instance.selectedSegment = vm.selectedSegment;
                vm.container.insert(cmpRef.hostView);
            });
        }
    }

    ngOnInit() {
        let vm = this;

    }
}

Here's my template:

<ion-header>
  <ion-toolbar no-border-top no-border-bottom>
    <ion-segment [(ngModel)]="selectedSegment">
      <ion-segment-button value="home" no-border-bottom>
        <ion-icon name="home"></ion-icon>
      </ion-segment-button>
    </ion-segment>
  </ion-toolbar>
</ion-header>
<ion-content>
  <div [ngSwitch]="selectedSegment">
    <div *ngSwitchCase="'home'">
        ...
    </div>
  </div>
</ion-content>

And here are my errors in Chrome Dev Tools:

Can't bind to 'ngModel' since it isn't a known property of 'ion-segment'.

  1. If 'ion-segment' is an Angular component and it has 'ngModel' input, then verify that it is part of this module.
  2. If 'ion-segment' 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. ("

Any ideas how or even if I can accomplish this? Also, using VS2017 and standard Ionic template with default build scripts, i.e. webpack.

1
I'm not sure it is possible but you can try to add this to your the NgModule: imports: [ IonicPageModule.forChild(MaterialCardsSpookyHomeComponent) ]. - David
Nice! Thanks! That's on the right track for sure. That got me past the errors trying to use angular modules like ngModel, but now I'm getting an error regarding the use of a custom component I use in my template. It says it doesn't recognize it so trying to figure out how to get that one to work as well... - shinsnake
Good, I'm glad I could help you a bit. The custom component also needs to be in the imports array of the module if its declared in another module and in the declarations array if its not already declared in another module. - David
Yeah, that's where I'm getting lost now. I put it in imports and get this error: Error: Unexpected directive 'ImageSliderComponent' imported by the module 'MaterialCardsSpookyHomeModule'. Please add a @NgModule annotation. So I changed it to the declarations and then I get the "already imported error." So just for test purposes, I take it out of my app.module and then it tells me it doesn't recognize the custom attribute. - shinsnake
Haha! It was a different component, I removed the component from the module for the other components. Thanks for the help, I appreciate it. I'll let you know if that was the only problem so you can change your suggestion to an answer and I can mark it as the answer. - shinsnake

1 Answers

0
votes

To make Angular recognize ionics custom components the following is needed in the imports array of the NgModule:

imports: [ IonicPageModule.forChild(MaterialCardsSpookyHomeComponent) ]

Also every custom component thats used somewhere in the module needs to be in the imports array if its already declared and exported in another module and in the declarations array if its not already declared in another module.