Hone logo
Hone
Problems

Angular Module Imports: Building a Feature-Rich Application

Angular applications are built from components, services, and modules. Effectively managing dependencies through imports is crucial for organizing your codebase, promoting reusability, and ensuring that your application components can access the functionalities they need. This challenge will test your understanding of how to properly import various Angular artifacts into a component and module.

Problem Description

Your task is to create an Angular component and a module that demonstrate proper import practices. You will need to import:

  1. Angular built-in modules: Specifically, CommonModule (for common directives like *ngIf, *ngFor) and FormsModule (for template-driven forms).
  2. Custom Angular services: A simple service that provides some data or functionality.
  3. Another custom Angular component: A component that might be used by the component you're creating.

The goal is to ensure that your main component can correctly utilize the imported modules and services, and that your module correctly declares and exports the necessary components and services.

Key Requirements:

  • Create a new Angular module (e.g., SharedModule).
  • This module should import CommonModule and FormsModule.
  • This module should declare at least one custom component (e.g., ReusableCardComponent).
  • This module should export the custom component so it can be used elsewhere.
  • Create a component (e.g., UserProfileComponent) that will be the primary focus of this challenge.
  • UserProfileComponent should import the SharedModule (or directly import CommonModule and FormsModule if not using a shared module, but the shared module approach is preferred for this challenge).
  • UserProfileComponent should import a custom Angular service (e.g., UserService).
  • UserProfileComponent should be able to inject and use the UserService.
  • UserProfileComponent should demonstrate the use of a directive from CommonModule (e.g., *ngIf).
  • UserProfileComponent should demonstrate the use of a directive from FormsModule (e.g., ngModel).

Expected Behavior:

When UserProfileComponent is rendered, it should:

  • Display data retrieved from the UserService.
  • Conditionally render content based on a property, utilizing *ngIf.
  • Allow a user to input text into an input field, and have that input bound to a property using ngModel.

Edge Cases:

  • Ensure that services are correctly provided (e.g., at the root level or within the module) and injected.
  • Consider scenarios where a service might return null or an empty array.

Examples

Example 1: Basic Component Import and Service Usage

Assume you have a UserService with a getUser method that returns a user object:

user.service.ts:

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

interface User {
  name: string;
  email: string;
}

@Injectable({
  providedIn: 'root' // Provided at the root level
})
export class UserService {
  getUser(): User {
    return { name: 'Alice Smith', email: 'alice.smith@example.com' };
  }
}

And you want to display this user's information in UserProfileComponent:

user-profile.component.ts:

import { Component } from '@angular/core';
import { UserService } from '../services/user.service'; // Path to your service

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.css']
})
export class UserProfileComponent {
  user: any; // Type this appropriately later

  constructor(private userService: UserService) {
    this.user = this.userService.getUser();
  }
}

user-profile.component.html:

<div>
  <h2>User Profile</h2>
  <p>Name: {{ user.name }}</p>
  <p>Email: {{ user.email }}</p>
</div>

Output: The UserProfileComponent should render the user's name and email.

Explanation: The UserProfileComponent successfully imports UserService and injects it via the constructor. It then calls userService.getUser() to fetch user data and displays it in the template.

Example 2: Using CommonModule Directives within a Component Imported via a Module

Assume SharedModule imports CommonModule and exports ReusableCardComponent:

reusable-card.component.ts:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-reusable-card',
  template: `
    <div class="card">
      <h3>{{ title }}</h3>
      <p>{{ content }}</p>
    </div>
  `,
  styles: [`
    .card { border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; }
  `]
})
export class ReusableCardComponent {
  @Input() title: string = '';
  @Input() content: string = '';
}

shared.module.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReusableCardComponent } from './reusable-card.component';

@NgModule({
  declarations: [ReusableCardComponent],
  imports: [CommonModule],
  exports: [ReusableCardComponent, CommonModule] // Export CommonModule if needed by consumers
})
export class SharedModule { }

And UserProfileComponent uses ReusableCardComponent and *ngIf:

user-profile.component.ts:

