Client-Side

Web Frameworks II


  • More Angular
  • Routing
  • Forms

Version


v0.1 7 December 2021 Draft

Acknowledgments


Thanks to:
  • Hamzeh Roumani, who has shaped EECS-4413 into a leading hands-on CS course at EECS and who generously shared all of his course materials and, more importantly, his teaching philosophy with me;
  • Parke Godfrey, my long-suffering Master’s supervisor and mentor; and
  • Suprakash Datta for giving me this opportunity to teach this course.

Download PDF

More Angular

Component Lifecycle

A component instance has a lifecycle that starts when Angular instantiates the component class and renders the component view along with its child views. The lifecycle continues with change detection, as Angular checks to see when data-bound properties change, and updates both the view and the component instance as needed. The lifecycle ends when Angular destroys the component instance and removes its rendered template from the DOM. Directives have a similar lifecycle, as Angular creates, updates, and destroys instances in the course of execution.

Your application can use lifecycle hook methods to tap into key events in the lifecycle of a component or directive to initialize new instances, initiate change detection when needed, respond to updates during change detection, and clean up before deletion of instances.

Respond to events in the lifecycle of a component or directive by implementing one or more of the lifecycle hook interfaces in the Angular core library. The hooks give you the opportunity to act on a component or directive instance at the appropriate moment, as Angular creates, updates, or destroys that instance.

Each interface defines the prototype for a single hook method, whose name is the interface name prefixed with ng. For example, the OnInit interface has a hook method named ngOnInit(). If you implement this method in your component or directive class, Angular calls it shortly after checking the input properties for that component or directive for the first time.

Lifecycle Event Sequence

After your application instantiates a component or directive by calling its constructor, Angular calls the hook methods you have implemented at the appropriate point in the lifecycle of that instance. Angular executes hook methods in the following sequence. Use them to perform the following kinds of operations.

Hook method Purpose Timing
ngOnChanges() Respond when Angular sets or resets data-bound input properties. The method receives a SimpleChanges object of current and previous property values. Note that this happens very frequently, so any operation you perform here impacts performance significantly. Called before ngOnInit() (if the component has bound inputs) and whenever one or more data-bound input properties change. Note that if your component has no inputs or you use it without providing any inputs, the framework will not call ngOnChanges().
ngOnInit() Initialize the directive or component after Angular first displays the data-bound properties and sets the directive or component’s input properties. Called once, after the first ngOnChanges(). ngOnInit() is still called even when ngOnChanges() is not (which is the case when there are no template-bound inputs).
ngDoCheck() Detect and act upon changes that Angular can’t or won’t detect on its own. Called immediately after ngOnChanges() on every change detection run, and immediately after ngOnInit() on the first run.
ngAfterContentInit() Respond after Angular projects external content into the component’s view, or into the view that a directive is in. Called once after the first ngDoCheck().
ngAfterContentChecked() Respond after Angular checks the content projected into the directive or component. Called after ngAfterContentInit() and every subsequent ngDoCheck().
ngAfterViewInit() Respond after Angular initializes the component’s views and child views, or the view that contains the directive. Called once after the first ngAfterContentChecked().
ngAfterViewChecked() Respond after Angular checks the component’s views and child views, or the view that contains the directive. Called after the ngAfterViewInit() and every subsequent ngAfterContentChecked().
ngOnDestroy() Cleanup just before Angular destroys the directive or component. Unsubscribe Observables and detach event handlers to avoid memory leaks. Called immediately before Angular destroys the directive or component.

Sharing Data

Between child and parent directives and components

A common pattern in Angular is sharing data between a parent component and one or more child components. Implement this pattern with the @Input() and @Output() decorators. Consider the following hierarchy:

The <parent-component> serves as the context for the <child-component>.

@Input() and @Output() give a child component a way to communicate with its parent component. @Input() lets a parent component update data in the child component. Conversely, @Output() lets the child send data to a parent component.

Sending Data to a Child Component

The @Input() decorator in a child component or directive signifies that the property can receive its value from its parent component. To use @Input(), you must configure the parent and child.

The Child Component

The Parent Component

Sending Data to a Parent Component

@Output() marks a property in a child component as a doorway through which data can travel from the child to the parent. The child component uses the @Output() property to raise an event to notify the parent of the change. To raise an event, an @Output() must have the type of EventEmitter, which is a class in @angular/core that you use to emit custom events. The following example shows how to set up an @Output() in a child component that pushes data from an HTML <input> to an array in the parent component. To use @Output(), you must configure the parent and child.

