Blog Series: Building a Single Page Application (SPA) with Angular and Rails (Part 5)
This 6-part blog series takes you through building a single-page application (SPA) using Angular for the frontend and Ruby on Rails for the backend. By the end of this series, you’ll have a fully functional SPA and understand the integration of these two frameworks.

Part 5: Building a Reactive SPA
Step 1: Use Angular Reactive Forms for Dynamic Inputs
Reactive forms provide a powerful and flexible approach to handling form input and validation in Angular. First, import the ReactiveFormsModule
into your application module.
Update app.module.ts
:
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
// other components
],
imports: [
ReactiveFormsModule,
// other modules
],
bootstrap: [AppComponent]
})
export class AppModule {}
Step 2: Create a Reactive Form for Articles
Update ArticlesComponent
to include a form for creating articles:
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ArticleService } from '../services/article.service';
@Component({
selector: 'app-articles',
templateUrl: './articles.component.html',
styleUrls: ['./articles.component.css']
})
export class ArticlesComponent implements OnInit {
articles: Article[] = [];
articleForm: FormGroup;
constructor(
private articleService: ArticleService,
private fb: FormBuilder
) {
this.articleForm = this.fb.group({
title: ['', Validators.required],
content: ['', Validators.required]
});
}
ngOnInit(): void {
this.articleService.getArticles().subscribe(data => {
this.articles = data;
});
}
createArticle(): void {
if (this.articleForm.valid) {
this.articleService.createArticle(this.articleForm.value).subscribe(newArticle => {
this.articles.push(newArticle);
this.articleForm.reset();
});
}
}
}
Update articles.component.html
to include the form:
<form [formGroup]="articleForm" (ngSubmit)="createArticle()">
<label for="title">Title</label>
<input id="title" formControlName="title">
<label for="content">Content</label>
<textarea id="content" formControlName="content"></textarea>
<button type="submit" [disabled]="articleForm.invalid">Create Article</button>
</form>
<div *ngFor="let article of articles">
<h2>{{ article.title }}</h2>
<p>{{ article.content }}</p>
</div>
Step 3: Add Real-Time Updates with Rails ActionCable
Install actioncable
in your Angular app:
npm install @rails/actioncable
Create a CableService
to manage WebSocket connections:
import { Injectable } from '@angular/core';
import * as ActionCable from '@rails/actioncable';
@Injectable({ providedIn: 'root' })
export class CableService {
private cable = ActionCable.createConsumer('ws://localhost:3000/cable');
subscribeToArticles(callback: (article: any) => void): void {
this.cable.subscriptions.create('ArticlesChannel', {
received: callback
});
}
}
Update ArticlesComponent
to include real-time updates:
constructor(
private articleService: ArticleService,
private cableService: CableService
) {
// other code
}
ngOnInit(): void {
this.articleService.getArticles().subscribe(data => {
this.articles = data;
});
this.cableService.subscribeToArticles((newArticle) => {
this.articles.push(newArticle);
});
}
Create a Rails ArticlesChannel
:
rails generate channel Articles
Update articles_channel.rb
:
class ArticlesChannel < ApplicationCable::Channel
def subscribed
stream_from 'articles'
end
end
Broadcast new articles in the ArticlesController
:
def create
article = Article.new(article_params)
if article.save
ActionCable.server.broadcast 'articles', article
render json: article, status: :created
else
render json: article.errors, status: :unprocessable_entity
end
end
We will learn about deploying in the next part ..