import { Component } from '@angular/core';
import { UserService } from '../services/user.service';
import { SharedModule } from '../shared/shared.module'; // Path to your shared module

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.css'],
  standalone: true, // Assuming standalone components for simplicity in this example
  imports: [SharedModule] // Import SharedModule
})
export class UserProfileComponent {
  user: any;
  showDetails: boolean = true;

  constructor(private userService: UserService) {
    this.user = this.userService.getUser();
  }

  toggleDetails() {
    this.showDetails = !this.showDetails;
  }
}

user-profile.component.html:

<div>
  <h2>User Profile</h2>

  <button (click)="toggleDetails()">Toggle Details</button>

  <div *ngIf="showDetails">
    <p>Name: {{ user.name }}</p>
    <p>Email: {{ user.email }}</p>
    <app-reusable-card [title]="'User Info'" [content]="'This card displays user details.'"></app-reusable-card>
  </div>
  <p *ngIf="!showDetails">Details are hidden.</p>
</div>

Output: Initially, the user details and the ReusableCardComponent are visible. Clicking "Toggle Details" hides them and displays "Details are hidden."

Explanation: UserProfileComponent imports SharedModule, which in turn imports CommonModule. This allows the use of *ngIf and the app-reusable-card component. ReusableCardComponent's inputs are bound correctly.

Example 3: Using FormsModule and ngModel

Add an input field to UserProfileComponent for updating the user's name:

user-profile.component.ts (addition):

// ... (previous imports and constructor)

export class UserProfileComponent {
  user: any;
  showDetails: boolean = true;
  editingName: string = ''; // Property to bind with ngModel

  constructor(private userService: UserService) {
    this.user = this.userService.getUser();
    this.editingName = this.user.name; // Initialize with current name
  }

  // ... (toggleDetails method)
}

user-profile.component.html (addition):

<div>
  <h2>User Profile</h2>

  <button (click)="toggleDetails()">Toggle Details</button>

  <div *ngIf="showDetails">
    <p>Name: {{ user.name }}</p>
    <p>Email: {{ user.email }}</p>
    <app-reusable-card [title]="'User Info'" [content]="'This card displays user details.'"></app-reusable-card>

    <hr>
    <h3>Edit Name</h3>
    <input type="text" [(ngModel)]="editingName">
    <p>Current edit value: {{ editingName }}</p>
  </div>
  <p *ngIf="!showDetails">Details are hidden.</p>
</div>

Ensure SharedModule (or your module that UserProfileComponent imports) imports FormsModule:

shared.module.ts (modification):

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; // Import FormsModule
import { ReusableCardComponent } from './reusable-card.component';

@NgModule({
  declarations: [ReusableCardComponent],
  imports: [CommonModule, FormsModule], // Add FormsModule here
  exports: [ReusableCardComponent, CommonModule, FormsModule] // Export FormsModule
})
export class SharedModule { }

Output: An input field appears, allowing the user to type. The "Current edit value" paragraph updates in real-time as the user types, demonstrating two-way data binding with ngModel.

Explanation: By importing FormsModule into the module that UserProfileComponent relies on, the [(ngModel)] directive becomes available, enabling two-way data binding between the input element and the editingName property.

Constraints

  • Your Angular project should be set up using the Angular CLI.
  • The solution must be written in TypeScript.
  • Assume you are working within a standard Angular project structure.
  • For simplicity, all custom components and services can be declared in a single feature module or a shared module.

Notes

  • Pay close attention to the imports and exports arrays within your Angular modules.
  • Consider where your services should be provided. For this challenge, providedIn: 'root' for the service is a good starting point.
  • If you're using standalone components, the import mechanism differs slightly (using the imports array directly in the component decorator). You can choose to implement either module-based or standalone component approaches, but clearly demonstrate the imports in each. The examples above lean towards standalone components for simplicity in demonstrating direct imports.
  • Success is defined by the ability to successfully compile and run the Angular application, with all imported modules, components, and services functioning as expected according to the problem description and examples.
Loading editor...
typescript