Angular2 is built around the concept of “components” which might be likened to something similar to Controllers in a MVC framework like Rails or Grails. How they differ, is that the component is a tag that is put into a page that will be rendered by the browser/user. The business logic that is in the component is then rendered via its single tag. ((Large part of this content was learned from this Udemy.com course: https://www.udemy.com/angular-firebase-application))
Example: <app-home></app-home> embedded into a HTML page, will execute any logic defined in the home-component.ts file.
In an MVC framework like Rails or Grails, the controller is tied to its specific view and similarly, values defined in the Controller can be called into the view that is rendered in a browser. So the concept is similar.
It might be tempting to overload a component with a lot of logic, but this is frowned upon. For example, we could set up an Angular component to query a database like so:
The above example will work and connect such a component to a database, pulling a list of books. There are reasons though that this won’t be the best place to write this code. What if we have multiple components that might pull from the database? Then we would have to write this logic in each component (duplicating work.)
A service allows us to add code that multiple components can access.
There’s also a programming concept of keeping data logic inside services and business logic inside Controllers/Components. If more people work on the same project, keeping to a standard will benefit the team.
Angular2 – Making the Service
Angular2 allows scaffolding like Rails and it’s invoked in a very similar fashion (using the generate keyword.) In your project, you can simply run a command like:
ng generate service [path/name of service]
such as: ng generate service shared/model/books
In my version of Angular2, I get a WARNING that says, “Service is generated but not provided, it must be provided to be used.” This means that we need to edit our app’s module typescript file (app.module.ts) and in the @NgModule annotation, we fill out providers with some a value of our service:
Injecting Services into Components
Back in the component (the component that will make use of our data query service) of our application, we modify the constructor to inject it:
We’ll also need to import the books.service.ts file with:
Calling The Service in ngOnInit
Within a component, we have both a constructor method and a ngOnInit method. Where the constructor is used to initialize the class properties, the ngOnInit method is where the work of initialization is done. (( See http://stackoverflow.com/questions/35763730/difference-between-constructor-and-ngoninit for more details))
Now that the booksService dependency has been injected in the constructor, we simply call it in the ngOnInit method like so:
We’re invoking the bookService method (not yet written) findAllBooks() to return us a list of Observables.
My first question on this was, “ok what’s an observable?”
Observables are a product of reactive programming, and not specific to Angular2. Reactive programming has specific methodologies around the flow of data through an application. Services and Components are part of this concept. (( For more information on Observables, see: http://blog.rangle.io/observables-and-reactive-programming-in-angular-2/))
The main purpose of using Observables is to observe the behaviour of a variable. In an imperative way, a variable is only changed when its state is mutated by assigning a new or updated value. The reactive approach allows variables to develop a history of evolving change, asynchronously being updated to the values of other points of data. Source: http://blog.rangle.io/observables-and-reactive-programming-in-angular-2/
Tyler Borchert’s blog post on this gives some great examples of this in action. Check out his post for those examples. To suffice we can say that Observables set a variable, which takes a continuous stream of data change. A Library application that might have a variable for a book and who has it checked out, would be updated on each event of check in and check out of the book. An application that pulls a variable for your local weather, can have real-time data updates of the current temperature.
Back to the Service
In the books service, we’re missing the method we’re invoking to find all books. To add this, add the method below the constructor on the service. The method will be an Observable and we’ll need to specifiy the Type of Observable we’re getting back. You can probably use type “any.” However, if you’re using the MVC approach, you’ll most likely have a model. The type then would be our model. In either case this is how it would look:
Notice the imports, we need to import the Observable module/library from rxjs. I’m also including an import to “Book” which incidentally is the defined type of this Observable (i.e. Observable<Book>) – Why the type is an array is because we’re pulling a list of results.
Sidetrack – Models
While not the focus of this post, Models should be mentioned as I’m using it as a data type for the Observable in the above example.
In MVC frameworks like Rails and Grails, the M stands for Model. Models describe the data – defining the columns of a table, the values and constraints, etc. For example, a Book model could define values for each book: Title, Author, ISBN code, url to cover photo, etc.
One thing I liked about Grails was the models were always clearly defined. In Rails, the model itself didn’t have the definitions (they were part of the schema, or part of the Active Record. But in Grails, when you opened the model class, you’ll see each element and its constraints.
Here’s an example of a Model:
Back in the earlier step where we generated a service, you might recall we had a warning about it not being usable until we made a provision for it. In that step, I referenced the Model in the provider array.
What we’re saying on the Observable here, is that this Observable will take data of the types specified in the model. You could have an Observable that takes <any> type, however, by declaring a model and defining what data can go into it, we also have agreement on what data we’ll accept back.
Back in our service, we need to inject the database connection modality we use… In my case, I’m using Firebase. ((see firebase.google.com for more details)) So my injection would look like this:
in the findAllLessons method we return our db lookup:
Updating the Component to Subscribe to the Observable
The way Observables work with data is by subscribing to the data as it flows through the application. As new events come in to change the data, it is immediately referenced in the application.
Our component makes a call to the “findAllBooks()” method, but that method isn’t subscribing to the Observable. To fix that we make the following changes to the method on our component:
The => might seem confusing. In Ruby that’s called a rocket, but in Typescript it’s called a “Fat Arrow.” Fat arrows are short-hand for a function. As to how this is all working, it involves Lexical Scope… and honestly, it’s a bit over my head. ((For more information on Lexical Scoping, see https://toddmotto.com/es6-arrow-functions-syntaxes-and-lexical-scoping/))
This now is subscribed to our database, using a Service, instead of using the Component to make the database call.