Posted in

如何使用信号转换 Angular 应用程序_AI阅读总结 — 包阅AI

包阅导读总结

1. 关键词:Angular、Signals、Application、Transformation、Performance

2. 总结:本文介绍了 Angular 框架及新推出的 Signals 特性,阐述了如何将传统 Angular 应用逐步转换为使用 Signals 以提升性能,包括必要的前提条件、现有应用的理解及具体的转换步骤。

3. 主要内容:

– 介绍 Angular 是构建企业应用的著名框架,其反应性有优势也可能导致性能问题,Signals 可改善性能。

– 列出跟随本教程所需的前提条件,如对相关语言和框架的理解、相关软件安装等。

– 详细说明了如何运行现有的应用程序,包括克隆、安装依赖和启动。

– 讲解了现有应用程序的代码结构和组件架构。

– 逐步阐述将应用转换为使用 Angular Signals 的步骤,包括转换变量、处理计算信号、更新组件中的方法和模板等。

思维导图:

文章地址:https://www.freecodecamp.org/news/how-to-transform-an-angular-appl-with-signals/

文章来源:freecodecamp.org

作者:Anujkumarsinh Donvir

发布时间:2024/9/10 11:49

语言:英文

总字数:2246字

预计阅读时间:9分钟

评分:88分

标签:Angular,信号,性能优化,网页开发,TypeScript


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

Angular is a famous framework for building robust and complex enterprise applications. It is widely used by large companies. Therefore, having the skills to build a performant application using Angular is one of the top skills for a developer..

Angular’s rise to fame can be attributed to a special feature called reactivity. Reactivity is the ability of the framework to change the user interface (UI) when underlying data or state of the application changes.

This change can be due to asynchronous events like getting response from an API call, or from a user action such as clicking a button. To achieve this reactivity, Angular deploys a mechanism called change detection. Reactivity is a double-edged sword though, and can often lead to performance issues due to unwanted updates to UI.

Angular recently released a new feature called signals, which allows developers to improve the performance of existing applications built with Angular, as well build new applications which have significant performance gains over traditional Angular applications.

Signals give you control over change detection, and prevent unwanted updates to UI. It can be very tricky to transform existing applications to use signals, and this tutorial aims to guide you on getting started with it. In this tutorial, an existing Angular application, which was built traditionally, will be transformed step by step to use signals.

Table of Contents

Prerequisites

To follow this tutorial you must meet below prerequisites:

  • Have an understanding of JavaScript, TypeScript and the Angular framework.

  • Node.js and NPM installed on your machine. You can verify this using these commands: node -v and npm --version.

  • Git installed on your machine. You can verify this using git --version.

    node, npm, and git version in the terminal

  • A code editor installed on your machine. This tutorial has been developed using Visual Studio Code, but any code editor should work.

How to Run an Existing Application

We’ll transform an existing application using Angular signal. The application is a timesheet management application in which a team lead/shift manager can see the number of hours each employee has worked each week, as well as team total hours.

The team lead can also update the hours of an employee as well. Follow the steps below to clone and run the application locally on your machine:

  • Clone the application using the GitHub URL – https://github.com/anujkumartech/emp-time-tracker-1.git. You can use terminal or clone it directly inside your editor.

  • After cloning the app, open a new terminal if not already opened and go to the application folder using cd emp-time-tracker-1.

  • Once you are inside the application folder, install the project dependencies using npm install.

After dependencies have been installed, open the application using npm start. Once the application has started, navigate to http://localhost:4200/ and you should see the below page rendered.

Existing Application Home Page

Understanding the Existing Application Code

It is very important to understand the code and component architecture which renders the application. Open the application in your code editor by navigating to src -> app. You should see a structure like this.

Code Directory

This application has two main components, the parent component and the child component. The parent component displays the team’s cumulative hours as well a list of employees reporting to the team lead and each employee’s total hours and overtime hours. The parent component has necessary code to render the list as well calculate cumulative team hours from this list. Moreover, the parent component also provides the details of the selected employee to the child component.

The child component receives the selected employee details as its input and allows editing the regular as well overtime hours. Once the team lead is satisfied, a save action is initiated. The save action emits an event from the child component back to the parent component. Parent component acts on this event and does necessary updates to hours of the selected employee. This update triggers recalculation of cumulative hours.

