When I first started my career in software engineering, my primary passion was backend development. Over the past year, however, I have broadened my interests and found myself more drawn to full stack engineering. I became especially fascinated by Angular, having been exposed to some of its powerful tools by the fantastic frontend engineers on my team. This post was created to be a ramp-up resource that will allow anyone to dive into an Angular project and start contributing quickly.

What is Angular, and why should we care?

Angular is a robust TypeScript Framework by Google that is a great tool for building Single Page Applications (SPAs). This framework allows bundling of multiple projects into a single repository (think customer view, employee view, patient portal, etc.), all while maintaining a core project that stores the entirety of the shared logic. As I explored the various tools that Angular offers, I became especially intrigued by several examples which illustrate its power and versatility. For instance, Angular offers a unique two-way data binding path, highly accessible UI libraries such as Material UI, and the ability to smoothly integrate with deployment through Google Cloud Platform.

Angular Components and Basic Architecture:

At its core, Angular is constructed from modules. An easy way to conceptualize modules are as blueprints for groups of classes. A module configures a set of classes that share a similar purpose, specifies their required dependencies, and establishes the particular functionality belonging to the segment of the application. An Angular application begins with its required root module; any additional modules and classes are nested beneath it, forming a tree-like hierarchy. Modules are a complex topic worthy of a dedicated blog post, but the above basic concepts should be enough for the sake of this article. Our primary focus now turns to the view of the application, which is outlined by the diagram below.

Diagram 1

The application view primarily comprises components with their corresponding templates. The view also optionally contains services that can be added into a component through dependency injection.

To supplement this material, I created a simple single-module application called Keto Finder. The purpose of this application is to provide and display a database of meal options that are ketogenic (very low-carb) diet friendly and available at common restaurant chains.

Below is one of the components from my application:

As well as the corresponding template:

The above template first generates a list of restaurant names from an HTTP request with an async pipe (more on that later) and the usage of a structural declarative. The element property *ngFor= is a simple loop that is defined in-line at the template level, creating a selectable option per restaurant like so:

Image 1

The line between the option tags determine the text to display on each line of the selectable option menu using double-bracket annotation, which accomplishes code interpolation within a template. When a user selects one of the options, the value assignment in the previous line’s option tag sets the selection change value. This value determines a navigation event based on restaurant choice.

My original component (home component) contains a restaurant service which was passed in through the constructor. This configuration is known as dependency injection. The service can be seen below:

The specification root following the @Injectable decorator indicates which application level this service is provided for. In this case, it can be used within the root module of the application. If you turn your attention back to the original component snippet, you will see that the service is used to generate a list of restaurants and meals when the component is initialized.

Rendering Data and Navigation:

Lifecycle Hooks:

In the component example above, notice the method ngOnInit, which calls the service method. This one of several lifecycle hooks, events that are called automatically based on some component-level trigger. The ngOnInit hook provides an event that happens one time near the beginning of a component’s life, immediately after the component is created. Some other common lifecycle hooks are ngOnChanges, which is triggered by some predetermined value(s) changing, and ngOnDestroy, which is called right before a component is destroyed (similar to a destructor).

Structural Declaratives:

Recall the element attribute ngFor from our template, which cycled through the restaurant names creating a selectable option for each restaurant name. This is an example of a structural declarative, a simple way for the template to know when or how many elements to generate dynamically. Another commonly used declarative is the ngIf which can be used to conditionally render data.

Application Navigation with Routing:

In addition to the injected service, our component’s constructor also received a similarly injected router. The router, whose simple implementation can be seen below, was used to pass a navigation command to the component.

This router contains two defined routes, each linked to a corresponding component-template pair. The router is utilized in the last method of the component, navigateTo, which I have replicated again for your convenience:

This is the navigateTo method which was referenced in the template, and is used to navigate to a path based on a restaurant name. Because the router conveniently houses the path-component linkage information, it is used by the method implementation to carry out the navigation event.

The second argument passed into the router’s navigation method highlights a lesser-used feature, allowing us to pass a copy of state data while navigating to a new component. In this instance, it both sets the url to the restaurant name and passes the restaurant data to the new component to be rendered there. While this offers an interesting way to accomplish component-to-component communication, the more common mechanisms are discussed in the next section.

Passing Data Between Components and Templates:

As shown prior, the double-bracket {{ componentValue }} is used to bind property values from the component into the template. However, double-bracket annotation does not provide a means to pass data between components. The standard way to pass data between components is through the use of input and output decorators.

Input and Output of Data:

The following shows an example of binding a list of restaurant names to a list of selectable options. I have provided an additional template and component below to illustrate this concept – the restaurant view template:

and the add meal component:

For the sake of clarity, please note that the corresponding restaurant view component and the full implementation of the add meal template are not shown here.

Although much of its template code is elsewhere, the add meal template itself is in fact nested inside of the restaurant view template, linking the restaurant view and add meal components together. This relationship allows data to pass unimpeded between the two components.

Observe the restaurantId member variable with the @Input decorator in the add meal component. Its value is passed from the parent to the child through the restaurant view template by using square bracket syntax [restaurantId]. Conversely, notice the @Output decorator attached to a member variable which provides an event emitter. Its value is passed from the child to the parent using the parentheses syntax (newMealAdd) in the template.

This technique provides robust, convenient, and versatile communication mechanisms while code is kept clear and concise. For example, the latter mechanism described here uses a class method to pass a new meal item up to its parent after a POST request. This allows us to render the view without an additional GET request!

Putting it all together with HTTP Requests:

At this point you are capable of working with received data, so let’s find out how to get that data from an API. There are two popular ways to accomplish this task: one way uses observables and the other uses promises. Because the majority of Angular applications favor observables, they will be our focus for the rest of this section.

Observables:

A simplistic way of understanding an observable is to think of it as an object wrapper, which contains a future value (a value that will become available at some point in the future). Observables are declarative in nature because they are not executed until a consumer subscribes to them. This pattern is similar to the publish and subscribe pattern.

Fetching Data with Async Pipe and Subscription:

Let’s take a look at a couple ways to use observables for a GET request.

Async Pipe:

An async pipe is the most desirable way to work with an observable. To illustrate this, we will revisit some of the code snippets described earlier.

The service methods:

Component variable and method:

Template subscription with async pipe:

In this case the observable in the component contains an array of RestaurantBundleDTOs. It is generated from the onInit lifecycle hook which assigns its value to the member variable of the component. The observable is then subscribed to in the template, using an async pipe off of a structural declarative loop. If you recall, this operation takes in the array and generates a selectable option for each DTO.

The beauty of the async pipe is that it handles the subscribe as well as the unsubscribe automatically. This provides a very simple way to render an observable and is the preferred pattern when it is a viable option.

Subscribe Method:

Sometimes we may want to perform some actions on the observable in the component level prior to passing it to the template. In this case, we should use the subscribe method.

Restaurant view component method:

Meal card template:

In this example we see a GET request that pulls in a single RestaurantDTO using the subscribe method. The observable class offers numerous versatile ways to manipulate data, but to keep this concise, I will give an overview of a few key points.

When we use a subscribe it is important to always unsubscribe from it when it is no longer needed. This can be done by calling .unsubscribe() on the subscription, or in our case using, the take(1) operator. This take(1) is telling the subscription to retrieve the data only once. We are able to dictate this by the pipe off of the observable (which allows us to change the observable’s data by, for example, filtering) and then chaining a subscribe. As shown, the subscribe method accepts a callback, which we use to call the implicit next method by setting a member variable as the result. This data gets passed from the restaurant view template into the meal card template where the meals are structured into some simple card elements.

Making a Post with FormGroup:

The easiest way to make a POST request based on user input is through the use of a form. Happily, using Angular gives you access to a handy tool called FormGroup.

Setting up a FormGroup:

The add meal component:

To start, we inject a FormBuilder into the component we are using. The fields in the above FormBuilder correspond to a predefined request object:

The next step is to set up the template to track these values, as displayed in the add meal template:

Observe both that “mealForm” is bound to the formGroup and that the input element uses “mealName” as a formControlName. This connects the form values to the component-level variables inside the created form builder. For sake of simplicity I omitted the rest of the input fields from this post.

As you can see the form is bound to ngSubmit tied to a method called mealSubmit(). This method is our next topic of discussion, as it allows us to send a POST from the add meal component.

Posting to an API with FormGroup:

Once the form is set up in the component and the template, the rest of the POST is quite simple.

The mealSubmit method we saw on the template performs a simple series of events. It prepares the response object, submits the POST service method, sends an output to the restaurant view to perform client-side data rendering, and lastly resets each FormField.

Let’s see this is action!

Image 2

And after we click submit:

Image 3

The data has been successfully posted to the backend as well as rendered on the client with one API call.

Conclusion:

At this point, you should be able to navigate through an Angular project and be able to contribute to it quickly. While there is much more to learn, these solid fundamentals should help you get up and running in no time!

Should you like to learn more, I have found the following to be helpful resources: