Using Angular with Rails
Tags: ruby, javascript, angular
Published: 2021-05-18 16:54:55

*This article was originally written by [Julio Sampaio][1] on the [Honeybadger Developer Blog][2].*  

Now, you want to move on to Angular. Or, perhaps you’re just looking for a way to integrate Angular with your Rails projects because you prefer things this way. We don’t blame you.

With such an approach, you can take advantage of both worlds and decide whether you want to use features from Rails or Angular to format stuff, for example.

## What We'll Build

There’s no need to worry. This tutorial was written for this purpose. We’ll dive into the creation of a fully-working CRUD application over a domain of users.

At the end of the article, you will have learned some basic concepts around Angular and how to set up a Rails back-end project that directly integrates with Angular for the front-end, as shown below:

![CRUD of users](https://www.honeybadger.io/images/blog/posts/angular-rails-5/Figure1.png)
_CRUD of users made with Rails and Angular_

The application will handle all four CRUD operations over the domain of users retrieved from an external fake-testing web service. The app will be built on top of an MVC architecture, with each Angular layer explained in detail to help you better understand how things tie together. The style is up to Bootstrap.

## Setup

As you may have guessed, we will need the following software:

- [Ruby](https://www.ruby-lang.org/en/) (I’ve picked the version 2.7.0preview1),
- [Ruby and Rails](https://rubyonrails.org/) (I’m using its version 5.0.7.2),
- [Node.js](http://nodejs.org/) (I’m going with v13.7.0),
- [Yarn](https://yarnpkg.com/) (At least its version 1.22.4)

Make sure to get everything installed properly. Then, we can move on to the project. Select a folder of your preference and run the following command:

```shell
rails new crud-rails-angular
```

Wait for the setup to complete and open the project within your favorite IDE. We’re going to work with [VS Code](https://code.visualstudio.com/) for this article because it’s simple, powerful, and smoothly embraces both Rails and Angular syntaxes.

If you’ve been using Rails 5 for a while, you may have noticed that its `new` command generates a bug within the _Gemfile_ for the SQLite config. It comes without a minimum version, and that will make it run with errors. Let’s fix this problem by updating it to

```ruby
gem 'sqlite3', '~> 1.3.10'
```

Perfect!

## Webpacker Setup

The best way to manage JavaScript-like applications in Rails is through [Webpacker](https://github.com/rails/webpacker). It makes use of [Webpack](https://webpack.js.org/) behind the scenes to provide features, such as pre-processing and bundling JavaScript apps, like Angular, into an existing Rails application.

To install it, just add a new line to your _Gemfile_:

```ruby
gem 'webpacker', '~> 4.3.x'
```

This will assure that you’ll install a very recent version. Next, run the following commands:

```shell
bundle install
bundle exec rake webpacker:install
bundle exec rake webpacker:install:angular
```

The first command will download and update the added Rails dependencies.

The second is the equivalent to `npm install` since it creates the _node_modules_ folder and installs a bunch of required Angular dependencies, such as Babel, Sass, Browserlist, and Webpack. Now, we have both a Node and a Rails app in the same project.

In the latest command, we have the equivalent of `npm install angular`, which will download all the Angular-required dependencies and make it work alongside our Rails project.

At the end of these commands, you can also see the _package.json_ file created. All of our required dependencies are placed there, and you can add whichever you need in the future.

Also, some folders and files were created under the _/app_ folder, such as the new _/javascript_. In this folder, you already have a _/hello_angular_ folder created to support the beginning of your development.

To gain some time, I’ll ask you to mirror your folders and files structure with the following one:

![Files and folders](https://www.honeybadger.io/images/blog/posts/angular-rails-5/Figure2.png)

## Some Angular Adjustments

Webpacker recommends a series of adjustments within your generated Rails project. So, let’s take some time to organize the house.

First, open your _application.js_ file placed under the _/packs_ folder (shown in the figure above) and add the following code:

```javascript
import "core-js/stable";
import "regenerator-runtime/runtime";
```

These imports work as an auxiliary force to stabilize the JavaScript environment within the Rails project.

Now, we need to inform Rails from where it must pick the output to its pages. Once Webpacker finishes packing things up, it’ll generate a bunch of distributable static files that Rails must be aware of.

Go to the _application.html.erb_ file under the _app/views/layout_ folder and change its `&lt;head>` tag content to the following:

```html
<head>
  <title>CrudRailsAngular</title>
  <base href="/" />
  <!-- 1 -->
  <%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" />
  <!-- 2 -->
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application' %>
  <!-- 3 -->
</head>
```

Let’s break this down a bit:

1. Here, we’re adding the `base` tag, which tells Rails where to look when the application starts up.
2. We’ll make use of [Bootstrap](https://getbootstrap.com/) to infer style to the page, so we can focus on the implementation only.
3. Here is where you must place the Webpacker tag that maps to the _/packs_ folder content (the same that will be auto-generated by Webpacker after every compilation).

## Model and Database

Move on to the database setup. To make things faster, we’ll scaffold a new model called `User`. This is the command you must run to achieve it:

```shell
rails g scaffold User name:string age:integer address:text && rake db:migrate
```

It’ll create all of our model’s folders and files, which we will need to make Rails manipulate database information (from SQLite) and store it to our CRUD operations.

Then, you will see that a new file _XXX1_create_users.rb_ was created under the _db/migrate/_ folder. Open it, and you'll see the newly created `CreateUsers` record.

Within the _app/models/_ folder, you will see the then-created `User` model at the _user.rb_ file.

Now, open the _db/seeds.rb_ file and add the following code:

```ruby
User.create(name: 'Luke Wan', age: 23, address: '123 Test St.')
User.create(name: 'Mary Poppins', age: 41, address: '123 ABC St.')
User.create(name: 'John Neilman', age: 76, address: '000 Test St.')
```

This code will initialize our `Users` table with some data at startup. Save it, and run the command:

```shell
rake db:seed
```

This will seed the table through the commands listed above. Next, you can enter the SQLite database and check if that worked by issuing the command:

```shell
sqlite3 db/development.sqlite3
```

Then, select the table data:

```sql
select * from users;
```

and you may see the results.

## The User Component

You will need to install a couple more of the dependencies to help with the conversion of HTML and CSS to our Rails pages; add the Angular router, forms libs, and [ngx-bootstrap](https://www.npmjs.com/package/ngx-bootstrap), which we’ll use to facilitate the creation and manipulation of Bootstrap components. So, issue the following command:

```shell
yarn add @angular/router @angular/forms html-loader css-loader ngx-bootstrap
```

However, before we jump into the component code, there are some important concepts we need to point out, starting with the anatomy of an Angular component.

## What is a Component?

In Angular, a component exists to connect your views with the application logic made in TypeScript.

In other words, a component is like a container for all the logic that your views need to support its functioning. It defines the values that the views will present and controls their flows. It's the equivalent of a ‘controller’ in similar frameworks.

To create a component, all you need to do is define a new class, implement the [`OnInit`](https://angular.io/api/core/OnInit) interface, and annotate the class with the [`@Component`](https://angular.io/api/core/Component) decorator:

```typescript
export class UserIndexComponent implements OnInit {
	constructor() { ... }

	ngOnInit() { ... }
}
```

## @Component and OnInit

The `@Component` decorator is important because it marks this class a recognizable Angular component and provides metadata configurations that help Angular deal with them regarding processing, instantiation, and use during the runtime.

Take the following metadata configs:

```typescript
@Component({
	selector: "users",
	template: templateString,
})

```

Here, `selector` tells Angular that the provided value is the CSS selector it may use to identify the current directive into a template; yes, it is the same template provided in the next metadata property.

The `OnInit` interface, however, is optional, and it's a good way to initialize stuff before the component finishes its lifecycle. It works like a post-construct method.

## Dependency Injection

Angular is a DI (_Dependency Injection_) framework, a characteristic that increases its modularity and productivity.

Dependencies in Angular can vary from your services and repositories to any kind of ordinary object you feel is fit to be injected somewhere else in the code.

To turn a class "injectable", you only need to annotate it with the [`@Injectable`](https://angular.io/api/core/Injectable) decorator:

```typescript
@Injectable({
	providedIn: "root",
})
export class UserService {
	...
}
```

The `providedIn` indicates which injector will provide the injectable you're creating. The `root` value tells Angular that the injector should be the application-level one. There are more that you can check on [here](https://angular.io/api/core/Injectable#providedIn).

To inject the class into a component, for example, you ask Angular to do it in the component's constructor:

```typescript
constructor(
	private userService: UserService,
) {}
```

It’s as simple as that!

## The Finished Component

Below, you can find the final code listing for our User component. Place it into _index.component.ts_, under the _javascript/hello_angular/app/_ folder.

```typescript
import { Component, OnInit, TemplateRef } from "@angular/core";
import { FormGroup, FormBuilder } from "@angular/forms";
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";

import templateString from "./index.component.html";
import { UserService } from "../user.service";
import { User } from "../user.class";

@Component({
  selector: "users",
  template: templateString,
})
export class UserIndexComponent implements OnInit {
  users: User[];
  modalRef: BsModalRef;
  userForm: FormGroup;
  isNew: Boolean;

  constructor(public fb: FormBuilder, private userService: UserService, private modalService: BsModalService) {}

  public newUser(template: TemplateRef<any>) {
    this.reset();
    this.modalRef = this.modalService.show(template);
  }

  public createUser() {
    this.userService.create(this.userForm.value).subscribe(() => {
      console.log("User created!");
      this.reset();

      this.modalRef.hide();
    });
  }

  public editUser(user, template: TemplateRef<any>) {
    this.isNew = false;
    this.userForm = this.fb.group({
      id: [user.id],
      name: [user.name],
      age: [user.age],
      address: [user.address],
    });

    this.modalRef = this.modalService.show(template);
  }

  public updateUser() {
    const { id } = this.userForm.value;
    this.userService.update(id, this.userForm.value).subscribe(() => {
      console.log("User updated!");
      this.reset();

      this.modalRef.hide();
    });
  }

  public deleteUser(id) {
    if (confirm("Are you sure?")) {
      this.userService.delete(id).subscribe(() => {
        console.log("User deleted!");
        this.reset();
      });
    }
  }

  ngOnInit() {
    this.reset();
  }

  public reset() {
    this.isNew = true;
    this.userService.getUsers().subscribe((users) => {
      this.users = users;
    });

    this.userForm = this.fb.group({
      id: [""],
      name: [""],
      age: [""],
      address: [""],
    });
  }
}
```

The `users` array will hold the current table data listed on the screen and retrieved from the `reset` method that, in turn, calls our Rails API via `UserService` (to be created).

The `userForm` is just a reference to help create and update our users since the same form will be used for both operations. The `isNew` also helps with that, identifying which flow we’re in at the moment.

Here, we have a CRUD-equivalent method for each of the operations. Each of them calls the respective `UserService` method to commit the process in the Rails API.

We’ll also need to set up the HTML module to convert our templates to HTML (we'll see more on modules soon). So, open the _html.d.ts_ file within the same folder and add:

```typescript
declare module "*.html" {
  const content: string;
  export default content;
}
```

## The Angular Service and Model

Let’s move on to Angular’s `UserService` creation. Angular is a framework, just like Rails. So, it means that it’s okay to obey their rules even if this means having duplicate (or very similar) models, for example.

## What is a Model?

Angular models are simple objects that hold data attributes that make sense together (i.e., they represent a concise piece of your domain). They are just like any other model in most languages and frameworks.

It helps a lot to have your data concentrated in one place, rather than duplicating it throughout the code like we do with our user's model:

```typescript
export class User {
  constructor(public id: number, public name: string, public age: number, public address: string) {}
}
```

Remember that it's TypeScript, so your model's attributes must always have a type defined.

Create a new file called _user.class.ts_ under the _javascript/hello_angular/app/user/_ folder and place the code above into it.

## What about a Service?

[Services](https://angular.io/guide/architecture-services) are a broad concept, but we can understand them as well-defined and purposed objects. They help the components with more complex logic, serving them with processed and transformed data, usually coming from an external service or a database.

A service doesn't need any specific annotation or interface; you just create a class and make it _injectable_, as we've seen before. Then, you can inject it into your components.

## Observable Services

Another interesting feature of Angular is that it allows you to use [RxJS](http://reactivex.io/rxjs/) with your classes.

For example, the Angular's default HTTP client, the same one that we'll use to fetch information from an external service, returns RxJS `Observables`. This is why, when you call any of our `UserService` methods within the user component, you might `subscribe` to the `Observable` result:

```typescript
this.userService.getUsers().subscribe((users) => {
  this.users = users;
});
```

_Note that if you're not familiar with RxJS, I'd strongly recommend a brief read over its [documentation](http://reactivex.io/rxjs/); it's not that hard! ;)_

Again, in the _javascript/hello_angular/app/user/_ folder, create another file called _user.service.ts_. This is its content:

```typescript
import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { map } from "rxjs/operators";
import { Observable } from "rxjs";

import { User } from "./user.class";

@Injectable({
  providedIn: "root",
})
export class UserService {
  constructor(private http: HttpClient) {}

  httpOptions = {
    headers: new HttpHeaders({
      "Content-Type": "application/json",
    }),
  };

  getUsers(): Observable<User[]> {
    return this.http.get("/users.json").pipe(
      map((users: User[]) =>
        users.map((user) => {
          return new User(user.id, user.name, user.age, user.address);
        })
      )
    );
  }

  create(user): Observable<User> {
    return this.http.post<User>("/users.json", JSON.stringify(user), this.httpOptions);
  }

  update(id, user): Observable<User> {
    return this.http.put<User>("/users/" + id + ".json", JSON.stringify(user), this.httpOptions);
  }

  delete(id) {
    return this.http.delete<User>("/users/" + id + ".json", this.httpOptions);
  }
}
```

Can you spot the similarities between this one and the component we’ve just created? This is because we need correspondent operations to support the ones in the component.

Note that the `HttpClient` must also be injected within the class’ constructor, so we get to use it alongside the class.

Each operation makes an HTTP call to our Rails API, the auto-generated one.

## Views

Angular works with templates for its views. A template is a sort of hierarchical HTML-and-JavaScript mix that tells Angular how to render each component.

However, before going any further with the construction of our view, let's first understand how Angular splits its template system up.

## The Angular Directives

Because the Angular templates are essentially dynamic, some _directives_ are needed to drive Angular through the right way to render stuff.

Directives are simply classes with a `@Directive` decorator, like the components. Yes, `@Component` inherits from `@Directive`, so it's officially a directive too.

However, there are two other types: the _structural_ and _attribute_ directives.

### Structural Directives

These directives represents conditional and loop structures translated from JavaScript into the Angular template. They help make the template as dynamic as possible, like if you were programming within your vanilla JavaScript code. Take the following example:

```html
<tr *ngFor="let user of users">
  <td>{{ user.name }}</td>
</tr>
```

The _`*ngFor`_ directive tells Angular to iterate over the array of `users` and print each user's name to the DOM.

### Attribute Directives

These work directly with the appearance or behavior of the elements. Take the following example:

```html
<form [formGroup]="userForm" (ngSubmit)="isNew ? createUser() : updateUser()" novalidate></form>
```

Here, we're modifying the behavior of the form by conditionally setting its `submit` function and making use of Angular's [`FormGroup`](https://angular.io/api/forms/FormGroup) to data-bind each of the form inputs.

## Data Binding

Creating forms with web frameworks can be a tricky and error-prone task if it doesn't provide data binding.

Angular supports two-way data binding, which means that you can directly connect pieces of your template to the component and vice versa.

The above form is a good example of the `FormGroup` data binding power. It automatically binds each form field to the `userForm` object created within our component.

In the `editUser` method, for example, you can see the opposite version of the binding, in which the `userForm`'s values are set within the component and shall reflect the form on the view.

## Building the Index View

Let’s break down the content for _index.component.html_ into two parts. This is the first one:

```html
<div class="container pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
  <h1 class="display-4">User's Listing</h1>
  <p class="lead">A quick CRUD example of how to integrate Rails with Angular</p>

  <table class="table">
    <tr>
      <th>Id</th>
      <th>Name</th>
      <th>Age</th>
      <th>Address</th>
      <th>Actions</th>
    </tr>

    <tbody>
      <tr *ngFor="let user of users">
        <td>{{ user.id }}</td>
        <td>{{ user.name }}</td>
        <td>{{ user.age }}</td>
        <td>{{ user.address }}</td>
        <td colspan="2">
          <button class="btn btn-secondary" (click)="editUser(user, template)">Edit</button>
          |
          <button class="btn btn-danger" (click)="deleteUser(user.id)">Delete</button>
        </td>
      </tr>
    </tbody>
  </table>

  <button class="btn btn-primary float-right mt-4" (click)="newUser(template)">Insert New</button>
</div>
```

Most of it is composed of plain HTML. We won’t go into detail about Bootstrap classes.

The important part here is the `ngFor` directive on the table’s row. It helps iterate over the `users` array (remember it?) printing each of its attributes to the HTML output through the `{{ … }}` operator.

Whenever you want to add one of the DOM events, like the _onClick_, just wrap the event name with parentheses and add the component function that it will call when clicked.

## Building the Modal View

The second part is related to the modal contents, so add it below the previous one:

```html
<ng-template #template>
  <div class="modal-header">
    <h4 class="modal-title pull-left">{{ isNew ? "New User" : "Update User" }}</h4>
    <button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    <form [formGroup]="userForm" (ngSubmit)="isNew ? createUser() : updateUser()" novalidate>
      <input type="hidden" formControlName="id" class="form-control" />
      <div class="form-group">
        <label>Name</label>
        <input type="text" formControlName="name" class="form-control" />
      </div>
      <div class="form-group">
        <label>Age</label>
        <input type="text" formControlName="age" class="form-control" />
      </div>
      <div class="form-group">
        <label>Address</label>
        <textarea class="form-control" formControlName="address" rows="3"></textarea>
      </div>

      <button type="submit" class="btn btn-primary">Submit</button>
    </form>
  </div>
</ng-template>
```

Note that we’re making use of the `<ng-template>` tag, which allows you to anchor elements between the HTML and Angular. The template ID comes right after the `#` sign.

Within the form, also note that we’re making use of the `isNew` component variable to verify whether the current usage of this form is related to a user’s creation or update.

Finally, we need to inject the whole _hello_angular_ application into the Rails _index.html.erb_ page. So, open this file under the _views/users/_ folder and change its contents to the following:

```html
<hello-angular>We're almost done...</hello-angular> <%= javascript_pack_tag 'hello_angular' %>
```

## Angular Modules

Now, we need to tell Angular where to find stuff out. It happens within its modules’ configurations.

Let’s start by adding content to _app-bootstrap.module.ts_:

```typescript
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";

import { ModalModule } from "ngx-bootstrap/modal";

@NgModule({
  imports: [CommonModule, ModalModule.forRoot()],
  exports: [ModalModule],
})
export class AppBootstrapModule {}
```

This is restricted to the Bootstrap components we’re inheriting from the ngx-bootstrap. The only component we’re making use of for now is the Bootstrap modal.

Then, open the _app-routing.module.ts_ file and change its contents to the following:

```typescript
import { RouterModule, Routes } from "@angular/router";
import { NgModule } from "@angular/core";

import { UserIndexComponent } from "./user/index/index.component";

const appRoutes: Routes = [
  { path: "users", component: UserIndexComponent },
  { path: "", redirectTo: "/users", pathMatch: "full" },
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes, { scrollPositionRestoration: "enabled" })],
  exports: [RouterModule],
})
export class AppRoutingModule {}
```

This will ensure that Angular matches the right User’s component when the _/users_ path is called.

And, finally, register all of them within the main `AppModule` class. Open the _app.module.ts_ file and make sure it looks like this:

```typescript
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { HttpClientModule } from "@angular/common/http";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";

import { AppComponent } from "./app.component";
import { AppRoutingModule } from "./app-routing.module";
import { AppBootstrapModule } from "./app-boostrap.module";
import { UserIndexComponent } from "./user/index/index.component";

@NgModule({
  declarations: [AppComponent, UserIndexComponent],
  imports: [HttpClientModule, AppRoutingModule, BrowserModule, FormsModule, ReactiveFormsModule, AppBootstrapModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
```

Here, everything’s mapped. From our form, the HTTP client and the user component to the Bootstrap module configs, and routing.

## Finishing Configs Up

Before we jump into the tests, we need to finish some stuff up, starting with the _app.component.ts_ file:

```typescript
import { Component } from "@angular/core";

@Component({
  selector: "hello-angular",
  template: "<router-outlet></router-outlet>",
})
export class AppComponent {
  name = "Angular!";
}
```

The main app component needs to know how to route the paths, so the [RouterOutlet](https://angular.io/api/router/RouterOutlet) will do the job.

Then, we need to make sure that Webpacker understands the HTML extension we’re working with so far. For this, open the _webpacker.yml_ file and, under the _/config_ folder, search for the _extensions_ section and add the following item:

```yaml
- .html
```

Webpacker only recognizes the built-in TypeScript loader that comes by default with Angular. We need to process HTML, which is why we previously installed the _html-loader_ dependency. To set it up, open the _environment.js_ file, under the _config/webpack_ folder, and add the following loader config:

```javascript
environment.loaders.append("html", {
  test: /\.html$/,
  use: [
    {
      loader: "html-loader",
      options: {
        minimize: true,
      },
    },
  ],
});
```

Finally, to prevent our Angular service from receiving errors on their HTTP calls, we need to disable the CSRF token checks performed by Rails. For this, open the _application_controller.rb_ file, under the _app/controllers_ folder, and change its contents to the following:

```ruby
class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session
end
```

## Testing

That’s it! It looks a bit tricky since the setup requires a lot of customizations, but the result is worth it.

To test, save everything, and start the server by issuing the `rails s` command.

Then, go to your web browser and type the address [http://localhost:3000/users](http://localhost:3000/users). Go ahead and play around with the CRUD web application.

## Conclusion

It’s been a long road to get this CRUD up and running. After the first trial, you’ll see that things get easier for your future projects. I hope this project helps to set up a starting point for those of you that want to start a project quickly by joining both techs.

While we don’t have an open-source scaffolding project to help with it, we rely on each other’s efforts to have materials like that. Now, it’s your turn; fork the project (or create it from scratch) and start making your customizations.

The GitHub repository for this example can be found [here](https://github.com/iamjuliosampaio/crud-rails-angular). Have fun!

[1]: https://www.honeybadger.io/blog/angular-rails-5/#authorDetails
[2]: https://www.honeybadger.io/blog/angular-rails-5/