Search code examples
angulartypescriptangular-directiveangular-componentsangular-module

Why to Export Component from One Module & Use in Another Module Component


I have been trying to work on this for long hours. However, I'm finally giving up and looking for community support on this. Thanks in advance for the help!

I have Web Projects Module which uses Webpage Component. I am importing Objects Module (which has a Navbar Component & ComponentLoaderDirective) in the Webprojects Module.

& I am trying to Load the Contents of navbar.html to webpage.component.html

Why does Angular force me to import NavbarComponent & ComponentLoaderDirective again in weboage.components.ts? Without the same I get below errors: Cannot find the name 'Navbar Component'. Cannot find the name 'ComponentLoaderDirective'.

I think I'm missing something here. Just not able to locate what is missing.

webprojects.module.ts

import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { Global } from '../../../class.global'; //Global Class Contains the Global Variables & Functions Being Used Throughout the Project.
import { StartupService } from 'src/app/services/shared/startup.service'; //StartupService is Used to Get the Project, Page, Object & Elements on Page Load.
import { ObjectsModule } from './objects/objects.module';
import { WebpageComponent } from './webpage/webpage.component'; //WebProjectsModule Uses WebpageComponent as the View (webpage.component.html) & Controller (webpage.component.html).

@NgModule({
  declarations: [WebpageComponent], //WebProject Module Uses the Webpage Component
  imports: [HttpClientModule, ObjectsModule],
  exports: [], //Webprojects Module Exports the Webpage Component to the App Module & Other Modules Using the WebProjects Module
  providers: [StartupService, Global] //StartupService, GlobalClass are Required for the WebProjects Module to Work.  
})
export class WebProjectsModule {}

webpage.component.ts

//Webpage Component is the First Component to be loaded Under the Webprojects Module.
import { Component, ViewChild, OnInit } from '@angular/core';
import { Global } from '../../../../class.global'; //Global Class Contains the Global Variables & Functions Being Used Throughout the Project.
import { Error } from '../../../../services/shared/error' //Error Class is Used to Display Errors
import { StartupService } from '../../../../services/shared/startup.service'; //StartupService is Required to Load the Project, Page, Object & Elements.
import { Project } from '../../../../services/projects/webprojects/project'; //Project Model is Required to Constrain the Type of Data Being Received from the Startup Service getProjects().
import { Projectpage } from '../../../../services/projects/webprojects/projectpage'; //Project Page Model is Required to Constrain the Type of Data Being Received from the Startup Service getProjectPage().
import { Pageobject } from '../../../../services/projects/webprojects/pageobject'; //Page Object Model is Required to Constrain the Type of Data Being Received from the Statup Service getPageObjects().
import { Property } from '../../../../services/projects/webprojects/property'; //Property Object Model is Required to Constrain the Type of Property Array Being Created for Objects & Elements.
import { SEOService } from '../../../../services/shared/seo.service'; //SEO Service is Used to Display the Page Title, Description, Other SEO Related Content on the Page.
import { ComponentLoaderDirective } from '../../../../services/shared/component-loader.directive';
import { NavbarComponent } from '../objects/navbar/navbar.component';

@Component({
  selector: 'app-webpage',
  templateUrl: './webpage.component.html',
  styleUrls: ['./webpage.component.scss']
})
export class WebpageComponent implements OnInit {
  @ViewChild(ComponentLoaderDirective, {static: true}) pageObjectLoader!: ComponentLoaderDirective;