Component Architecture of Existing Application

Please go through the code in the editor as well, and pay special attention to code inside files parent-component.ts and child-component.ts. In the next section you will transform both of these components to use the new feature Angular Signals.

How to Transform the Application to Use Angular Signals

The transformation of the code can begin with parent component code. Change the managerName variable to use signals. This highlights how a new signal variable can be created. A signal can be initiated with the signal keyword and option type definition along with initial value for the signal. Code below showcases how a new signal called managerName which has a type of string can be initiated with the initial value of John Doe.

managerName = signal<string>('John Doe');

You would observe rendering issues once you update managerName in the component file to use the signal. This is because, to read a signal value, it needs to be executed. Update the component HTML code to below to read the value of the signal properly.

<h1 class="text-3xl font-bold mb-6">Hello {{managerName()}}!</h1>

List of employees inside parent component is a simple array, transform it to signal as well.

 employees = signal<Employee[]>([    {      id: 1,      name: 'Jon Snow',      regularHours: 40,      overtimeHours: 5    },    ...restOfEmployees    }  ])

As soon as the employee array is transformed to a signal, the parent component will observe several errors. For the moment, comment out the code inside getTeamRegularHoursTotal, getTeamOvertimeHoursTotal and employeeChange methods as highlighted in the illustration given below.

    getTeamRegularHoursTotal() {    let total = 0;        return total;  }  getTeamOvertimeHoursTotal() {    let total = 0;        return total;  }  employeeChange(updatedEmployee: Employee | null) {                              }

To render the application again, update the parent component template HTML and execute employee signal as per the code given below.

<div class="flex items-center py-4 space-x-4 group border-b cursor-pointer hover:bg-gray-50" *ngFor="let employee of employees()" (click)="selectEmployee(employee)">

It’s time to transform logic to calculate the value of regular hours which are showing as zero currently. This leads to examination of another important type of signal known as “computed”.

Computed signals, as the name implies, rely on other signals for their value. Their value updates as soon as underlying signals change, without the need of any additional code to handle the change.

Create a computed signal teamRegularHoursTotal as shown in the code below, which depends on the employees signal directly. Thus, whenever an employees signal changes, the value of teamRegularHoursTotal gets updated automatically.

Define the computed signal as shown in the code below, and ensure the computed dependency is imported from Angular’s core package. Also, remove or comment out method getTeamRegularHoursTotal completely.

 teamRegularHoursTotal = computed(() => {    let total = 0;    this.employees().forEach(employee => total += employee.regularHours);    return total;  })

Update the HTML template of the parent component to reflect this change and show total hours value.

<p class="text-lg font-medium text-gray-700">Regular Hours: <span class="font-bold">{{teamRegularHoursTotal() }}</span></p>

Similarly, overtime hours can be transformed to a computed signal as well. Refer to the code below to update both the component code and its HTML template. Also, comment out or remove the getTeamOvertimeHoursTotal method completely.

 teamOvertimeHoursTotal = computed(() => {    let total = 0;    this.employees().forEach(employee => total += employee.overtimeHours);    return total;  })
<p class="text-lg font-medium text-gray-700">Overtime Hours: <span class="font-bold">{{teamOvertimeHoursTotal() }}</span></p>

It is time to convert the selectedEmployee variable in the parent component to a signal. Transform it using the code below:

selectedEmployee = signal<Employee | null>(null);

As soon as this change is made, the selectEmployee method in the parent component will have errors. These errors are a great opening to an important topic: changing values of a signal. Angular signals can be updated using the set or update API from signals.

As the name implies, the set method assigns a new value to a signal, and the update method updates the value of a signal. Use the set method to change the selectedEmployee signal value. You’ll be seeing update method in the action soon.

selectEmployee(employee: Employee): void {    this.selectedEmployee.set(employee);}

Along with this change, the parent component’s template needs to be updated. Update the code containing the child component as shown in the code below.

After this change, verify that the application is rendering properly and you are able to select an employee and see the details on the screen. This is important as because we’ll be taking this signal transformation journey to the child component.

