Criando um CRUD com Angular 2 + Rails 5

post-rails5-angular2
Introdução 🙂

Os Apps profissionais seguem há algum tempo na direção de ter uma divisão entre o backend (API) e o front end (Cliente Web), recentemente foi lançado a versão 2 do Angular (que tem pouco a ver com a versão 1) e também a versão 5 do Rails (que tem muito a ver com a versão 4 🙂 ), nesse tutorial eu vou demonstrar como conectar essas duas tecnologias para criar um App incrível \o/.

Nós vamos criar um CRUD de uma pequena aplicação de FAQ, e para fazer isso mais rapidamente nós vamos usar uma ferramenta bem legal que chama Angular-CLI (command line interface), que vai nos permitir gerar o projeto Angular 2, os components e os services facilmente (similar aos generates do Rails).

O que vamos aprender?
  1. Como criar um CRUD em uma aplicação Rails 5
  2. Como criar um CRUD em uma aplicação Angular 2
  3. Como conectar o Rails 5 com o Angular 2

INGREDIENTES
Objetivos

Criar um Pequeno App que nos permita criar questões para um FAQ, com o front end em Angular 2 e o Backend no Rails 5.

Passo a Passo
  • Rails 5
    1. Criar o Projeto Rails Api
    2. Gerar o CRUD
    3. Incluir o CORS
  • Angular 2
    1. Criar o Projeto Angular usando o Angular-CLI
    2. Configurar nosso projeto
    3. Criar nossos components
    4. Criar nosso service
    5. Editar nossas views
    6. Conectar tudo isso 🙂
Mãos à Obra \o/
Parte 1 – Criando o projeto Rails 5

Primeiro vamos gerar o nosso projeto Rails 5 API que vai ser consumido via JSON pelo projeto Angular 2.

  1. Para começar, rode no seu console o comando para gerar o projeto:
    1
    
    rails new noticias --api

    *a flag –api significa que o rails vai configurar o projeto como uma API pra gente

  2. Agora vamos gerar nosso scaffold, rode:
    1
    
    rails g scaffold questions title:string text:text
  3. Rode as migrations:
    1
    
    rake db:migrate
  4. Agora vamos configurar o CORS para que nosso APP possa receber chamada de outros domínios, primeiro no seu Gemfile adicione:
    1
    
    gem 'rack-cors'

    *O que é CORS

  5. Rode o bundle, para instalar a Gem:
    1
    
    bundle
  6. Agora no arquivo config/application.rb, substitua o conteúdo por:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    require_relative 'boot'
     
    require "rails"
    # Pick the frameworks you want:
    require "active_model/railtie"
    require "active_job/railtie"
    require "active_record/railtie"
    require "action_controller/railtie"
    require "action_mailer/railtie"
    require "action_view/railtie"
    require "action_cable/engine"
    # require "sprockets/railtie"
    require "rails/test_unit/railtie"
     
    # Require the gems listed in Gemfile, including any gems
    # you've limited to :test, :development, or :production.
    Bundler.require(*Rails.groups)
     
    module News
      class Application < Rails::Application
        # Nessa parte estamos configurando o CORS para aceitar chamadas de outras páginas
        config.middleware.insert_before 0, "Rack::Cors" do
          allow do
            origins '*'
            resource '*', :headers => :any, :methods => [:get, :post, :options, :delete, :put]
          end
        end
        # Settings in config/environments/* take precedence over those specified here.
        # Application configuration should go into files in config/initializers
        # -- all .rb files in that directory are automatically loaded.
     
        # Only loads a smaller set of middleware suitable for API only apps.
        # Middleware like session, flash, cookies can be added back manually.
        # Skip views, helpers and assets when generating a new resource.
        config.api_only = true
      end
    end
  7. Para testar, rode o servidor e visite http://localhost:3000/questions
  8. Pronto \o/, nossa aplicação Rails já está pronta. Agora vamos para o Angular 2 🙂

 

Pausa para o Merchan do BEM! 🙂
BOOTCAMP ONE BIT CODE – SUBA DE LEVEL NO MUNDO DO RUBY ON RAILS

