Home >Web Front-end >JS Tutorial >A Guide to Building Quality Angular 1.5 Components

A Guide to Building Quality Angular 1.5 Components

尊渡假赌尊渡假赌尊渡假赌
尊渡假赌尊渡假赌尊渡假赌Original
2025-02-17 09:34:10814browse

A Guide to Building Quality Angular 1.5 Components

Key Points

  • Component isolation: minimizes coupling, enhances encapsulation, maintains internal logic private, and controls interaction with other components.
  • Component Focus: Each component focuses on a single responsibility, thereby improving testability and reusability while maintaining simplicity.
  • One-way binding: Reduces digest cycle load and ensures data flows into components without external interference, thereby improving performance and design clarity.
  • Single Binding: Further optimize performance by reducing the number of observers in the digest cycle, which is especially useful for static or invariant data.
  • Life Cycle Events: Use life cycle events such as $onInit and $onDestroy to effectively manage the settings and disassembly of components to ensure that resources are properly initialized and cleaned.
  • Component Events: Components should issue events to communicate with other components, preferring to interact with custom events rather than $scope, which is in line with Angular 2 practices and improves the modularity and reusability of components.

This article was reviewed by Mark Brown and Jurgen Van de Moere. Thanks to all the peer reviewers at SitePoint for getting SitePoint content to its best!

A Guide to Building Quality Angular 1.5 Components


January 10, 2017: The article updated, clarified the section about one-way binding and added information about single-time binding. ---

In Angular 1, components are the mechanisms for creating custom HTML elements. This was possible in the past with Angular directives, but the components built on various improvements to Angular and enforce best practices in building and design.

In this article, we will dive into the design of components and how to use them in your application. If you haven't started using components in Angular 1, you can read our recent tutorial on their syntax and design. My goal is to outline some best practices that will improve the quality of your application.

It should also be noted that many best practices for Angular 2 have been introduced into Angular 1 through the new component API, allowing you to build applications that are easier to refactor later. Angular 2 influences the way we think about and design Angular 1 components, but there are still many obvious differences. Angular 1 is still a very powerful tool for building applications, so I believe that even if you are not going to or are not ready to migrate to Angular 2, it is worth investing in using components to improve your application.

What are good components?

Components should be designed with many key features in mind to make them a powerful building block for applications. We will explore each feature in more detail, but here are the main concepts that components should follow.

  • Isolation: The logic of the component should be encapsulated, kept internal and private. This helps reduce coupling between components.
  • Focus: Components should perform a major task as a single unit, which makes them easy to understand and often easier to reuse.
  • One-way binding: Use one-way binding whenever possible to reduce the load on the digest cycle.
  • Using Lifecycle Events: The lifecycle of a component starts with instantiation and ends with removal from the page. It is best to use these events to maintain components.
  • Unknown API: Components should accept configurations as attributes in a consistent manner so that it is easy to understand how to use them.
  • Issue events: In order to communicate with other components, they should issue events with appropriate names and data.

Let us now first understand why and how to isolate and encapsulate the components from the rest of the application.

Components should be isolated

The evolution of Angular 1 functionality is to enable isolated and encapsulated components, for good reason. Some early applications were highly coupled with the use of $scope and nested controllers. Initially Angular didn't provide a solution, but now it has it.

Good components do not expose their internal logic. This is easy to achieve due to the way they are designed. However, unless absolutely necessary (e.g., send/broadcast events), abuse of component use $scope should be avoided.

Components should be focused on

Components should assume a single role. This is very important for testability, reusability and simplicity. It is better to create additional components instead of overloading individual components. This doesn't mean you won't have bigger or more complex components, it just means that each component should focus on its main work.

I divide the components into four main groups based on their role in the application to help you think about how to design components. There is no different syntax to build these different types of components—just consider the specific roles the components assume.

These types are based on my 5+ years of experience using Angular. You can choose to organize slightly differently, but the fundamental concept is to ensure that your components have a clear role.

Application Components

Only one application component can act as the root of the application. You can think of it as having only one component in the body of a web application, through which all other logic is loaded.

<code>>
  <app>></app>>
>
</code>

This is mainly recommended for Angular 2 design consistency, so it will be easier in the future if you wish to migrate. It also helps with testing by moving all the root content of the application into a single component (rather than putting some of it in the index.html file). The application component also gives you a place to instantiate your application so you don't have to do this in the application run method, which enhances testability and reduces dependency on $rootScope.