The Child Component

The Child’s Template

The Parent Component

The Parent’s Template

Using @Input() and @Output() together

Use @Input() and @Output() on the same child component as follows:

The target, item, which is an @Input() property in the child component class, receives its value from the parent’s property, currentItem. When you click delete, the child component raises an event, deleteRequest, which is the argument for the parent’s crossOffItem() method. The following diagram shows the different parts of the @Input() and @Output() on the <app-input-output> child component.

Diagram showing the different parts of the `@Input()` and `@Output()` on the `<app-input-output>` child component

The child selector is <app-input-output> with item and deleteRequest being @Input() and @Output() properties in the child component class. The property currentItem and the method crossOffItem() are both in the parent component class. To combine property and event bindings using the banana-in-a-box syntax, [()], see Two-way Binding.

Routing

Routing

The Angular Router NgModule provides a service that lets you define a navigation path among the different application states and view hierarchies in your application. It is modeled on the familiar browser navigation conventions:

  • Enter a URL in the address bar and the browser navigates to a corresponding page.
  • Click links on the page and the browser navigates to a new page.
  • Click the browser’s back and forward buttons and the browser navigates backward and forward through the history of pages you’ve seen.

The router maps URL-like paths to views instead of pages. When a user performs an action, such as clicking a link, that would load a new page in the browser, the router intercepts the browser’s behavior, and shows or hides view hierarchies.

If the router determines that the current application state requires particular functionality, and the module that defines it hasn’t been loaded, the router can lazy-load the module on demand.

The router interprets a link URL according to your application’s view navigation rules and data state. You can navigate to new views when the user clicks a button or selects from a drop box, or in response to some other stimulus from any source. The router logs activity in the browser’s history, so the back and forward buttons work as well.

To define navigation rules, you associate navigation paths with your components. A path uses a URL-like syntax that integrates your program data, in much the same way that template syntax integrates your views with your program data. You can then apply program logic to choose which views to show or to hide, in response to user input and your own access rules.

For a more detailed discussion, see Routing and navigation.

App Routing Module

Routing within the Templates

Add your routes to your application.

Now that you have defined your routes, add them to your application. First, add links to the two components. Assign the anchor tag that you want to add the route to the routerLink attribute. Set the value of the attribute to the component to show when a user clicks on each link. Next, update your component template to include <router-outlet>. This element informs Angular to update the application view with the component for the selected route.

More Routing

Route Order

The order of routes is important because the Router uses a first-match wins strategy when matching routes, so more specific routes should be placed above less specific routes. List routes with a static path first, followed by an empty path route, which matches the default route. The wildcard route comes last because it matches every URL and the Router selects it only if no other routes match first.

Wildcards & Redirection Routes

Nesting Routes

Getting Route Information

Forms

  • Template-driven Forms
  • Reactive Forms

Template-Driven Forms

Template-driven forms use two-way data binding to update the data model in the component as changes are made in the template and vice versa.

Angular supports two design approaches for interactive forms. You can build forms by writing templates using Angular template syntax and directives with the form-specific directives and techniques described in this tutorial, or you can use a reactive (or model-driven) approach to build forms.

Template-driven forms are suitable for small or simple forms, while reactive forms are more scalable and suitable for complex forms. For a comparison of the two approaches, see Introduction to Forms

Template-driven forms rely on directives defined in the FormsModule.

  • The NgModel directive reconciles value changes in the attached form element with changes in the data model, allowing you to respond to user input with input validation and error handling.

  • The NgForm directive creates a top-level FormGroup instance and binds it to a <form> element to track aggregated form value and validation status. As soon as you import FormsModule, this directive becomes active by default on all <form> tags. You don’t need to add a special selector.

  • The NgModelGroup directive creates and binds a FormGroup instance to a DOM element.

Template-Driven Forms - Example

Reactive Forms

Reactive forms provide a model-driven approach to handling form inputs whose values change over time. This guide shows you how to create and update a basic form control, progress to using multiple controls in a group, validate form values, and create dynamic forms where you can add or remove controls at run time.

There are three steps to using form controls.

  • Register the ReactiveFormsModule in your application. This module declares the reactive-form directives that you need to use reactive forms.
  • Generate a new FormControl instance and save it in the component.
  • Register the FormControl in the template.

Reactive Forms - Example

This slide is intentionally left blank.

Return to Course Page.