  responseProject: Project[] = []; //Variable of Type Project Array Used to Store the Array of Projects Received from the Startup Service getProjects().
  project: any = {}; //Variable of Type Any Used to Store the Parameters of the Current Project being Loaded. Initializing with Empty Object.
  projectId: number = 0; //Variable of Type Number Used to Store the Id of the Current Project being Loaded. Initilizing with 0.
  projectType: string = 'web'; //Variable of Type String Used to Store the Type of the Current Project being Loaded. Initializing with web.
  responseProjectPage: Projectpage[] = []; //Variable of Type Projectpage Array Used to Store the Object of Projectpage Received from the Statup Service getProjectPage().
  projectPage: any = {}; //Aggregate of Type Object Used to Store the Parameters of Project Page, Project & Page. Initializing with Empty Object.
  projectPageProperties: Property[] = []; //Variable of Type Property[] Used to Store Array of Properties of Project Page.
  page: any = {}; //Variable of Type Any Used to Store the Parameters of the Current Page being Loaded.
  pageTitle: string = ''; //Variable of Type String Used to Store the Title of the Current Page being Loaded.
  pageDescription: string = ''; //Variable of Type String Used to Store the Description of the Current Page being Loaded.
  pageProperties: Property[] = []; //Variable of Type Property Used to Store the Properties of the Page being Loaded.
  pageType: string = 'home'; //Variable of Type String Used to Store the Type of Current Page being Loaded. Initializing with home by default to be loaded. Unless Overwritten by the Route.
  pageObjects: Pageobject[] = []; //Variable of Type Any Used to Store the Page & Object Parameters Relevant to the Page Being Loaded. Initializing with Empty Object.
  pageObjectProperties: Property[] = []; //Variable of Type Property[] Used to Store the Array of Page Object Properties.
  object: any = {}; //Variable of Type Any Used to Store the Object Relevant to the Page Being Loaded. Initializing with Empty Object.
  objectProperties: Property[] = []; //Variable of Type Property[] Used to Store the Array of Object Properties.
  properties: Property[] = []; //Variable of Type Property[] Used to Store Array of Properties.
  active: boolean = true; //Initializing with true. All the Requests to Get the Projects, ProjectPages & PageObjects are with Active = true.
  global = new Global();
  error = new Error();

  constructor(private startupService: StartupService, private seoService: SEOService) {};

  async ngOnInit(){
    try{
      this.responseProject = await this.startupService.getProjects(this.projectType,this.active).toPromise();  
      var i = this.responseProject.length;
      while(i--){
        if(this.responseProject[i].tags.indexOf(window.location.href) == -1){ //Using window.location.href to Get the Url Which Was Loaded & Get the Project Accordingly.
          this.responseProject.splice(i, 1); //Removes the Project from the Array if it is Not Relevant to the Current Project.
        }
      };

      if(this.responseProject.length > 0){ //Checks if Atleast 1 Project is Received.
        this.project = this.responseProject[0]; //Storing the Relevant Project in the Project Variable
        this.projectId = this.project.nid; //Storing the Relevant Project Id in the projectId Variable.

        this.responseProjectPage = await this.startupService.getProjectPage(this.projectId, this.pageType, this.active).toPromise();
        //Use the getPage() in StartupService to Get the Home Page of the Relevant Project Being Loaded.
        if(Object.keys(this.responseProjectPage).length === 1){ //Checks if the Array Received is Not Empty.          
          this.projectPage = this.responseProjectPage[0]; //Aggregate Object Received from the Database is Stored in projectPage Variable.
          this.projectPageProperties = this.projectPage.properties; //Project Page Properties which are a Part of the Aggregate Received is Separately Stored in projectPageProperties Variable.

          this.page = this.projectPage.page; //Page Object which is Part of the Aggregate Received is Separately Stored in page Variable.
          this.pageProperties = this.page.properties; //Page Properties which are a Part of the Page Object Received are Separately Stored in pageProperties Variable.
          
          //We Pick All the Properties from projectPageProperties & the Remaining Properties from pageProperties which are Not a Part of projectPageProperties.
          var namesToFilter = new Set(this.projectPageProperties.map(d => d.name)); //We Get All the Names in the projectPageProperties Array and Store them in namesToFilter Array.
          this.properties = [...this.projectPageProperties, ...this.pageProperties.filter(d => !namesToFilter.has(d.name))]; //We Merge Both the Arrays and Skip the Objects with Names in the namesToFilter Array.
          
          //Looping through the Properties Array to Get the Page Title & Description for the <head> Tag.
          this.properties.forEach((property: Property) => {   
            this.pageTitle = (property.name === 'pgTitle')?property.value:this.pageTitle;      
            this.pageDescription = (property.name === 'pgDescription')?property.value:this.pageDescription;
          });                
          this.seoService.updateTitle(this.pageTitle); //Using the SEO Service, We Update the <html><head><title> Tag of the Page.
          this.seoService.updateDescription(this.pageDescription); //Using the SEO SErvice, We Update the <html><head><meta = "description"> Tag is the Page.

          this.pageObjects = this.projectPage.pageobjects; //Storing the Page Objects Array in pageObjects Variable.
          if(this.pageObjects.length > 0){ //Checking if there is Atleast One Page Object.
            const viewContainerRef = this.pageObjectLoader.viewContainerRef; //We Use Instance of Component Loader Directive to Define Where to Load the Child Component.

            this.pageObjects.forEach((pageObject: Pageobject) => { //Looping through the Page Objects.
              this.pageObjectProperties = pageObject.properties; //Storing the Page Object Properties Array in pageObjectProperties Variable.              

              this.object = pageObject.object; //Storing the Object which is Part of Page Object in the object Variable.
              this.objectProperties = this.object.properties; //Storing the Properties of Object in the objectProperties Variable.

              //We Pick All the Properties from pageObjectProperties & the Remaining Properties from objectProperties which are Not a Part of pageObjectProperties.
              namesToFilter = new Set(this.pageObjectProperties.map(d => d.name)); //We Get All the Names in the pageObjectProperties Array and Store them in namesToFilter Array.
              this.properties = [...this.pageObjectProperties, ...this.objectProperties.filter(d => !namesToFilter.has(d.name))]; //We Merge Both the Arrays and Skip the Objects with Names in the namesToFilter Array.

              switch(this.object.path){ //Based on the Path of Object We Load the Respective Component.
                case 'NavbarComponent':{ //NavbarComponent Relates with the Object Type of navbar.
                  const componentRef = viewContainerRef.createComponent<any>(NavbarComponent);
                  componentRef.instance.properties = this.properties;
                  break;
                }
              };
            });
          } else { //Throw Error if No Page Objects Were Received.
            this.error.display('Error Loading Project in webpage.component. Unable to Fetch Page Objects Data...');
          };          
        } else { //Throw Error if Query Ran Succcessfully. However, No Project Page Was Received.
          this.error.display('Error Loading Project in webpage.component. Unable to Fetch Project Page Data...');
        };
      } else { //Throw Error if Query Ran Succcessfully. However, No Projects Were Received.
        this.error.display('Error Loading Project in webpage.component. Unable to Fetch Projects Data...');    
      };
    } catch(error){
      this.error.display('Error Loading Project in webpage.component...', error);
    };
  };
};

