Angular Template-Driven Forms

Template-Driven Forms in Angular.

template driven forms in angular


What Are Template-Driven Forms in Angular Exactly?

  • Two-Way Data Binding: Use ngModel to bind data between typescript and html.
  • Automatic Form Control: Angular is creating FormControl instances by default unless you specify otherwise.
  • Built-in Validation: we can use HTML5 validation attributes (required, email) together with Angular's custom validators.

Your First Template-Driven Form from Scratch, Step by Step

Lets design a simple login form using template driven forms in angular

Step 1: Set Up the Angular Module

Make sure your AppModule includes FormsModule:

template driven forms angular

Step 2: Create the Template for the Form

template driven forms angular html form

Step 3: Handle Form Submission in Your Component

onSubmit(form: NgForm) {  
  if (form.valid) {  
    console.log('Form Data:', form.value);  
    // Send data to your backend here  
  }  
}  

Explanation:

  • #loginForm="ngForm" provides a reference to the form's NgForm instance.
  • ngModel binds input values with your component.
  • (ngSubmit) is triggered when the user submits the form.

Adding Validation and Error Messages

Validation is vital to ensure that our input is a legal and meaningful data point. Here's how to present user-friendly error messages:

<div *ngIf="email.invalid && email.touched">  
  <span *ngIf="email.errors?.required">Email required.</span>  
  <span *ngIf="email.errors?.email">Incorrect format for email.</span>  
</div>  

<div *ngIf="password.invalid && password.touched">  
  <span *ngIf="password.errors?.required">Password is required.</span>  
  <span *ngIf="password.errors?.minlength">Password should contain at least 6 characters.</span>  
</div>  

Advantages and Disadvantages of Template-Driven Forms

Advantages:

  • Easy Setup: Perfect for simple forms.
  • Less Boilerplate: No need to manually declare FormControls.
  • Familiar Syntax: Uses HTML and data binding, suitable for beginners.

Disadvantages:

  • Limited Control: Difficult to perform things such as complex form management or dynamic validation with this model.
  • Testing Challenges: The logic is in the template which makes unit tests harder.

Tips for Writing Template-Driven Forms

  1. Keep It Simple: Template-driven forms are best for simple use cases like logins, and contact forms.
  2. Use Built-in Validators: Combine custom Angular validators with HTML5 attributes for the strongest checks.
  3. Organize Code Structure: Place complex logic in components where it makes sense.
  4. Use NgModelGroup for Grouping: Group related fields such as billing and shipping addresses together.

Real time scenario: 

Create template driven form for user object which we will get from an api or from mock data:

1. Create user service class which will have api logic of getting user objects 
  i am providing sample syntax but you can use online mock data apis for this

template driven forms angular user service

2. Component logic: call user api from component and get data:
  • Now we have a service with getuser and updateuser methods which will call apis and get data.
  • we need to call getUser method and subscribe to it ,
  • we will capture the response and store in our user object and use same object in html to populate data in form
template driven forms in angular component

3. Template driven form in html
  • Do not forget to add name attribute while using ngModel  for template driven forms.
  • Try to practice all the validations , here i haven given some sample for your reference.
template driven forms angular html page

How to pass data between components using routes in Angular?

Techniques to Share Routes Data in Angular—How Efficient Communication Works Among Components of an Angular Application

In the world of Angular applications, components often need to share data, especially as they move from view to view. While services and state management libraries such as NgRx are often the answer, Angular's routing provides some lightweight techniques for passing information directly along routes.

If you're building a product detail page, search filter, multi-step form, or something else entirely, you need to learn how to transfer or pass data through routes. This guide demonstrates several methods with practical examples and advice on best practices.

How to Pass Data Between Components Using Routes in Angular

1. The Need to Share Data via Routes

  • Stateless Navigation: Prevent simple data transfer from becoming overly dependent (and inducing spaghetti code in your otherwise clean services or components).
  • Bookmarkable URLs: Keeping data in the URL ensures users can return to their entry anytime without any problem.
  • Lightweight: Designed for tiny, transient pieces of information like IDs and filters.

2. Passing Data via Routes

Route Parameters

Use Case: Passing key data, such as an ID (e.g., /products/123).

