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!
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/imghw/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!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.

Understanding how JavaScript engine works internally is important to developers because it helps write more efficient code and understand performance bottlenecks and optimization strategies. 1) The engine's workflow includes three stages: parsing, compiling and execution; 2) During the execution process, the engine will perform dynamic optimization, such as inline cache and hidden classes; 3) Best practices include avoiding global variables, optimizing loops, using const and lets, and avoiding excessive use of closures.

Python and JavaScript have their own advantages and disadvantages in terms of community, libraries and resources. 1) The Python community is friendly and suitable for beginners, but the front-end development resources are not as rich as JavaScript. 2) Python is powerful in data science and machine learning libraries, while JavaScript is better in front-end development libraries and frameworks. 3) Both have rich learning resources, but Python is suitable for starting with official documents, while JavaScript is better with MDNWebDocs. The choice should be based on project needs and personal interests.

Both Python and JavaScript's choices in development environments are important. 1) Python's development environment includes PyCharm, JupyterNotebook and Anaconda, which are suitable for data science and rapid prototyping. 2) The development environment of JavaScript includes Node.js, VSCode and Webpack, which are suitable for front-end and back-end development. Choosing the right tools according to project needs can improve development efficiency and project success rate.
