Using Angular in the right way: Template Syntax

栏目: IT技术 · 发布时间: 4年前

内容简介:The purpose of this article is to make our Angular template code readable and enable a high-caliber performance by following the right practices. It's very useful to have good practices in place for an Angular template to overcome the future performance-re

The purpose of this article is to make our Angular template code readable and enable a high-caliber performance by following the right practices. It's very useful to have good practices in place for an Angular template to overcome the future performance-related issues in the enterprise application.

In this article we'll learn the most appropriate approach for binding the data with a few common use cases of template syntax binding, and how efficiently we can solve the current/future problems.

I am assuming that you have a basic understanding of Angular Template Syntax .

Before beginning with the actual use case, let's brush-up Angular interpolation in the template for binding the text.

{{title}}
title:string = "Template Syntax Binding";

Logical Template Syntax

Let's refer to the below code then after discussing in detail:

Percentage {{ totalMarks / 600 }}

In the similar fashion of this practice, we usually do a lot in the multiple templates. This arises a code readability, maintainability, and reusability concern on a bigger template because HTML templates are not meant for writing business logic. It's good to write a logical code in the TS file.

The recommended approach is to create a getter property in the component and use the respective property in the HTML template. Here is the transformed code :

{{percentage}}
get percentage() {
 return this.totalMarks / 600;
}

The above code gives us the opportunity to use the same code in multiple areas: in the template and in the component as well, if necessary.

Calling method from Template

In my early days of Angular, I was calling the component methods from the Angular template in my smart component. As the data is coming from the parent component, I was comfortable to call the method from the HTML, because this is easier than the other approaches (shortcuts are always useful for developers). One of the code snippet I am putting here.

{{getOffer(amount)}}
@Input() amount:number

    getOffer(amount:number){
        if(amount > 3500  && amount < 4999)
           return `You will get 20% off on 5k purchase`;
        else if (amount > 5000)
           return `You will get 30% off on 7k purchase`;
        else
           return `5% off on your existing purchase.`;
	}

There is no problem with the above code, according to the Angular documentation. However, I see a lot of potential issues with this approach. If there is a complex business rule, then the template rendering will take time, and the performance will degrade.

Let's transform the code:

{{offerMessage}}
offerMessage: string;

    @Input() set amount(value: number) {
        let message: string = '';
        if (value > 5000)
            message = `You will get 30% off on 7k purchase`;
        else if (value > 3500 && value < 4999)
            message = `You will get 20% off on 5k purchase`;
        else
            message = `5% off on your existing purchase.`;
        this.offerMessage = message;
    }

As per the above-shown code, we have used the setter method for amount property, whenever the setter method is called at that time it will set the offerMessage based upon the value. So there is no need to call the method from the template. Seems good, but you may think why I have used the setter method if we can achieve the same thing by using the ngOnChanges method. We can, But if the complexity grows then it's difficult to manage a lot of code in the ngOnChanges method with if clauses and somewhere we lose the power of scalable code in TypeScript. It's up to you to decide which one is better for your application as per the complexity. I prefer both approaches based on my component complexity. If I have multiple @Input decorator properties then I would prefer to go with the setter method otherwise happy with the ngOnChanges .

So far so good.

As per the above solution we may have a question that which approach we should choose between getter properties and methods?

By convention, a method represents an action and a property represents some data. Getter properties are useful where there's no computational complexity, proxying a value of another object or hiding private variables, etc. On the other hand, the methods are useful where our business operation is too expensive or async processes, etc.

I would like to share one of the use case which is not sufficient for setter / ngOnChanges approach:

The data is coming from the server which returns Student Name,  Total Marks and we have to show the Student Name, Grade (computational calculation) in the table.

Again, I preferred to call the method from the template, like below:

<table>
<tr *ngFor="let student of students">
<td>{{student.name}}</td>
<td>{{getGrade(student)}}</td>
</tr>
</table>
students:any[] = [{ id: 1, name: 'John', marks: 65 }, ...]

getGrade(marks: number) {
        let grade: string = 'F';
        if (marks >= 85)
            grade = 'S'
        else if (marks > 60 && marks < 85)
            grade = 'A'
        return grade;
    }

To solve the problem, we should apply Single Responsibility Principle .

See the transformed code below:

<td>{{student.grade}}</td>
students:Student[] = [new Student({ id: 1, name: 'John', marks: 65 }), ...]
export class Student {
    constructor(data: Partial<StudentModel>) {
        this.id = data.id;
        this.name = data.name;
        this.grade = this.getGrade(data.marks);
        this.marks = data.marks;
    }

    id: number;
    name: string;
    marks: number;
    grade: string;

    private getGrade(marks: number) {
        let grade: string = 'F';
        if (marks >= 85)
            grade = 'S'
        else if (marks > 60 && marks < 85)
            grade = 'A'
        return grade;
    }
}

Let's discuss a few questions you may have.

Why we have created a class when we can achieve the same thing without class?

True, we can. But we are losing the benefit of Single Responsibility Principle practices, because other than the class we have to write the code for grade calculation whether in the service class / component / Angular Template, apparently the code become clumsy/duplicate if the same entity is used with minor modification in multiple components with different service classes. Creating a class gives us the power to use the same behavior of the entity in multiple components. This fulfills the need of code maintainability and reusability.

Classes are valuable, this only fits when we may need to initialize the properties and methods to help create objects/process business rules.

Why we haven’t used Pure Pipe?

Pure pipes are good, this definitely saves our additional looping which is going to be done by following the class approach. But I believe pipes are not meant to cover this kind of case. I will explain this in the upcoming article "Using Angular in the right way: Pipes".

Why we are not using memoization in the grading method of the component?

In simple words, memoization is used for heavy computational logic which can significantly improve the performance and our case is not suitable for memorization. If we go with this approach, it may increase the memory consumption as well as the code complexity, if there are multiple methods to be used in the template.

So far we are focusing on accessing the Object property instead of the method in the template, but this is not enough for rendering template faster. Why?

There are two improvements we can make in this code: bind data with ngFor and apply OnPush change detection strategy.

Binding Data with *ngFor

After updating the anyone row of student list, the entire list is recomputed. This impacts a performance issue with a larger data. To solve this problem, we can use ```trackBy``` function, which helps Angular to know how to track our element in the student collection, the only modified value will be recomputed and repainted rather than the whole collection. Refer to the modified code below:

<tr *ngFor="let student of students; trackBy:trackByFn">
    <td>{{student.name}}</td>
    <td>{{student.grade}}</td>
  </tr>
trackByFn(index, item) {
        return item.id;
    }

OnPush Change Detection Strategy

By default, Angular performs change detection on all component everytime something changes in the application, this checks if the value of template expressions have changed. If the component complexity grows then it takes more time to check, but through ChangeDetectionStrategy.OnPush we tell Angular to check only if the references have changed rather than checking for the values of every property. With this approach, we significantly improve the performance and when we want to update an object, the respective object to be propagated to the view.

With OnPush , change detection runs for the component when:

  • The Input reference changes.
  • A native DOM event is triggered from the component or one of it's children.
  • Change detection is triggered manually through detectChanges method of the ChangeDetectorRef class.
  • Async pipe observable gets new value.

Here is the code:

@Component({
    selector: 'app-product',
    template: `...`,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProductComponent { ... }

To learn more, please refer to the article byMax Koretskyi onChange Detection.

Conclusion

The discussed approach provides the flexibility to modularly assemble our code based upon the specific need. It's good to use object property in the template to make our template code readable and performative as compared to method approach.

You may have a lot of questions on bindings or reactive forms elegant approach. Wait for the upcoming articles. For Angular Forms, you can refer to theArmen Vardanyan superb article on Angular Forms: Useful Tips and Angular: The unexpected . I liked both articles and I am sure you will learn a lot.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

需求

需求

[美] 亚德里安•斯莱沃斯基(Adrian J. Slywotzky)、[美]卡尔•韦伯 (Karl Weber) / 魏薇、龙志勇 / 浙江人民出版社 / 2013-6 / 64.9

《财富汇•需求:缔造伟大商业传奇的根本力量》内容简介:需求,是缔造伟大商业传奇的根本力量。《财富汇•需求:缔造伟大商业传奇的根本力量》呈现了人们无法拒绝、竞争对手无法复制的需求创造的六大关键,在人们无奈接受的现状和心中真正期待的理想的这道鸿沟之上,架设起了一道桥梁。 创造需求,需要解开一个谜团,这个谜团是人类学、心理学、科技、设计、经济学、基础设施以及其他众多因素综合而成的奇特组合。《财富汇......一起来看看 《需求》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器