Implementation

Define the Route:

// app-routing.module.ts
const routes: Routes = [
  { path: 'product/:id', component: ProductDetailComponent }
];

Navigate with the Parameter:

// product-list.component.ts
navigateToProduct(id: number) {
  this.router.navigate(['/product', id]);
}

Retrieve the Parameter:

// product-detail.component.ts
import { ActivatedRoute } from '@angular/router';

export class ProductDetailComponent {
  constructor(private route: ActivatedRoute) {
    this.route.paramMap.subscribe(params => {
      const id = params.get('id');
      // Fetch product details using the ID
    });
  }
}

Query Parameters

Use Case: Passing optional data such as filters or sorting options (e.g., /products?category=books).

Implementation

Navigate with Query Params:

// product-list.component.ts
applyFilter(category: string) {
  this.router.navigate(['/products'], { queryParams: { category: category } });
}

Retrieve the Query Parameter:

// product-list.component.ts
this.route.queryParamMap.subscribe(params => {
  const category = params.get('category');
  // Filter products according to category
});

Route Data Property

Use Case: Passing static or resolved data (e.g., page titles, permissions).

Using Data in Route Configuration

Define Static Data:

// app-routing.module.ts
{
  path: 'dashboard',
  component: DashboardComponent,
  data: { requiresAuth: true, title: 'User Dashboard' }
}

Access the Data:

// dashboard.component.ts
ngOnInit() {
  this.route.data.subscribe(data => {
    console.log(data.title); // Output: "User Dashboard"
  });
}

Dynamic Data with Resolvers

Create a Resolver:

// product.resolver.ts
@Injectable({ providedIn: 'root' })
export class ProductResolver implements Resolve {
  constructor(private productService: ProductService) {}

  resolve(route: ActivatedRouteSnapshot) {
    return this.productService.getProduct(route.params['id']);
  }
}

Configure the Route with Resolver:

// app-routing.module.ts
{
  path: 'product/:id',
  component: ProductDetailComponent,
  resolve: { product: ProductResolver }
}

Retrieve the Resolved Data:

// product-detail.component.ts
ngOnInit() {
  this.route.data.subscribe(data => {
    this.product = data.product;
  });
}

State Object (NavigationExtras)

Use Case: Passing temporary or sensitive data without putting it in the URL.

Implementation

Navigate with State:

// checkout.component.ts
proceedToPayment() {
  this.router.navigate(['/payment'], { state: { cartItems: this.cartItems } });
}

Retrieve the State:

// payment.component.ts
ngOnInit() {
  this.cartItems = history.state.cartItems;
}

Practical Example: User Profile Editor

Scenario

Pass a user ID via route parameters and use a resolver to retrieve user data.

Route Configuration

{
  path: 'profile/:userID',
  component: ProfileComponent,
  resolve: { user: UserResolver }
}

Create Resolver:

// user.resolver.ts
resolve(route: ActivatedRouteSnapshot) {
  return this.userService.getUser(route.params['userId']);
}

Retrieve Data in Component:

// profile.component.ts
ngOnInit() {
  this.route.data.subscribe(data => {
    this.user = data.user;
  });
}

Best Practices

  • Use Route Parameters for Necessary Data: Keep URLs neat and meaningful.
  • Limit State Object Size: Avoid passing large objects (risk of data loss on page reload).
  • Resolvers over Route Data: Ensure data is loaded before the component is initialized.
  • Encode Sensitive Information: Do not expose sensitive information in URLs.
  • *Use trackBy with ngFor: Optimize performance when rendering lists from route data.

Angular provides various ways to transfer data between components through routes: from simple IDs in URLs to complex resolved data. By making the right choices in route parameters, query parameters, resolvers, and state objects, you can create flexible, user-friendly applications.

Pay attention to both security and performance, and choose the method that best fits your use case.

Creating custom directives in angular

 Creating Custom Data Directives

Step 1: Build an Attribute Directive

Example: Auto-Format Text on Input

  1. Create the directive:
ng generate directive autoFormat
  1. Define its behavior:
// auto-format.directive.ts
@Directive({
 selector: '[appAutoFormat]'
})
export class AutoFormatDirective {
 @HostListener('input', ['$event']) onInput(event: Event) {
  const input = event.target as HTMLInputElement;
  input.value = input.value.toUpperCase();
 }
}
  1. Use it in a template:
<input appAutoFormat placeholder="Type in uppercase">

Step 2: Build a Structural Directive

Example: Delay Element Rendering

  1. Create the directive:
ng generate directive delayRender
2.define logic:
// delay-render.directive.ts  
@Directive({  
  selector: '[appDelayRender]'  
})  
export class DelayRenderDirective {  
  constructor(  
    private templateRef: TemplateRef<any>,  
    private viewContainer: ViewContainerRef  
  ) {}  

  @Input() set appDelayRender(delay: number) {  
    setTimeout(() => {  
      this.viewContainer.createEmbeddedView(this.templateRef);  
    }, delay);  
  }  
}  
3.use it in template:
<div *appDelayRender="2000">This content renders after 2 seconds.</div>  

Directives in angular

Think of building a web application where every setting, list item, or condition for display requires manually updating the HTML. This would not only be a very time-consuming process, but it would also make for an ungainly codebase. Here Angular Data Directives step in at just the right moment—assigning meaningful content and movement to manipulated elements, regularly invoked whenever information changes throughout our UI. Still in doubt? Whether you're a programming beginner or an experienced professional, understanding directives is key to effective program design. This guide unravels Angular's directive world, provides useful examples, and presents expert knowledge to help you fully utilize its capabilities.

What Are Angular Directives?

Directives are DOM enhancements that extend HTML functionality. They create dynamic effects by binding data into elements. Angular divides directives into three categories:

  1. Components: Directives that include templates (e.g., @Component).
  2. Attribute Directives: Modify element appearance or behavior (e.g., ngClass, ngStyle).
  3. Structural Directives: Change the DOM structure by adding/removing elements (e.g., *ngIf, *ngFor,*ngSwitch).
directives in angular


Built-in Data Directives

1. Attribute Directives

NgClass

Use Case: Dynamically apply CSS classes based on data.

// component.ts
export class AppComponent {
 isActive = true;
}
<!-- component.html -->
<div [ngClass]="{ 'active': isActive, 'error': !isActive }">
 Status: {{ isActive ? 'Active' : 'Inactive' }}
</div>

NgStyle

Use Case: Conditionally apply inline styles.

// component.ts
export class AppComponent {
 progress = 60;
}
<!-- component.html -->
<div [ngStyle]="{
 'width': progress + '%',
 'background-color': progress >= 50 ? 'green' : 'red'
}">
 Progress: {{ progress }}%
</div>

NgModel (Two-Way Binding)

Use Case: Keep input fields in sync with component data.

// app.module.ts
import { FormsModule } from '@angular/forms';
@NgModule({ imports: [FormsModule] })
<!-- component.html -->
<input [(ngModel)]="username" placeholder="Enter username">
<p>Hello, {{ username }}!</p>

2. Structural Directives

*ngIf

Use Case: Conditionally render items.

<div *ngIf="user.isLoggedIn; else loginPrompt">
 Welcome, {{ user.name }}!
</div>

<ng-template #loginPrompt>
 <button (click)="login()">Log In</button>
</ng-template>

*ngFor

Use Case: Iterate over lists and dynamically generate items.

// component.ts
export class AppComponent {
 frameworks = ['Angular', 'React', 'Vue'];
}
<!-- component.html -->
<ul>
 <li *ngFor="let framework of frameworks; index as i">
  {{ i + 1 }}. {{ framework }}
 </li>
</ul>

*ngSwitch

Use Case: Manage multiple conditional cases.

<div [ngSwitch]="userRole">
 <p *ngSwitchCase="'admin'">Admin Dashboard</p>
 <p *ngSwitchCase="'editor'">Editor Tools</p>
 <p *ngSwitchDefault>Guest View</p>
</div>

Best Practices for Angular Directives

  • Use built-in directives like *ngIf and *ngFor whenever possible instead of reinventing the wheel.
  • Avoid direct DOM manipulation. Angular provides Renderer2 for cross-platform compatibility.
  • Optimize performance using trackBy in *ngFor to limit unnecessary re-renders.
  • Keep directive logic concise and reusable.
  • Validate directive behavior using Angular's testing utilities.

With Angular data directives, you can easily transform static pages into real-time interactive systems. By leveraging built-in directives like *ngIf and *ngFor and developing custom ones for specialized tasks, you can build an efficient and responsive application. When creating custom directives, ensure they are simple, focused on a single purpose, and avoid direct DOM manipulation for better maintainability across platforms. Optimize *ngFor for performance and always test your directives using Angular’s built-in utilities.

Are you ready to innovate your Angular project? Start using these directives now!

Data binding angular

Deep Dive into Angular Data Binding

Consider developing a dynamic web application where each time the user interacts, such as button-clicking, form input or data retrieval, you have to manually update elements on the page. Boring, right? This is why Angular Data Binding exists--a great feature that automatically synchronizes your application model's data with its presentation. Data binding is essential for creating responsive applications that are easily maintained and extended, regardless of your level. This guide is designed to introduce the techniques of Angular's data binding, provide step-by-step examples in each case, offer best practices for individual methods and overall just power up your projects.

What's Data Binding in Angular?

Data binding is a way of automatically synchronizing the information that one side (in this case, your application state) sees (the model) with what's seen on the other hand (the view). There are four types of built-in data binding in Angular:

  • Interpolation: Displays component data in the template.
  • Property Binding: Associates all properties for an HTML element with data.
  • Event Binding: Reacts to things happening in the user interface, like button clicks.
  • Two-Way Binding: Automatically keeps UI and model data in sync, in either direction.
data binding in angular


A Detailed Look at Different Types of Data Binding

Interpolation ({{ }})

Example:

// component.ts
export class AppComponent {
  title = 'Data Binding Demo';
}
{{ title }}

Output: in a tag "Data Binding Demo"

Property Binding ([ ])

Use Case: Assign values to properties like src, disabled, and custom directives of an HTML element.

Example:

// component.ts
export class AppComponent {
  imageUrl = 'https://example.com/logo.png';
  isButtonDisabled = true;
}
<img [src]="imageUrl" />
<button [disabled]="isButtonDisabled">Submit</button>

Event Binding (( ))

Use Case: Invoke functions when users perform operations such as clicks and keystrokes.

Example:

// component.ts
export class AppComponent {
  showAlert() {
    alert('Button clicked!');
  }
}
<button (click)="showAlert()">Click Me</button>

Two-Way Binding ([( )])

Use Case: Simultaneously update model and view (e.g., form input).

Prerequisite: Import FormsModule in app.module.ts.

import { FormsModule } from '@angular/forms';
@NgModule({
  imports: [FormsModule]
})

Example:

// component.ts
export class AppComponent {
  username = '';
}
<input [(ngModel)]="username" type="text" />
<p>Hello, {{ username }}!</p>

When you type into the input box, the HTML tag is being updated in real-time.

Building a User Profile Form

Set Up the Component

// profile.component.ts
export class ProfileComponent {
  user = { name: '', email: '', bio: '' };
  onSubmit() {
    console.log(this.user);
  }
}

Create the Template

<form (ngSubmit)="onSubmit()">
  <input [(ngModel)]="user.name" name="name" placeholder="Name" />
  <input [(ngModel)]="user.email" name="email" placeholder="Email" />
  <textarea [(ngModel)]="user.bio" name="bio" placeholder="Bio"></textarea>
  <button type="submit">Save</button>
</form>

Best Practices for Angular Data Binding

  • Avoid Heavy Logic in Templates: To reduce performance use the template to express simple behavior.
  • Use OnPush Change Detection: Maximize performance by minimizing unnecessary checks.
  • Unsubscribe from Observables: In event-driven scenarios, to prevent your application from leaking memory.
  • Prefer One-Way Binding When Possible: Simplifies debugging and reduces unintentional side effects.

Angular's data binding is a game-changer for building interactive applications. By mastering interpolation, property binding, event binding, and two-way binding, you’ll streamline UI updates and write cleaner code. Remember to follow best practices like minimizing template logic and leveraging OnPush change detection for optimal performance. Ready to put data binding into action? Start integrating these techniques into your next Angular project!

