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.