This component should be as simple as possible. If possible, it may contain only templates, without any bindings or controllers. However, it does not replace ng-app or boot the application needs.

Routing Component

In the past, we linked controllers and templates in ui-router state (or ngRoute routing). The route can now be linked directly to the component, so the component is still where the controller and template pair, but there are also the advantages of being routable.

For example, using ui-router, which is how we link templates and controllers.

<code>>
  <app>></app>>
>
</code>

You can now link the URL directly to the component.

<code>$stateProvider.state('mystate', {
  url: '/',
  templateUrl: 'views/mystate.html',
  controller: MyStateController
});
</code>

These components can bind data from routing parameters such as project IDs, and their role is to focus on setting up routing to load other components required. This seemingly minor change to routing definition is actually very important for Angular 2 migration capabilities, but it is just as important in Angular 1.5, as it better encapsulates templates and controllers at the component level.

Angular 1 actually has two routing modules, ngRoute and ngComponentRouter. Only ngComponentRouter supports components, but it is also deprecated. I think the best way is to use ui-router.

State Component

Most of the only components you build for your application are stateful. Here you will actually place the application business logic, issue HTTP requests, process forms, and other stateful tasks. These components may be unique to your application, and they focus on maintaining data rather than visual rendering.

Suppose you have a controller loading user profile data for display, and there is also a corresponding template (not shown here) linked together in the directive. This code snippet is probably the most basic controller to do the job.

<code>$stateProvider.state('mystate', {
  url: '/',
  component: 'mystate'
});
</code>

Using components, you can design it better than before. Ideally, you should also use the service instead of using $http directly in the controller.

<code>.controller('ProfileCtrl', function ($scope, $http) {
  $http.get('/api/profile').then(function (data) {
    $scope.profile = data;
  });
})
.directive('profile', function() {
  return {
    templateUrl: 'views/profile.html',
    controller: 'ProfileCtrl'
  }
})
</code>

Now you have a component that loads its own data, so it becomes stateful. These types of components are similar to routing components, except that they may not be used to link to a single route.

The stateful component will use other (stateless) components to actually render the UI. Also, you still want to use the service instead of putting the data access logic directly in the controller.

Stateless Component

Stateless components focus on rendering without managing business logic and do not have to be unique to any particular application. For example, most components used for UI elements such as form controls, cards, etc. also do not handle logic like loading data or saving forms. They are designed to be highly modular, reusable and isolated.

If the stateless component displays only data or everything in the control template, a controller may not be needed. They will accept input from stateful components. This example gets the value from the stateful component (profile example above) and displays the avatar.

<code>.component('profile', {
  templateUrl: 'views/profile.html',
  controller: function($http) {
    var vm = this;
    // 当组件准备好时调用,见下文
    vm.$onInit = function() {
      $http.get('/api/profile').then(function (data) {
        vm.profile = data;
      });
    };
  }
})
</code>

To use it, the stateful component will pass the username through the attribute, as shown below: <avatar username="vm.profile.username">.</avatar>

Many libraries you use are collections of stateless components (and possible services). They can certainly accept configurations to modify their behavior, but they are not intended to be responsible for logic outside of themselves.

Components should use one-way binding

This is not new to the component, but it is usually wise to use it in the component. The purpose of one-way binding is to avoid loading more work into the digest cycle, which is a major factor in application performance. Data now flows into the component without looking outside it (which leads to some coupling problems that exist today), and the component can simply render itself based on that input. This design also works with Angular 2, which helps with future migrations.

In this example, the title attribute is bound to the component only once based on the provided initial value. If the title is changed by an external actor, it will not be reflected in the component. The syntax that indicates that the binding is unidirectional is to use

<code>>
  <app>></app>>
>
</code>

When the title property changes, the component will still be updated. We will explain how to listen for changes to the title property. It is recommended that you use one-way binding whenever possible.

Components should consider single binding

Angular also has the ability to bind data in a single time, so you can optimize the digest cycle. Essentially, Angular will wait to provide the first non-undefined value into the binding, bind that value, and then (once all bindings have been parsed) remove the relevant observer from the digest cycle. This means that a specific binding does not add any processing time to a future digest loop.

This is done by preceding the binding expression with ::. If you know that the input binding will not change during the life cycle, it only makes sense to do so. In this example, if the title is a one-way binding, it will continue to update inside the component, but the binding here will not be updated because we specify it as a single-time binding.

<code>$stateProvider.state('mystate', {
  url: '/',
  templateUrl: 'views/mystate.html',
  controller: MyStateController
});
</code>

Components should use lifecycle events

You may have noticed the $onInit function as a new function. Components have lifecycles and corresponding events that you should use to help manage certain aspects of the component.

$onInit()

The first step in the component life cycle is initialization. This event runs after the controller and binding are initialized. You should almost always use this method for component setup or initialization. It will ensure that all values ​​are available for the component before running. If you access bound values ​​directly in the controller, you cannot guarantee that these values ​​are available.

<code>$stateProvider.state('mystate', {
  url: '/',
  component: 'mystate'
});
</code>

$postLink()

The next step is to link any child elements in the template. When the component is initialized, there is no guarantee that it has also rendered any child elements used in the template. This is important if you need to operate the DOM in any way. An important caveat is that asynchronously loaded templates may not have been loaded when the event is triggered. You can always use a template caching solution to ensure that the template is always available.

<code>>
  <app>></app>>
>
</code>

$onChanges()

When the component is active, it may need to react to changes in input values. One-way binding will still update your component, but we have a new $onChanges event binding to listen for when the input changes.

For this example, assume that the component is provided with a product title and description. You can detect changes as shown below. You can view the object passed to the function, which has objects mapped to available bindings, containing the current value and the previous value.

<code>$stateProvider.state('mystate', {
  url: '/',
  templateUrl: 'views/mystate.html',
  controller: MyStateController
});
</code>

$onDestroy()

The final stage is to remove components from the page. This event runs before the controller and its scope are destroyed. It is important to clean up anything that the component may create or hold memory, such as event listeners, observers, or other DOM elements.

<code>$stateProvider.state('mystate', {
  url: '/',
  component: 'mystate'
});
</code>

Components should have clear API

To configure and initialize components with a set of data, components should use bindings to accept these values. This is sometimes considered a component API, which is just a different way of describing how a component accepts input.

The challenge here is to provide a concise but clear name for the binding. Sometimes developers try to shorten the name to make it very concise, but this is dangerous for the use of components. Suppose we have a component that accepts the stock code as input, which of the following is better?

<code>.controller('ProfileCtrl', function ($scope, $http) {
  $http.get('/api/profile').then(function (data) {
    $scope.profile = data;
  });
})
.directive('profile', function() {
  return {
    templateUrl: 'views/profile.html',
    controller: 'ProfileCtrl'
  }
})
</code>

I hope you think symbol is better. Sometimes developers also like to prefix components and bindings as a way to avoid name conflicts. It is wise to prefix components, for example md-toolbar is the Material toolbar, but prefixing for all bindings can become verbose and should be avoided.

Component should issue an event

In order to communicate with other components, the component should issue a custom event. There are many examples of using services and two-way data binding to synchronize the number between components

It is, but events are a better design choice. Events are much more efficient as a way to communicate with pages (and are the fundamental part of the JavaScript language and how it works in Angular 2, no coincidence).

Events in

can be used with $emit (up to scope tree) or $broadcast (down to scope tree). This is a practical application of a quick sample event.

<code>.component('profile', {
  templateUrl: 'views/profile.html',
  controller: function($http) {
    var vm = this;
    // 当组件准备好时调用,见下文
    vm.$onInit = function() {
      $http.get('/api/profile').then(function (data) {
        vm.profile = data;
      });
    };
  }
})
</code>

There are two main situations in which you need to communicate between components: between components you know and between components you don't know. To illustrate this difference, let's assume we have a set of components to help manage tabs on the page, and a toolbar with corresponding help page links.