Angular life cycle hooks

 It is possible with Angular components to see how their state evolves from birth to destruction. Lifecycle hooks in Angular are procedures that you can use to "hook" into various stages and events at run time. They help you to have a robust finished product which is easy to construct by enabling quick fixes. Whether you are initializing data, responding to changes or tidying up resources, lifecycle hooks make it possible for you to develop productive, manageable applications

When you build a house, we can say that the ground must be prepared prior to any structures being laid atop it. Water and electricity are brought into new homes, the same for plumbing or even ceramics. Paint and furniture then go on finally after all that. As shown, each step of the construction process takes place at a certain time and in specific detail; leaving out one step may very likely lead to confusion.

In Angular, lifecycle hooks work just like that. They give you the opportunity to add code, component by component, and custom logic hooking into key moments in a component's life. From creation—when something is born out of nothing in your browser window—until destruction, when it is no more than memory dust on some computer system miles away, lifecycle hooks play a crucial role.

This can be done at any point during construction: whether you’re fetching data from APIs, managing subscriptions for user input handlers to avoid memory crashes, or cleaning up resources after a component is destroyed.

In this blog post, we’ll delve into Angular’s lifecycle hooks and provide practical examples, as well as best practices. By the end of this article, you can become an expert on these key points in your Angular application that most developers overlook: the lifecycle.

Let’s get started!

What Are Angular Lifecycle Hooks?

Angular components and directives have a well-defined lifecycle managed by Angular itself. Lifecycle hooks are methods that you can redefine in order to execute logic at specific points in a component’s lifecycle.

Some examples include:

  • When a component is initialized.
  • When its input properties change or output events are fired.
  • When it’s destroyed and removed from the DOM.

These hooks are particularly useful for tasks like:

  • Fetching data from an API.
  • Responding to user interactions.
  • Managing subscriptions to prevent memory leaks.
  • Cleaning up resources when the component is destroyed.
  1. ngOnChanges: Responds to input changes.
  2. ngOnInit: Initializes component data.
  3. ngDoCheck: Detects custom changes.
  4. ngAfterContentInit: Runs after content projection.
  5. ngAfterContentChecked: Checks projected content.
  6. ngAfterViewInit: Runs after the view renders.
  7. ngAfterViewChecked: Checks view changes.
  8. ngOnDestroy: Cleans up before component destruction.
angular life cycle hooks


Mastering Angular's Eight Lifecycle Hooks


1.ngOnChanges(changes: SimpleChanges):
  • When it runs:  Before ngOnInit and whenever an input property changes
  • Use case: Reacting to changes in input properties.
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-user',
  template: `<p>User Name: {{ name }}</p>`,
})
export class UserComponent implements OnChanges {
  @Input() name: string = '';

  ngOnChanges(changes: SimpleChanges) {
    if (changes['name']) {
      console.log(`Name changed from ${changes['name'].previousValue} to ${changes['name'].currentValue}`);
    }
  }
}

'
2.ngOnInIt():
  • When It Runs : Once, after the first ngOnChanges.
  • Use Case : Initializing data, fetching data from APIs, or setting up subscriptions.
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-dashboard',
  template: `<h1>Welcome to the Dashboard</h1>`,
})
export class DashboardComponent implements OnInit {
  ngOnInit() {
    console.log('Dashboard initialized');
    // Fetch data or initialize services here
  }
}
3. ngDoCheck
  • When It Runs : During every change detection cycle.
  • Use Case : Custom change detection logic (use sparingly, as it can impact performance).
import { Component, DoCheck } from '@angular/core';

@Component({
  selector: 'app-custom-check',
  template: `<p>Custom Change Detection</p>`,
})
export class CustomCheckComponent implements DoCheck {
  ngDoCheck() {
    console.log('Change detected!');
  }
}
4. ngAfterContentInit
  • When It Runs : After Angular projects external content into the component’s view.
  • Use Case : Working with content projected via <ng-content>.
import { Component, AfterContentInit } from '@angular/core';