webpage.component.html

<ng-template pageObjectLoader></ng-template>
<div>Webpage Works!</div>

objects.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NavbarComponent } from './navbar/navbar.component';
import { ComponentLoaderDirective } from '../../../../services/shared/component-loader.directive';
import { MaterialModule } from '../material/material.module';

let components = [NavbarComponent, ComponentLoaderDirective];

@NgModule({
  declarations: [components],
  imports: [CommonModule, MaterialModule],
  exports: [components]
})
export class ObjectsModule {}

navbar.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Property } from '../../../../../services/projects/webprojects/property';

@Component({
  selector: 'app-navbar',
  templateUrl: './navbar.component.html',
  styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit {
  @Input() properties: Property[] = []; //Properties of Navbar Component
  @Input() elements: any; //Elements of Navbar Component

  class: string = '';    
  
  ngOnInit(): void {
    this.properties.forEach((property) => {
      this.class += (property.type = 'class')?(this.class == '')?property.value:' ' + property.value:'';
    });
  };
}

navbar.component.html

<mat-toolbar [ngClass]="class">
    <span>Yellow Pages & Classifieds | ypandc.com</span>
</mat-toolbar>

Solution

  • Please read ngmodule-vs-jsmodule article that explains the difference between jsModules and ngModules. They are 2 different things:

    JavaScript modules and NgModules can help you modularize your code, but they are very different. Angular applications rely on both kinds of modules.

    1. ngModule has an 'import' property that enable importing other modules whose exported classes are needed by component templates declared in this NgModule.

    2. jsModule syntaxt that should be declared in order to use a js module in another .ts file.

    The export/import ngModule properties are necessary in order to declare which classes can be imported in other modules, and the second 'import' in your component.ts is just a typescript syntaxt for access an object in your file.