<code>.component('avatar', {
  template: '<img  src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/173975605585460.png" class="lazy" ng- alt="A Guide to Building Quality Angular 1.5 Components" >',
  bindings: {
    username: '  },
  controllerAs: 'vm'
})
</code>

In this case, the my-tabs and my-tab components may know each other because they work together to create a set of three different tabs. However, my-toolbar components are beyond their cognitive scope.

Whenever a different tab is selected (this will be an event on the my-tab component instance), the my-tabs component needs to know so that it can adjust the display of the tab to display the instance. The my-tab component can issue events upwards to the parent my-tabs component. This type of communication is like internal communication between two components that work together to create a single function (a tabbed interface).

But what if my-toolbar want to know which tab is currently selected so that it can change the help button based on the visible content? The my-tab event will never reach my-toolbar because it is not a parent. So another option is to use $rootScope to issue events for the entire component tree down, which allows any component to listen and React. The potential downside here is that your events now reach each controller, and if another component uses the same event name, you may trigger unexpected effects.

Determine which approach is suitable for your use case, but whenever another component may need to know about the event, you may want to issue events to the entire component tree using the second option.

Summary

Angular 1 applications can now be written using components, which changes the best practices and nature of our application writing. This is for better, but just using components is not necessarily better than you used before. Keep the following points in mind when building Angular 1 components.

  • isolate your logic. Keep as much component logic as possible internal and away from other aspects of the application to ensure consistency and quality.
  • Keep components simple and focus on a single role. They may be complex components, but various tasks of a single component should be logically connected as units.
  • Use lifecycle events. By connecting to the component lifecycle, you can make sure that the data is ready at the right time and that you can clean it up.
  • Use one-way and single-shot bindings. If possible, one-way binding is more efficient and promotes good design, while single-time binding can speed up the application. You can always use the $onChanges lifecycle event to observe changes.
  • Use events to communicate. Components can communicate using custom events, which is consistent with Angular 2's functionality and is better designed.
  • Have a clear API. Make sure your components are named clearly and easily understandable.

Are you using components in your Angular 1.x application? Or, are you going to wait until you switch to Angular 2? I'd love to hear about your experience in the comments below.

FAQs on Building Angular 1.5 Components

What are the main differences between Angular 1.5 components and directives?

Angular 1.5 component is a simpler and more intuitive API for creating directives. Although instructions are powerful, they can be difficult to use due to their flexibility. On the other hand, components have simpler configurations and are designed to be used to build UI elements. They also facilitate the use of one-way data binding and lifecycle hooks, which can lead to more predictable data flow and easier debugging.

How to use one-way data binding in Angular 1.5 component?

One-way data binding can be achieved using the bindings attribute in Angular 1.5 component.

What is life cycle hook? How to use them in Angular 1.5 components?

Lifecycle hook is a function called at a specific point in the life cycle of a component. Angular 1.5 introduces several life cycle hooks, such as $onInit, $onChanges, $onDestroy and $postLink. These hooks can be used to perform tasks such as initializing data, cleaning up resources, or making Reacts to binding changes.

How to communicate between components in Angular 1.5?

Communication between components can be achieved using bindings and events in Angular 1.5. Parent-to-son communication can be done using binding, while child-to-parent communication can be done using events. This facilitates one-way data flow, which can make your application easier to understand.

How to migrate from directives in Angular 1.5 to component?

Migrating from directives in Angular 1.5 to components involves several steps. First, replace the directive to define the object with the component definition. Then, replace the link function with the life cycle hook. Finally, replace the two-way data binding with one-way data binding and events.

What are the benefits of using components in Angular 1.5 instead of directives?

The components in Angular 1.5 offer several benefits over instructions. They have a simpler API that facilitates one-way data binding and one-way data flow, and provide lifecycle hooks. These features can make your code easier to understand, debug, and maintain.

How to use transcription in Angular 1.5 component?

Transcription can be achieved in Angular 1.5 components using the transclude option in component definition. This allows you to insert custom content into the component's template, which is very useful for creating reusable UI elements.

How to create multi-slot transcription in Angular 1.5 component?

Multi-slot transcription can be implemented in Angular 1.5 components using the transclude option with object syntax. This allows you to define multiple transcription slots in the component's template that can be filled with custom content.

How to use $onChanges Lifecycle hook in Angular 1.5 component?

Whenever a one-way binding is updated, the $onChanges lifecycle hook in Angular 1.5 component is called. It receives a change object containing the current and previous values ​​of the binding. This can be used to make Reacts to the binding changes and perform tasks such as updating component state or getting data.

How to use $postLink Lifecycle hook in Angular 1.5 component?

After the element of the component and its child elements are linked, the $postLink lifecycle hook in the Angular 1.5 component will be called. This can be used to perform tasks that require access to the DOM elements of the component, such as setting up an event listener or operating the DOM.

The above is the detailed content of A Guide to Building Quality Angular 1.5 Components. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn