Category: Angular

Angular Change Detection

Easy way to understand change detection in angular with example

Angular’s change detection is a mechanism that allows the framework to automatically detect and update the view whenever there is a change in the application data. It’s a core part of Angular’s functionality that enables the framework to maintain a reactive and interactive user interface.

Angular’s change detection system operates by comparing the current and previous states of the application data. When there is a change in the data, the change detection mechanism triggers a re-evaluation of the view, updating it with the new data.

Angular offers two modes of change detection: default and OnPush. The default change detection mode checks for changes in all component properties and their nested properties. In contrast, the OnPush mode is an optimization technique that limits the change detection to the components with an @Input decorator, or that use immutable objects and observables.

The change detection process can be triggered in three ways:

  1. Default change detection: This is the default change detection strategy used by Angular. In this strategy, Angular checks for changes to the component’s properties and inputs on every change detection cycle. This can be resource-intensive and can slow down the application in large and complex applications. Here’s an example:
import { Component } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="increment()">Increment</button>
    <p>{{ count }}</p>
  `
})
export class CounterComponent {
  count = 0;

  increment() {
    this.count++;
  }
}

In the above code, the CounterComponent has a count property that is incremented every time the increment method is called. Since the default change detection strategy is used, Angular will detect and update the view on every change detection cycle.

OnPush change detection: The OnPush change detection strategy is an optimization that can improve the performance of an application by reducing the number of change detection cycles. In this strategy, Angular only checks for changes to the component’s inputs and properties if the reference to the input or property has changed. Here’s an example:

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

@Component({
  selector: 'app-person',
  template: `
    <p>{{ name }}</p>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class PersonComponent {
  @Input() name: string;
}

Manually triggering change detection: This can be achieved by calling the ChangeDetectorRef.detectChanges() method in the component code.

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="increment()">Increment</button>
    <p>{{ count }}</p>
  `,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CounterComponent {
  count = 0;
  
  constructor(private ref: ChangeDetectorRef) {}

    increment() {
      setInterval(() => {
        this.count++;
        // require view to be updated
        this.ref.markForCheck();
      }, 1000);
    }
  }

In this above example, we are using the push strategy that means CheckOnce, rather than the default CheckAlways, then forces a second check after an interval

In Angular, we can manually trigger change detection using the detectChanges() method of the ChangeDetectorRef service. The ChangeDetectorRef service is available in every Angular component and provides several methods for manually triggering change detection:

  • detectChanges(): This method triggers a change detection cycle for the component and its children.
  • markForCheck(): This method marks the component and its children for change detection but doesn’t trigger a change detection cycle immediately. Angular will trigger change detection during the next change detection cycle.
  • detach(): This method detaches the component from the change detection tree, preventing it from being checked for changes. This can be useful when we have a component that doesn’t need to be updated frequently.
  • reattach(): This method reattaches the component to the change detection tree, allowing it to be checked for changes again.

Here’s an example of manually triggering change detection:

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-my-component',
  template: `
    <ul>
      <li *ngFor="let item of items">{{ item }}</li>
    </ul>
    <button (click)="addItem()">Add Item</button>
  `
})
export class MyComponent implements OnInit {
  items: string[];

  constructor(private dataService: DataService, private cdr: ChangeDetectorRef) {}

  ngOnInit() {
    this.dataService.getItems().subscribe((newItems: string[]) => {
      this.items = newItems;
      this.cdr.detectChanges(); // manually trigger change detection
    });
  }

  addItem() {
    this.dataService.addItem('New Item');
    this.cdr.markForCheck(); // mark component for change detection
  }
}

In the above code, the MyComponent retrieves items from a DataService asynchronously and displays them in a list. When the user clicks the “Add Item” button, we add a new item to the list and mark the component for change detection using the markForCheck() method.

In summary, we can manually trigger change detection in Angular using the detectChanges() method of the ChangeDetectorRef service. Additionally, the markForCheck(), detach(), and reattach() methods provide additional flexibility for controlling when change detection is triggered.

It’s worth noting that Angular’s change detection mechanism is highly performant, thanks to its ability to detect only the changes that occur in the application data. However, it’s important to be mindful of the number of components that are being checked for changes, as this can impact the performance of the application. In some cases, optimizing the change detection strategy can lead to significant performance gains in large applications.

Step by step guide to create a theme in liferay with Angular Material UI

Liferay theme is a collection of files that define the look and feel of a Liferay portal instance. It includes files such as CSS, JavaScript, images, and templates that control the presentation and layout of the portal’s pages, as well as its overall styling and behavior.

Themes in Liferay can be customized and created from scratch to meet specific design requirements, or they can be based on pre-built templates that are available from the Liferay community or commercial sources. They can also be applied to individual sites or globally to affect the appearance of the entire portal.

In addition to providing a consistent and visually appealing user interface, Liferay themes can also improve the accessibility and usability of a portal, enhance its branding and identity, and help to create a seamless and engaging user experience for visitors and users.

What is Angular Material UI and how it can help building a better UI (User Interface) ?

Material UI is a popular user interface (UI) library for building web applications using Angular, a JavaScript library for building user interfaces. Material UI provides a set of pre-built UI components that follow Google’s Material Design guidelines, which is a design language for creating intuitive and engaging user experiences across platforms and devices.

The library includes components for common UI elements such as buttons, forms, dialogs, navigation, and more. It also provides support for responsive design, accessibility, and internationalization.

Material UI can help to speed up the development process by providing a consistent and visually appealing set of UI components that can be easily customized to fit specific design requirements. It is also widely used and supported by a large community, with regular updates and improvements being made to the library.

Here’s an example of creating Liferay theme with Angular Material UI

1. Create an Angular application using the Angular CLI:

ng new my-app

2. Add Angular Material and the Liferay theme dependencies to the Angular project:

ng add @angular/material
npm install --save-dev liferay-theme-deps liferay-theme-tasks

3. Use the Liferay Theme Generator to generate a base Liferay theme for Angular, and select Angular Material as the UI framework:

yo liferay-theme:import --includeAngular --includeJSP --includeSass --framework=material

4. Customize the theme as needed by modifying the Angular code and using the Liferay theme configuration files. For example, you can modify the styles in the _custom.scss file:

@import "node_modules/@angular/material/theming";
@import "node_modules/liferay-font-awesome-web/css/font-awesome.min.css";

@include mat-core();

// Define your custom theme
$liferay-theme-primary: mat-palette($mat-deep-purple);
$liferay-theme-accent: mat-palette($mat-amber, 600, 800, A200);
$liferay-theme-warn: mat-palette($mat-red);

$liferay-theme-theme: mat-light-theme($liferay-theme-primary, $liferay-theme-accent, $liferay-theme-warn);

// Include Material Design icons from the icon font
$md-icon-font-path: "~@angular/material/prebuilt-themes/indigo-pink/"; // adjust path to match your setup
@import "~@angular/material/prebuilt-themes/indigo-pink/prebuilt-themes.css";

5. Use Angular Material’s theming capabilities to customize the look and feel of the UI components. For example, you can change the color scheme of a button:

<button mat-raised-button color="primary">My Button</button>

6. Build the theme using the Liferay theme tasks and deploy it to your Liferay instance:

gulp build
gulp deploy

Note that this is just a basic example and there are many other customization options available for both Liferay and Angular Material. Also, make sure to check the official documentation and best practices for each technology to ensure proper usage.

What’s new in liferay 7?

Liferay 7 was a major release that introduces several new features and improvements over previous versions of Liferay.

Here are some of the notable new features in Liferay 7:
  1. Modular architecture: It will allows developers to build and deploy individual components as separate modules with modular architecture. This makes it easier to develop, test, and deploy Liferay components.
  2. React-based UI: Liferay 7 introduces a new UI framework built with React, a popular JavaScript library for building user interfaces. The new UI framework provides a modern, responsive, and intuitive user experience.
  3. Content Management System (CMS): Liferay 7 includes a new CMS that provides an intuitive and easy-to-use interface for managing content, pages, and sites. The CMS allows users to create and manage content without the need for technical skills.
  4. Personalized experiences: Liferay 7 introduces features for creating personalized experiences for users based on their preferences, behaviors, and interests. This includes features for segmenting and targeting audiences, and for personalizing the content and layout of pages.
  5. Improved performance: Liferay 7 includes faster page loading times, improved scalability, and reduced resource utilization.
  6. Improved security: Liferay 7 includes several security improvements, including support for OAuth2 and OpenID Connect, improved password policies, and enhanced access control.
  7. Microservices support: Liferay 7 includes support for building and deploying microservices, which are small, independent, and modular components that can be deployed and managed separately. This allows developers to build and deploy Liferay components as microservices, making it easier to manage and scale their applications.

These are just some of the new features and improvements in Liferay 7. If you are considering upgrading to Liferay 7, it is important to carefully evaluate your specific needs and requirements, and to consult with an expert in Liferay development.

Understanding Angular lifecycle methods with Examples

In Angular, components have a lifecycle managed by Angular itself, from creation to destruction. Angular provides lifecycle methods that you can use to tap into this process and perform custom logic at specific points in the lifecycle.

Here are some examples of how you might use each lifecycle method:

ngOnChanges: This method is called whenever a bound input property changes. You can use it to react to changes in input properties and perform logic when the value of an input property changes.

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

@Component({
  selector: 'app-counter',
  template: '<p>{{counter}}</p>'
})
export class CounterComponent implements OnChanges {
  @Input() counter: number;

  ngOnChanges(changes: SimpleChanges) {
    console.log(changes);
  }
}

In this example, the CounterComponent implements the ngOnChanges method and logs the changes to its input properties.

  1. ngOnInit: This method is called after the first ngOnChanges call and is used to perform initialization logic for the component.
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-hello',
  template: '<p>Hello, {{name}}!</p>'
})
export class HelloComponent implements OnInit {
  name: string;

  ngOnInit() {
    this.name = 'Angular';
  }
}

In this example, the HelloComponent implements the ngOnInit method and initializes its name property.

  1. ngDoCheck: This method is called during every Angular change detection cycle and is used to perform custom change detection.
import { Component, DoCheck } from '@angular/core';

@Component({
  selector: 'app-todo-list',
  template: `
    <ul>
      <li *ngFor="let item of items">{{item}}</li>
    </ul>
  `
})
export class TodoListComponent implements DoCheck {
  items: string[];

  ngDoCheck() {
    console.log(this.items);
  }
}

In this example, the TodoListComponent implements the ngDoCheck method and logs its items array.

  1. ngAfterContentInit: This method is called after the component’s content has been fully initialized.
import { Component, AfterContentInit } from '@angular/core';

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

In this example, the TabsComponent implements the ngAfterContentInit method and logs a message to the console.

  1. ngAfterContentChecked: This method is called after every change detection cycle that updates the component’s content. It can be used to perform logic that depends on the component’s content being fully updated and checked.
import { Component, AfterContentChecked } from '@angular/core';

@Component({
  selector: 'app-tabs',
  template: `
    <ng-content></ng-content>
  `
})
export class TabsComponent implements AfterContentChecked {
  ngAfterContentChecked() {
    console.log('Tabs content checked');
  }
}

In this example, the TabsComponent implements the ngAfterContentChecked method and logs a message to the console.

6. ngAfterViewInit: This method is called after the component’s view (including its child views) has been fully initialized.

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

@Component({
  selector: 'app-tab-panel',
  template: `
    <ng-content></ng-content>
  `
})
export class TabPanelComponent implements AfterViewInit {
  ngAfterViewInit() {
    console.log('Tab panel view initialized');
  }
}

In this example, the TabPanelComponent implements the ngAfterViewInit method and logs a message to the console.

7. ngAfterViewChecked: This method is called after every change detection cycle that updates the component’s view (including its child views).

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

@Component({
  selector: 'app-tab-list',
  template: `
    <ng-content></ng-content>
  `
})
export class TabListComponent implements AfterViewChecked {
  ngAfterViewChecked() {
    console.log('Tab list view checked');
  }
}

In this example, the TabListComponent implements the ngAfterViewChecked method and logs a message to the console.

8. ngOnDestroy: This method is called just before the component is destroyed by Angular. It can be used to perform cleanup logic for the component, such as unsubscribing from observables or detaching event handlers.

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

@Component({
  selector: 'app-timer',
  template: '<p>{{time}}</p>'
})
export class TimerComponent implements OnDestroy {
  time: number;
  intervalId: number;

  ngOnInit() {
    this.intervalId = setInterval(() => {
      this.time = Date.now();
    }, 1000);
  }

  ngOnDestroy() {
    clearInterval(this.intervalId);
  }
}

In this example, the TimerComponent implements the ngOnDestroy method and uses it to stop the interval timer it started in its ngOnInit method.

I hope you found this article useful and informative. Thanks 😊

How to Troubleshoot Common Angular Errors

Learn how to optimize your troubleshooting tools and became aware of the common Angular errors you might encounter while developing applications.

Debugging Tools

In this article, you will intentionally introduce an easy-to-make mistake so that you can become familiar with real-life errors that can happen while developing your applications and gain a solid understanding of the tool that makes make you an effective developer.

Now pretend that you have made an innocent mistake when copying and pasting the URL from the API documentation page on OpenWeatherMap.org and forgot to add http:// in front of it. This is an easy mistake to make:

src/app/weather/weather.service.ts

...

return this.httpClient

  .get<ICurrentWeatherData>(

    `api.openweathermap.org/data/2.5/weather?q=${city},${country}&appid=${environment.appId}`

  ).pipe(map(data => this.transformToICurrentWeather(data)))

...

Your app will compile successfully, but when you inspect the results in the browser, you won’t see any weather data. In fact, it seems like the Current Weather component is not rendering at all, as you can see in the image below:

Current Weather Does Not Render

There are many ways to debug angular errors for application or any JavaScript application here are some of this:

Debugging with Chrome Developer Tools

You can use the Google Chrome browser because of its cross-platform and consistent developer tools with helpful extensions.

Open Chrome Developer Tools (dev tools) on macOS by pressing option + ⌘ + I or on Windows by pressing F12 or CtrlShift + I.

As a best practice, you write code with VS Code and the browser open side by side, while the dev tools are also open in the browser. There are several good reasons for practicing side-by-side development:

  • Fast feedback loops: With live-reloading, you see the end result of your changes very quickly
  • Laptops: A lot of developers do most of their development on a laptop and a second monitor is a luxury
  • Attention to responsive design: As you may have limited space to work with, you can constantly pay attention to mobile-first development, fixing desktop layout issues after the fact
  • Awareness of network activity: To enable you to quickly see any API call errors and also ensure that the amount of data that is being requested remains in line with your expectations
  • Awareness of console errors: To enable you to quickly react and troubleshoot when new errors are introduced

Observe how side-by-side development looks like:

Side-by-side development with live-reloading running

Note that ultimately, you should do what works best for you. With the side-by-side setup, you will frequently find yourself toggling VS Code’s Explorer on and off and resizing the dev tools pane to a larger or smaller size depending on the specific task at hand. To toggle VS Code’s Explorer, click on the Explorer icon circled in the preceding screenshot.

Just as you can do side-by-side development with live-reloading using npm start to solve angular errors. You can get the same kind of fast feedback loops for unit testing using npm test:

Side-by-side development with unit testing

With the side-by-side unit testing setup, you can become very effective in developing unit tests.

Optimizing Chrome Dev Tools

For the side-by-side development with live-reloading to work well, you need to optimize the default dev tools experience:

Optimized Chrome Developer Tools

Looking at the preceding figure, you will note that numerous settings and information radiators are highlighted:

  1. Have the Network tab open by default so that you can see network traffic flowing.
  2. Open the dev tools settings by clicking on the
  3. Click on the right-hand side icon so that dev tools dock on the right-hand side of Chrome. This layout gives more vertical space, so you can see more network traffic and console events at once. As a side benefit, the left-hand side takes the rough size and shape of a mobile device.
  4. Toggle on large request rows and toggle off overview to see more of the URL and parameters for each request and gain more vertical space.
  5. Check the option Disable cache, which will force reload every resource when you refresh a page while the dev tools are open. This prevents bizarre caching errors from ruining your day.
  6. You will mostly be interested in seeing XHR calls to various APIs, so click on XHR to filter results.
  7. Note that you can glance at the number of console errors in the upper-right corner as 12. The ideal number of console errors should be at all times.
  8. Note that the top item in the request row indicates that there’s an error with status code 404 Not Found.
  9. Since you’re debugging an Angular application, the Augury extension has been loaded.

With your optimized dev tools environment, you can now effectively troubleshoot and resolve the application error from earlier.

Troubleshooting network issues

There are three visible issues with the app at this state:

  • The component details aren’t displaying
  • There are numerous console errors
  • The API call is returning a 404 not found error

Begin by inspecting any network errors, since network errors usually cause knock-on effects:

  1. Click on the failing URL in the Network
  2. In the Details pane that opens to the right of the URL, click on the Preview
  3. You should see this:
Cannot GET /api.openweathermap.org/data/2.5/weather

By just observing this error message, you will likely miss the fact that you forgot to add the http:// prefix to the URL. The bug is subtle and certainly not glaringly obvious.

  1. Hover over the URL and observe the full URL, as shown:

Inspecting Network Errors

As you can see, now the bug is glaringly obvious. In this view, you will get to see the full URL, and it becomes clear that the URL defined in weather.service.ts is not fully qualified, so Angular is attempting to load the resource from its parent server, hosted on localhost:5000, instead of going over the web to the right server.

Investigating console errors

Before you fix this issue, it is worthwhile to understand the knock-on effects of the failing API call:

  1. Observe the console errors:

Dev Tools Console Error Context

The first element of note here is the ERROR CONTEXT object, which has a property named DebugContext_. The DebugContext_ contains a detailed snapshot of the current state of your Angular application when the error happened. The information contained within DebugContext_ is light years ahead of the amount of mostly unhelpful error messages AngularJS generates.

Note that properties that have the value (…) are property getters, and you must click on them to load their details. For example, if you click on the ellipsis for componentRenderElement, it will be populated with the app-current-weather element. You can expand the element to inspect the runtime condition of the component.

  1. Now scroll to the top of the console.
  2. Observe the first error:
ERROR TypeError: Cannot read property 'city' of undefined

You have probably encountered the TypeError before. This error is caused by trying to access the property of an object that is not defined. In this case, CurrentWeatherComponent.current is not assigned to an object, because the http call is failing. Since current is not initialized and the template blindly tries to bind to its properties like {{current.city}}, you will get a message saying property ‘city’ of undefined cannot be read. This is the kind of knock-on effect that can create many unpredictable side-effects in your application. You must proactively code to prevent this condition.

Karma, Jasmine, and Unit Testing errors

When running tests with the ng test command, you will encounter some high-level errors that can mask the root cause of the actual underlying errors.

The general approach to resolving errors should be inside out, resolving child component issues first and leaving parent and root components for last.

Network Error

Network errors can be caused by a multitude of underlying issues:

NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'ng:///DynamicTestModule/AppComponent.ngfactory.js'.

Working inside out, you should implement test doubles of services and provide the fakes to the appropriate components. However, in parent components, you may still encounter errors even if you correctly provided fakes.

Generic ErrorEvents

Error events are generic errors that hide the underlying cause:

[object ErrorEvent] thrown

To expose the root cause of a generic error, implement a new test:debug script:

  1. Implement test:debug, as shown, in json:
package.json

...

"scripts": {

  ...

  "test:debug": "ng test --sourcemaps=false",

  ...

}
  1. Execute npm run test:debug.
  2. Now the Karma runner will likely reveal the underlying issue.
  3. If necessary, follow the stack trace to find the child component that may be causing the issue.

Note that if this strategy is not helpful, you may be able to glean more information on what’s going wrong by breakpoint debugging your unit tests.

Debugging with Visual Studio Code

You can also debug your Angular application, Karma, and Protractor tests from directly within Visual Studio Code. First, you need to configure the debugger to work with a Chrome debugging environment, as illustrated:

VS Code Debugging Setup

  1. Click on the Debug
  2. Expand the No Configurations drop-down and click on Add Configuration….
  3. In the Select Environment select box, select Chrome.

This will create a default configuration in the .vscode/launch.json file. You can modify this file to add three separate configurations.

  1. Replace the contents of jsonwith the following configuration:
.vscode/launch.json

{

  "version": "0.2.0",

  "configurations": [

    {

      "name": "npm start",

      "type": "chrome",

      "request": "launch",

      "url": "http://localhost:5000/#",

      "webRoot": "${workspaceRoot}",

      "runtimeArgs": [

        "--remote-debugging-port=9222"

        ],

      "sourceMaps": true

    },

    {

      "name": "npm test",

      "type": "chrome",

      "request": "launch",

      "url": "http://localhost:9876/debug.html",

      "webRoot": "${workspaceRoot}",

      "runtimeArgs": [

        "--remote-debugging-port=9222"

        ],

      "sourceMaps": true

    },

    {

      "name": "npm run e2e",

      "type": "node",

      "request": "launch",

      "program": "${workspaceRoot}/node_modules/protractor/bin/protractor",

      "protocol": "inspector",

      "args": ["${workspaceRoot}/protractor.conf.js"]

    }

  ]

}
  1. Execute the relevant CLI command like npm start, npm test, or npm run e2ebefore you start the debugger.
  2. On the Debugpage, in the Debug drop-down, select npm start and click on the green play icon.
  3. Observe that a Chrome instance has launched.
  4. Set a breakpoint on a .ts
  5. Perform the action in the app to trigger the
  6. If all goes well, Chrome will report that the code has been Paused in Visual Studio Code.

Note that this method of debugging doesn’t reliably work. You may have to manually set a breakpoint in Chrome Dev Tools | Sources tab, finding the same .ts file under the webpack://. folder, which will then correctly trigger the breakpoint in VS Code. However, this renders the entire benefit of using VS Code to debug code useless. For more information, follow the Angular CLI section on VS Code Recipes on GitHub at https://github.com/Microsoft/vscode-recipes.

If you liked this article, you can learn more about Angular 6 in Doguhan Uluca’s Angular 6 for Enterprise-Ready Web Applications to follow a hands-on and minimalist approach demonstrating how to design and architect high-quality apps.