Este é o último final de semana para se inscrever no primeiro Bootcamp online do OneBitCode!
Neste curso, nós vamos criar do zero um site similar ao Airbnb usando Rails 5 como Api e Angular 2 como cliente Web.
O curso vai ter vídeo aulas semanais, live de 2 horas por semana, suporte total às dúvidas e um Slack exclusivo para os alunos desenvolverem network.
O curso vai ser ÉPICO, então confere a Landing Page dele e faça parte do grupo de alunos One Bit Code! \o/
Conheça o Bootcamp!

Bootcamp OneBitCode
Parte 2 – Criando o App Angular 2

Agora sim começa a parte mais emocionante complicada, vamos criar nosso APP Angular 2 e para fazer isso vamos usar o Angular-CLI.

Dependências

Primeiro, vamos começar com as dependências.

  1. Você vai precisar ter o NPM instalado na sua máquina. Para instalar no Ubuntu:
    1
    
    sudo apt-get install npm

    Para instalar no Mac:

    1
    
    brew install node
  2. Agora você precisa instalar o Angular-CLI:
    1
    
    sudo npm install -g angular-cli --allow-root
Criação do App

Finalmente, vamos a criação do App.

  1. Para gerar o projeto, rode na pasta desejada (fora do projeto Rails) no console:
    1
    
    ng new questions
  2. Agora vamos ver se tudo funcionou corretamente, dentro do projeto, rode no console:
    1
    
    ng serve  --port 9000
  3. Acesse no seu browser, http://localhost:9000, deve aparecer uma mensagem dizendo que funcionou 🙂
  4. Agora vamos gerar nossos components, rode:
    1
    
    ng generate component questions

    *Os components são o coração do Angular 2, eles são a principal maneira que nós criamos elementos, definimos as views e a lógica

  5. Depois rode:
    1
    
    ng generate component questions/question-form
  6. Agora vamos gerar nosso Modelo de dados, crie uma pasta no caminho “/src/app/questions/” com o nome shared (“/src/app/questions/shared”).
  7. Depois crie um arquivo dentro dessa pasta chamado “question.ts” e coloque o seguinte conteúdo:
    1
    2
    3
    4
    5
    
    export class Question {
      id: number;
      title: string;
      text: string;
    }
  8. Vamos criar nosso Service (ele serve para o Angular 2 conversar com a API), no console rode o comando:
    1
    
    ng generate service questions/shared/question
  9. Agora vamos adicionar o router outlet para fazer nossas rotas funcionarem, no nosso arquivo ‘src/app/app.componet.html’ substitua o conteúdo por:
    1
    2
    3
    4
    5
    
    <h1>
      Simple Faq creator
    </h1>
    <hr />
    <router-outlet></router-outlet>
  10. Agora nós vamos criar o nosso arquivo de rotas (que vai gerenciar nossas URLs), no caminho “src/app/” crie o arquivo “app.routing.ts” e preencha ele com o seguinte conteúdo:
    *Note os comentários no código

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    import { ModuleWithProviders } from '@angular/core';
    // Importa o modulo de rotas do Angular 2
    import { Routes, RouterModule }   from '@angular/router';
     
    // Importa seus componentes criados
    import { QuestionsComponent } from './questions/questions.component';
    import { QuestionFormComponent } from './questions/question-form/question-form.component';
     
     
    // Cria nossas Rotas
    const appRoutes: Routes = [
      { path: '', pathMatch: 'full', component: QuestionsComponent },
      { path: 'questions/new', component: QuestionFormComponent},
      { path: 'questions/:id', component: QuestionFormComponent},
      { path: 'questions/:id/edit', component: QuestionFormComponent}
    ];
     
    // Exporta a constante routing para importarmos ela no arquivo app.module.ts
    export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
  11. Agora nós precisamos alterar nosso arquivo ‘src/app/app.module.ts’ para incluir nosso arquivo de rotas e algumas dependências do projeto, seu arquivo deve ficar assim:
    *Note os comentários no código

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    // Dependências do Angular 2
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { HttpModule } from '@angular/http';
     
    import { AppComponent } from './app.component';
    import { QuestionsComponent } from './questions/questions.component';
    // Adicionamos o arquivo routing
    import { routing } from './app.routing';
     
    // Inclui nossos components
    import { QuestionFormComponent } from './questions/question-form/question-form.component';
    import { QuestionService } from './questions/shared/question.service';
     
    // Adicionamos em imports a contant routing e também nosso service em Providers
    @NgModule({
      declarations: [
        AppComponent,
        QuestionsComponent,
        QuestionFormComponent
      ],
      imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        routing
      ],
      providers: [QuestionService],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
  12. Agora nós vamos editar nossos components, para começar vamos editar nosso componente de listagem, no arquivo ‘/src/app/questions/questions.component.ts’ substitua o conteúdo por:
    *Note os comentários no código

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
    import { Component, OnInit } from '@angular/core';
    // Importa nosso Service
    import { QuestionService } from './shared/question.service';
    // Importa nosso model
    import {Question} from "./shared/question";
     
    @Component({
      selector: 'app-questions',
      templateUrl: './questions.component.html',
      styleUrls: ['./questions.component.css']
    })
    export class QuestionsComponent implements OnInit {
     
      private questions: Question[] = [];
     
      constructor(private questionService: QuestionService) { }
     
      // Pega a listagem de Questions ao iniciar
      ngOnInit() {
        this.questionService.getQuestions()
          .subscribe(data => this.questions = data);
      }
     
      // Apaga a questão
      deleteQuestion(questions) {
        if (confirm("Você tem certeza que quer deletar a questions " + questions.title + "?")) {
          var index = this.questions.indexOf(questions);
          this.questions.splice(index, 1);
     
          this.questionService.deleteQuestion(questions.id)
            .subscribe(null);
        }
      }
     
    }
  13. Pronto, a parte lógica da listagem está ok (exceto pelo service ainda), agora vamos editar a aparência da aplicação, no arquivo ‘/src/app/questions/questions.component.html’ adicione o seguinte html:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    <p>
      Displaying {{ questions.length }} questions.
    </p>
     
    <table>
      <thead>
      <tr>
        <th data-field="name">Titulo</th>
        <th data-field="name">Texto</th>
      </tr>
      </thead>
     
      <tbody>
        <tr *ngFor="let q of questions">
          <td>{{ q.title }}</td>
          <td>{{ q.text }}</td>
     
          <td>
            <a [routerLink]="['/questions', q.id]">
              Editar
            </a>
          </td>
          <td>
            <a (click)="deleteQuestion(q)" href="">
              Deletar
            </a>
          </td>
        </tr>
      </tbody>
    </table>
     
    <div style="bottom: 45px; right: 24px;">
      <a routerLink="/questions/new">
        Novo
      </a>
    </div>
  14. Agora vamos para o próximo component, no arquivo /src/app/questions/question-form/question-form.component.ts’ cole o seguinte código para podermos editar e criar questions:
    *Note os comentários no código

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    
    import { Component, OnInit } from '@angular/core';
    // Importa nosso service para conseguirmos falar com a API
    import { QuestionService } from '../shared/question.service';
    // Importa nosso Model
    import {Question} from "../shared/question";
    // Importa o Router para podermos conseguir pegar o parâmetro id
    import { Router, ActivatedRoute } from '@angular/router';
     
     
    @Component({
      selector: 'app-question-form',
      templateUrl: './question-form.component.html',
      styleUrls: ['./question-form.component.css']
    })
    export class QuestionFormComponent implements OnInit {
     
      // Cria uma variável string para falarmos se é uma edição ou criação de Question
      title: string;
      // Pega nosso Model e coloca na variável question
      question: Question = new Question();
     
      constructor(
        //Declara nossas dependências
        private questionService: QuestionService,
        private router: Router,
        private route: ActivatedRoute
      ) { }
     
      // Esse método rola enquanto a página é carregada para preencher
      // a question caso seja edição
      ngOnInit() {
        var id = this.route.params.subscribe(params => {
          var id = params['id'];
     
          this.title = id ? 'Edit Faq Question' : 'Create Faq Question';
     
          if (!id)
            return;
     
          this.questionService.getQuestion(id)
            .subscribe(
              question => this.question = question,
              response => {});
        });
      }
     
      // Nós chamamos esse método no form quando estamos prontos para criar
      // uma questão ou editar
      save() {
        var result;
     
        if (this.question.id){
          result = this.questionService.updateQuestion(this.question);
        } else {
          result = this.questionService.addQuestion(this.question);
        }
     
        result.subscribe(data => this.router.navigate(['/']));
      }
     
    }
  15. Pronto, a parte lógica da edição/criação está ok (exceto pelo service ainda), agora vamos editar a aparência da aplicação, no arquivo ‘/src/app/questions/question-form/question-form.component.html’ adicione o seguinte html:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    <div class="container">
        <h1>{{ title }}</h1>
        <form (ngSubmit)="save()">
          <div>
            <label for="name">Title</label>
            <input type="text" id="title" required [(ngModel)]="question.title" name='title'>
          </div>
          <div>
            <label for="text">Text</label>
            <input type="text" id="text" required [(ngModel)]="question.text" name='text'>
          </div>
          <button type="submit">Submit</button>
        </form>
        <hr />
        <a routerLink="/">Back to Questions</a>
    </div>
  16. Excelente, nossos components estão ok, agora vamos configurar nosso service. No arquvio ‘src/app/questions/shared/questions.service.ts’ susbstitua o conteúdo pelo seguinte código:
    *Note os comentários no código

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    
    import { Injectable } from '@angular/core';
    import { Http } from '@angular/http';
     
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/do';
    import 'rxjs/add/operator/catch';
    import { Observable } from 'rxjs/Rx';
     
    @Injectable()
    export class QuestionService {
     
      // URL da nossa API
      private url: string = "http://localhost:3000/questions";
     
      constructor(private http: Http) { }
     
      // Pega as questions na API
      getQuestions(){
        return this.http.get(this.url)
          .map(res => res.json());
      }
     
      // Pega uma question na API
      getQuestion(id){
        return this.http.get(this.url + '/' + id)
          .map(res => res.json());
      }
     
      // Adiciona uma question na API
      addQuestion(question){
        return this.http.post(this.url, {'question': question})
          .map(res => res.json());
      }
     
      // Atualiza uma question na API
      updateQuestion(question){
        return this.http.put(this.url + '/' + question.id, {'question': question})
          .map(res => res.json());
      }
     
      // Apaga uma question no servidor
      deleteQuestion(id){
        return this.http.delete(this.url + '/' + id)
          .map(res => res.json());
      }
     
    }
  17. Pronto \o/, agora vamos testar.
  18. Rode no seu console:
    1
    
    ng serve --port 9000
  19. Acessando no browser, seu resultado deve ser similar a este:
    captura-de-tela-de-2016-12-10-02-49-37* É claro que não é um Faq muito bonito, mas ele funciona e é suficiente para demonstrar a integração 🙂
  20. Agora você pode criar novas questões, editá-las e deletá-las \o/, parabéns nós conseguimos!

 

CONCLUSÃO

Pronto, conseguimos fazer o nosso não tão simples CRUD, mas valeu a pena :). É claro que esse é um tutorial introdutório apenas para te dar uma visão geral de como o Angular 2 e o Rails 5 podem se comunicar e também para abrir um pouco a sua mente para a estrutura do Angular 2. Mas tenho certeza que você notou a riqueza dos dois frameworks e as possibilidades ilimitadas que eles trazem se usados juntos 🙂

Como de costume o Código completo da aplicação está no Github, caso você queria clonar o código, clique aqui para ver a parte do Angular 2 e clique aqui para acessar a parte do Rails 5. Aproveita e me segue lá \o/

Apenas informando novamente que o Bootcamp do OneBitCode está com as inscrições abertas (quase fechando já) e nesse bootcamp nós vamos nos aprofundar muito no Rails 5 e também no Angular 2 (fullstack) através da criação de um clone do Airbnb, então se você ainda não viu a landing page, VEJA CLICANDO AQUI. Vai ser épico, não deixe essa chance passar! \o/

Se você ficou com dúvidas ou tem sugestões de posts para o Blog comenta aí em baixo ou me adiciona no Facebook clicando aqui.

Muito Obrigado,
Sua presença aqui é uma honra para mim,
Leonardo Scorza 🙂

Deixe seu Feedback!

Comentários