What is a Module in Angular and Why is it Important?

Question 1: What is an Angular Module and why is it important?

Answer: An Angular module (NgModule) is a container for organizing related components, directives, pipes, and services. It’s important because it:

  1. Helps organize code into cohesive blocks
  2. Controls feature and dependency boundaries
  3. Provides compilation context for components
  4. Contributes to efficient bundling

Example of a basic module:

@NgModule({
  declarations: [
    HomeComponent,
    UserListComponent,
    UserDetailComponent
  ],
  imports: [
    CommonModule,
    RouterModule,
    SharedModule
  ],
  exports: [
    HomeComponent
  ],
  providers: [
    UserService
  ]
})
export class FeatureModule { }

Question 2: What are the different types of Angular Modules?

Answer:

// 1. Root Module
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,  // Important: Only in root
    AppRoutingModule,
    CoreModule,
    SharedModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

// 2. Feature Module
@NgModule({
  imports: [
    CommonModule,  // Note: CommonModule, not BrowserModule
    UserRoutingModule,
    SharedModule
  ],
  declarations: [
    UserListComponent,
    UserDetailComponent
  ]
})
export class UserModule { }

// 3. Shared Module
@NgModule({
  imports: [CommonModule],
  declarations: [
    HeaderComponent,
    FooterComponent,
    LoadingSpinner
  ],
  exports: [
    HeaderComponent,
    FooterComponent,
    LoadingSpinner
  ]
})
export class SharedModule { }

// 4. Core Module
@NgModule({
  imports: [CommonModule],
  providers: [
    AuthService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ]
})
export class CoreModule {
  constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
    if (parentModule) {
      throw new Error('CoreModule is already loaded.');
    }
  }
}

Question 3: How do you handle module dependencies and imports?

Answer:

// Feature module with dependencies
@NgModule({
  imports: [
    CommonModule,
    // Feature specific modules
    MatTableModule,
    MatPaginatorModule,
    // Shared functionality
    SharedModule,
    // Routing
    ProductRoutingModule
  ],
  declarations: [
    ProductListComponent,
    ProductDetailComponent
  ],
  providers: [
    ProductService,
    {
      provide: PRODUCT_CONFIG,
      useValue: defaultConfig
    }
  ]
})
export class ProductModule { }

// Lazy loaded module
const routes: Routes = [
  {
    path: 'products',
    loadChildren: () => 
      import('./product/product.module')
        .then(m => m.ProductModule)
  }
];

Question 4: How do you optimize module loading and performance?

Answer:

// Lazy Loading
const routes: Routes = [
  {
    path: 'admin',
    loadChildren: () => 
      import('./admin/admin.module')
        .then(m => m.AdminModule),
    canLoad: [AuthGuard]
  }
];

// Preloading Strategy
@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      preloadingStrategy: PreloadAllModules
    })
  ]
})
class AppModule { }

// Custom Preloading
export class CustomPreloading implements PreloadAllModules {
  preload(route: Route, load: () => Observable<any>): Observable<any> {
    return route.data?.['preload'] ? load() : EMPTY;
  }
}

Question 5: What are common module patterns and best practices?

Answer:

// 1. Feature Module Pattern
@NgModule({
  imports: [
    CommonModule,
    SharedModule,
    StoreModule.forFeature('orders', orderReducer),
    EffectsModule.forFeature([OrderEffects])
  ],
  declarations: [
    OrderListComponent,
    OrderDetailComponent
  ],
  providers: [
    OrderService,
    {
      provide: OrderConfig,
      useFactory: createOrderConfig,
      deps: [CONFIG_TOKEN]
    }
  ]
})
export class OrderModule { }

// 2. Shared Module Pattern
@NgModule({
  imports: [CommonModule],
  declarations: [
    LoadingSpinner,
    ErrorMessage,
    HighlightDirective
  ],
  exports: [
    CommonModule,  // Re-export common modules
    LoadingSpinner,
    ErrorMessage,
    HighlightDirective
  ]
})
export class SharedModule {
  static forRoot(): ModuleWithProviders<SharedModule> {
    return {
      ngModule: SharedModule,
      providers: [SharedService]
    };
  }
}

Common Interview Follow-up Questions:

  1. Q: What’s the difference between BrowserModule and CommonModule? A: BrowserModule is for the root module and provides browser-specific services, while CommonModule provides common directives and pipes. Only import BrowserModule in the root module, use CommonModule in feature modules.

  2. Q: How do you prevent a module from being imported multiple times? A: Use the constructor guard pattern in core modules:

    export class CoreModule {
      constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
        if (parentModule) {
          throw new Error('CoreModule already loaded');
        }
      }
    }
  3. Q: What are forRoot() and forChild() methods? A: They’re module patterns for configuring providers:

    @NgModule({})
    class MyModule {
      static forRoot(config: Config): ModuleWithProviders<MyModule> {
        return {
          ngModule: MyModule,
          providers: [
            { provide: CONFIG_TOKEN, useValue: config }
          ]
        };
      }
    }
  4. Q: How do you share services between modules? A: Use providedIn: ‘root’ for singleton services or declare in CoreModule:

    @Injectable({
      providedIn: 'root'  // Preferred way
    })
    export class GlobalService { }
    
    // Or in CoreModule
    @NgModule({
      providers: [GlobalService]
    })
    export class CoreModule { }

Test Your Knowledge

Take a quick quiz to test your understanding of this topic.

Test Your Angular Knowledge

Ready to put your skills to the test? Take our interactive Angular quiz and get instant feedback on your answers.