<section class="w-full md:w-1/2 bg-white p-6 rounded-lg shadow-lg border border-gray-200 order-1 md:order-2 mb-4 md:mb-0"      *ngIf="selectedEmployee() !== null">    <app-child-component [employee]="selectedEmployee()" (employeeChange)="employeeChange($event)"></app-child-component></section>

The child component receives the value of the selected employee through its input, which is currently defined using the @Input decorator. This can be transformed using a signal conveniently known as input. Change the employee variable to be of type input as shown in the code below. Comment out the code shown in saveChanges and resetForm method as highlighted.

employee  = input<Employee | null>();saveChanges() {            }private resetForm() {        }

The form inside the child component displays selected employee’s regular hours and overtime hours using two model variables: editedRegularHours and editedOvertimeHours.

These variables are no longer necessary and the inputs inside the form of the child components can be updated to use the signal’s value directly.

 <input      type="number"      id="regularHours"      [ngModel]="editedRegularHours"     (ngModelChange)="onRegularHoursChange($event)"      class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"  /> <input       type="number"       id="overtimeHours"       [ngModel]="editedOvertimeHours"      (ngModelChange)="onOvertimeHoursChange($event)"      class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"   />

The logic to capture updated values for regular and overtime hours can be transformed as well. Ideally, updating the code below should work but you will get an error as highlighted below.

Component Error

The reason behind the error is that the input type signal can not be mutated with set and update methods. Angular provides a different kind of signal known as model to have input to components which can be mutated within the component itself. Change the employee to type model as seen below.

employee = model<Employee | null>();

Update the overtime update method as shown in the code below. With this, the child component regains the ability to update regular and overtime hours values of the received employee from the parent component.

  onOvertimeHoursChange(event: number) {    this.employee.set({      ...this.employee(),      overtimeHours: event    })  }

Currently, the child component is communicating back the changes to the parent component using the @Output decorator. Similar to the input and the model, Angular has a signal of type output to enable two way communication between child and parent components. Update employeeChange event to output type as seen below.

employeeChange = output<Employee | null>();

Update the saveChanges method and emit an updated employee object.

 saveChanges() {    this.employeeChange.emit(this.employee()); }

For the final step in the child component update the resetForm and cancelChanges methods to reflect the changes made as shown in the code below.

cancelChanges() {    this.employeeChange.emit(null); }resetForm() {    this.employee.set(null);}

Moving back to the parent component now, it is important to make changes to the template to ensure smooth communication between both components.

The employee input in the child component has changed to a model type input. A model has two ways of data binding. Make updates to the code as seen below to use banana in the box (special syntax [( )], which is a shorthand for two-way data binding) notation for the input.

<section class="w-full md:w-1/2 bg-white p-6 rounded-lg shadow-lg border border-gray-200 order-1 md:order-2 mb-4 md:mb-0"    *ngIf="selectedEmployee() !== null">    <app-child-component [(employee)]="selectedEmployee" (employeeChange)="employeeChange($event)"></app-child-component></section>

It’s time to update the employeeChange method in the parent component so that updated hour values of the selected employee can be reflected back to the screen.

To achieve this, another key method used for mutating values can be leveraged. This is the update method, which takes in a function as argument instead of full value. The function passes the current value of the signal as the first parameter and returns the updated value of the signal.

Refer to the updated code for the employeeChange method as seen below to understand this better.

employeeChange() {    if (this.selectedEmployee()) {      this.employees.update(employees => employees.map(employee => {        if (employee.id === this.selectedEmployee().id) {          employee.regularHours = this.selectedEmployee().regularHours;          employee.overtimeHours = this.selectedEmployee().overtimeHours;        }        return employee;      }))    }    this.selectedEmployee.set(null);  }

With this change the entire transformation of the app to use signals is complete. Ensure all loads properly and all the functionality are working as expected as well.

Conclusion

Congratulations on completing this tutorial. During this journey, you have created regular a signal and updated its value using set and update methods.

You also learned how computed signals are defined and utilized. Additionally you have enabled communication between two components using input, model and output signals.

If you encounter any issues during this transformation journey, you can check the code in the feature/signals branch of the same repo you had cloned earlier. It is recommended that you follow along instead of copying the solution from this feature branch.

Once you feel confident about the signal types discussed above, continue your learnings by exploring RxJS Interop from the Angular team to manage your signal integration with RxJS observables.