alvinashcraft
shared this story
from Dima's code blog.
The angular team introduced the .component() method in version 1.5, it allows us to write component based apps using angular 1, this approach makes it easier to transfer your code base to angular 2.
In this guide we will build "Goatlove"(Got-love :), which is a dating app, for goats. It will help us to see component based architecture in action, and hopefully, find some goats around the world a true love.
You can find the entire code here.
Components based architecture
The component based architecture is a rising star in the world of web development, with libraries like React, and the new direction Angular 2.0 is headed. It's clearly we will see a lot more of web apps that embrace this practice in their workflow.
Working with components means separating your apps into small chunks, usually just a few lines of code each. In such way we can define the data flow for those components and isolate them from their environment. So later, we can take one component out and place it inside other part of our application.
Angular 1.X embraced the use of directives, but defining a directive for me was really annoying, all the boilerplate, markup, link functions, transclusion, etc... we're quite intimidating. So many developers ended up with something called "Scope Soup" which basically means that your controllers, were separated from the views, and you probably used ng-controller for tying things. Really fast it becomes a mess, you cant really know how your app behaves and how the data flows.
Later, the approach of using controllerAs syntax evolved, and for many it became a best practice to always alias your controllers in the view so you can understand the scope from looking at your code.
When I started to look into React it was so easy to create a new component, that I ended up writing components for almost everything, reusing them in other parts of my code was a piece of cake. I really love writing code this way nowadays.
One of the projects I've started working on required using angular for some of it part, so I searched for a way to write component based apps, without the hassle of writing directives. After a quick search I found that in angular 1.5 a new method released by the name .component(), which is basically a directive but much easier to write and read.
Angular .component()
The new syntax is pretty straight forward, instead of passing a function that returns an object, we simply pass an object.
Let's see how a component is declared in angular 1.5:
than when we want to use it inside our views we can simply type:
Let's examine the component structure:
Here we define the component's name camelCased, when we will want to use the component we will write it using hyphens as in our example above.
template - used as our component html view, you can write here other components as well! (You can also specify a templateUrl instead if you wish)
bindings - angular components come with isolated scope by default, and instead of writing bind-to-controller key as we used to in the directive method we can simply specify bindings. Bindings in additional to all the known angular bindings can now be used with the new < symbol, a one way binding option, this allows writing our components isolated to the outer scope.
controller - here we can specify a controller for our component, angular by default create a controllerAs with the name of $ctrl, we can change this default behavior by specifying controllerAs key.
require - We can require the controller of other components in order to communicate between the components.
transclude - enables transclusion, the uses are the same as with directives.
For additional configurations you can read the angular docs.
Writing simple component based webapp
Our "goatlove" app will contain a list of goats, with some details like: age, name, about info & photo (currently we will use only external pic link, after we will have some VC's interest, we can upgrade our app to use a server:)), we will allow our clients to create a new entry. For simplicity sake, and for the sake of goats with no WiFi connection, our creation page will allow creating multiple goat profiles with no login needed.
When writing component based apps it is important to visualise your different app components upfront. So here is our awesome goatlove layout.
We split our app into small reusable components based on their behavior, we can later decide that we will create more components for buttons, or other reusable parts we might find.
Note that there is a separation between smart and dumb components, it is a great concept I found from Dan Abramov post.
Shortly, our dumb controllers simply acquire their input from the parent controller, they don't know where the data is coming from or where its going when it needs to output something. This approach makes our code reusable. We can port our dumb components to some other place in our code, give them the i/o they needed and they will magically work :).
Smart controllers, are usually don't contain much html and css, their responsibility is to contact services and pass the data they acquire to the dumb components.
Boilerplate
We will start by grabbing NG6 Starter, its a great starting point for writing component based angular apps. It's come with pre configured webpack, sass compiling, es6 transpiler, auto reload, component blueprints and other cool stuff. So simply fork or clone this repo to your computer.
Enter the cloned directory and type npm install this will install all dependencies necessary for our Goatlove project.
The starter pack does not come with node-sass library, so simply type npm i node-sass -D.
Now we will added 2 loaders for our webpack.config.js file. npm install file-loader url-loader -D.
and add to the loaders array in webpack.config.js the next lines:
This will handle our font loading when importing bootstrap(We used require.resolve for finding the modules from node_modules folder, you can use simply the string, but I encountered a few errors with this starter kit).
When finished, we will start our app using:
Go to localhost:3000 and see your awesome app alive!
Styling
For the sake of this tutorial we will use bootstrap to make all of our styling. So let's install bootstrap as our app dependency:
Folder Structure
Inside the starters pack our code will reside inside client folder, in there we are going to create a new folder named services and containers which will hold our "smart components" and services accordingly. Also let's clear all the current html and scss content inside the components folders.
Next open app.scss file and clean its contents.
Our client folder in this point should look like:
Building the app
Let's start by adding bootstrap styles to our project. We simply do so by adding an import statement inside our app.js file.
We will also remove the old dependencies from the file.
And our index.html will look like this:
Note that we haven't included any js files inside the index.html, webpack-dev-server will handle this for us, and when we will build our app for production it will append any script tags necessary.
Navigation component
We will start writing our layout, and the first component we will build is going to be the navigation bar.
To add a component, write the following line inside your terminal:
This will generate a new component named navigation inside the components folder.
Navigate to the components folder and open the navigation.html file.
Add the next bootstrap snippet:
This will act as our navigation bar, ui-sref-active directive will add the active class based on the current app state. Next, clear the .scss file since we won't be using it for now.
Finally, open the app.js file and import the new component, after that, add it as app dependency:
Next, we will open app.html and add the following html:
Home page
Let's begin with our home page, this will host the Goats Listing component.
Open your terminal:
We added the --parent attribute to specify a different folder for the components generator, we will place this component inside the Pages folder(the parent path is relative to the components folder).
Open the home.html file and add the following html:
Now, go to the app.js file import the Home component and replace the $state route template with <home></home> This will generate the Home page component for the specific route.
Goats Listing (Smart Component)
Before, we can proceed building the smart component we need to build our GoatsService.
Create a new file inside our services folder, name it GoatsService.js.
This file will contain a simple function we will export and import inside app.js (when creating larger apps we will want to create separate module for our services, for the purpose of this tutorial we will keep it simple).
Now add the factory to our app.js file:
Next, we will create our smart component, by saying smart I mean that it will interact with our Services, and pass the retrieved information to the other dumb components beneath.
We will place our smart components inside the containers folder, a common practice I borrowed from react.
This component will not contain any html (maybe some wrapper divs and containers) It's main purpose is for sharing information with our goats-list component.
the goatsListing.html will contain only:
We are passing an array from our component's controller.
goatsListing.controller file will contain a fetching request from the Service and placing it on the controller scope, so we can pass it to our dumb components later:
For our container to work, we need to inject it to our home.js page file:
Now, after we have the container loading our data we need to show it. Time to create some dumb components.
Goats List Component
import the new component module to our goatsListing.js file as we did before with previous modules.
for the goatsList.html we will add the following html:
Remember that we passed to goatsList array from our container? Since we all components are scope isolated we need to open the input for the outside world, we do that from the goatsList.component.js file.
Goats List Item
This will represent each goat entry, let's create it:
Let's define a goat property binding to our component, since we passing it from the component above:
goatListItem.component.js
The html file will contain the markup for each entry:
Inject the ItemListItem in the goatsList.js file, so we can use it inside our ng-repeat loop.
Cool, our viewer seems to work, except one problem, our goat photo is too big. we can fix this inside our goatListItem.scss:
Awesome, good looking goat we have here! After finishing displaying our goats entries, we can make the <create></create> page.
Creating Goat Profile
We will start with a creator page for the ui-router:
Our html file is going to be:
Import the page module to the app.js file as we did before, and change the app.creator $state route with the <create></create> template.
Goat Creator Form Component
We are going to create our creation form as smart component. It will recieve use input from a form, and then submit the entry. After the entry will be saved to our service, we'll transfer to the home page.
inject the module to the create page module as before.
The html markup for the form:
And the form controller:
Summary
In this blog post we have built a simple app entirely using the new .component method from angular 1.5.
This approach allows us to write component based apps that allow us reuse our components between different pages easily, since each component is isolated from the outer app. Our dumb components expose their "API" as bindings, so we can change their context without problem.
Writing your apps this way, will help with the future migration of your code base to Angular 2.0, since it is built entirely using components.
You can find the entire app in my github repo here.