Creating Highly Reusable AngularJS Components
Preparing your AngularJS app for Angular
No question if you are going to build a new app today you might start off with Angular built on Typescript (Angular 2+). However, with thousands of AngularJS (Angular 1.X) apps still in production, AngularJS isn’t going anywhere just yet. In fact, AngularJS still sees continued development and support from the team.
**UPDATE**
Angular is currently at v6 and is well suited for large production apps. If you are building a new app today, please look to Angular 6+.
Check out our new guide for Creating Highly Reusable Components With Angular
Developers might also favor the rich developer community and open source libraries around AngularJS opposed to diving into new and limited ones. There are several reason why you may want to create an app in AngularJS. If migrating your ng1 app to ng2+ is on the radar, then consider componentizing your app a prerequisite to your migration.
New features since 1.5.x have been retroactively added, mainly the support of components and one-way data binding. We will look out how to maximize these features to create highly reusable components for your AngularJS applications that align closer to the new Angular framework.
Component Organization
It’s easy to get lost in the component this, component that hoopla. One of the most critical pieces to understand is which component should be responsible for what.
Keep page level components as the driving force behind a view while utilizing reusable or “dumb” components to take care of common task.
This keeps page specific logic to a single component while encapsulating all the routine code and functionality into reusable chunks.
One of the biggest mistakes I’ve seen is passing data to children components that alter the data, assume a very specific use case, or communicate via services all over the place. This eventually makes them hard to understand/test and not reusable at all as they become hard coded to specific use-cases.
Your First “Hello World” Component
The simplest component possible would look something like this. Notice how similar to directives these are.
angular.module('app', [])
.component('myComponent', {
template:'<h1>Hello World</h1>'
}
);
It’s worth noting that directives are still available to developers. There are some rare circumstances where you will still want to use a template directive over a component. Possibly if you were dependent on some compile or link phase…. but directives should be used as attributes to add other functionality to a component … for example, maybe you wanted to add permission based security if a component should be available or not to a user.
When in doubt favor the component model. You want to construct your app as a tree of components. See AngularJS docs for more info on components vs directives. Directives should be thought of components without templates used to enhance components.
Binding Options
Component parameters are passed via the bindings
property. Similar to the old way of assigning directive scope variables.
angular.module('myApp')
.component('myComponent', {
template:'<h1>$ctrl.name</h1>',
bindings:{
name: '<' //one way data binding
},
controller: function(){
//component controller }})* < - One way data bindingUtilize one-way data binding for your bindings. Two way data bindings were great and one of the neatest feature of AngularJS, but it comes at a performance cost with the bound properties being watched within the component scope. This also means anything you change in component scope would be reflected in the parent in a two-way binding.!Important! - Its important to note that objects are passed by reference instead of a new copy. This means if your bound property is an object or array, changes to these objects would indeed reflect changes in the parent scope and still give you a two way data binding result. This is why its important to not mutate the object/array in the component scope and make those changes in the parent.* = - Two way data bindingNo worries if you need it though. Two way data binding is still around. #yolo* & - Output event. Provides a hook to parent componentsBindings marked with '&' signify a parameter that accepts a callback function from the parent component. This allows the parent to listen or catch any changes/messages. This is how our "dumb" component will talk back to the parent component with the change or event.* @ - String input
Component Communication
Suppose we are making a messenger app. The main view is a page level component with a reusable contact list component. We want to use the contact list component in other areas of our application, so it’s important to make this as reusable as possible.
The contact list component has a single purpose. To render the list of available contacts from the server and output the selections made.
The messenger component should accept selections made from the contact list component and append them to its recipients list.
We call our contact list a “dumb” component because it knows nothing of the message we send, or who all the recipients would be. It merely acts as a facilitator to the page level component. It’s the messenger component that is calling our send()
method and procuring a list of selected individuals.
The dumb component calls the parent’s onUpdate
, passing in a key value pair for the parameters. This ensures your ordering of params within the parent component have no impact as you listen for changes. Just be sure your names match up.
The Code
Conclusion
Creating AngularJS components in this approach can help improve performance by stepping over the two-way data binding watch. It also sets you up nicely to have a well conditioned and reusable set of components for your application.
Finally, AngularJS apps written in components are primed to migrate towards Angular, as they follow a similar approach of structuring your application in a component tree.
Whats old is new again.
To be clear, there is no magic button to transform your ng1 code to ng2+. These efforts close the gap between ng1 and ng2+ apps to make moving towards Angular a smoother transition, but migrating is still very much a rewrite process. Utilizing es6 features via typescript or webpack with a babel processor would nudge you even closer to Angular built on Typescript.
Once in this component format, utilities like ngUpgrade
(an experimental module from Angular) are possible. ngUpgrade facilitates running an AngularJS component within an an Angular project, which is extremely attractive to those discouraged of rewriting every line of code. (something for a future discussion).