@Component({
  selector: 'app-content',
  template: `<ng-content></ng-content>`,
})
export class ContentComponent implements AfterContentInit {
  ngAfterContentInit() {
    console.log('Content has been initialized');
  }
}

5.ngAfterContentChecked

  • When it runs: After Angular checks the content projected into the component.
  • Use case: Performing actions after content changes.

Example:

import { Component, AfterContentChecked } from '@angular/core';  
@Component({  
  selector: 'app-content-checked',  
  template: `<p>Content Checked</p>`,  
})  
export class ContentCheckedComponent implements AfterContentChecked {  
  ngAfterContentChecked() {  
    console.log('Content checked');  
  }  
}  

6.ngAfterViewInit

  • When it runs: After Angular initializes the component's views and child views.
  • Use case: Manipulating the DOM or initializing third-party libraries.

Example:

import { Component, AfterViewInit } from '@angular/core';  
@Component({  
  selector: 'app-view',  
  template: `<p>view initialized</p>`  
})  
export class ViewComponent implements AfterViewInit {  
  ngAfterViewInit() {  
    console.log('View has been initialized');  
  }  
}  

7.ngAfterViewChecked

  • When It Runs: After Angular checks the component’s views and child views.
  • Use Case: Performing actions after view changes.

Example:

import { Component, AfterViewChecked } from '@angular/core';  
@Component({  
  selector: 'app-view-checked',  
  template: '`<p>viewChecked</p>`'  
})  
export class ViewCheckedComponent implements AfterViewChecked {  
  ngAfterViewChecked() {  
    console.log('View checked');  
  }  
}  

8.ngOnDestroy()

  • When It Runs: Just before Angular destroys the component.
  • Use Case: Cleaning up resources like subscriptions or timers.

Example:

import { Component, OnDestroy } from '@angular/core';  
@Component({  
  selector: 'app-timer',  
  template: '`<p>timer running</p>`'  
})  
export class TimerComponent implements OnDestroy {  
  intervalId: any;  
  constructor() {  
    this.intervalId = setInterval(() => console.log('Tick'), 1000);  
  }  
  ngOnDestroy() {  
    clearInterval(this.intervalId);  
    console.log('Timer destroyed');  
  }  
}  

Step-by-Step Example: Using Lifecycle Hooks Together

Let’s combine multiple lifecycle hooks in a single example to demonstrate their flow:

import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges, Input } from '@angular/core';  
@Component({  
  selector: 'app-lifecycle-demo',  
  template: `  
    <p>Name: {{ name }}</p>  
    <button (click)="changeName()">Change Name</button>  
  `  
})  
export class LifecycleDemoComponent implements OnInit, OnDestroy, OnChanges {  
  @Input() name: string = 'John';  
  ngOnInit() {  
    console.log('Component initialized');  
  }  
  ngOnChanges(changes: SimpleChanges) {  
    console.log('Name changed:', changes['name']);  
  }  
  ngOnDestroy() {  
    console.log('Component destroyed');  
  }  
  changeName() {  
    this.name = 'Jane';  
  }  
}  

Best Practices for Using Lifecycle Hooks

  • Avoid Heavy Logic in ngOnChanges: Since this hook runs frequently, keep its logic lightweight to avoid performance issues.
  • Clean Up Resources in ngOnDestroy: Always unsubscribe from observables and clear timers to prevent memory leaks.
  • Use ngOnInit for Initialization: Avoid placing initialization logic in the constructor; use ngOnInit instead.
  • Minimize Use of ngDoCheck: This hook runs often and can slow down your app if overused.
  • Leverage ngAfterViewInit for DOM Manipulation: If you need to interact with the DOM, do it here to ensure the view is fully initialized.

With a strong understanding of these hooks and by making full use of them, you can build dynamic, efficient, and maintainable applications.

To sum up:

  • Angular has eight lifecycle hooks, each corresponding to a specific function.
  • Use ngOnInit for initialization, and ngOnDestroy when you want to clean up everything.
  • Pay attention to performance when using hooks such as ngDoCheck and ngOnChanges.
  • Adopt best practices to write clean, efficient, and maintainable code.

Start playing around with lifecycle hooks today, and see how much more robust your Angular applications become thanks to them!

Select Menu