Criando um CRUD com Angular e Nodejs + Sequelize
Para ficar completo o post anterior (ORM Sequelize com database existente do Postgres no Nodejs), criei um crud para melhor visualização das informações.
Usei o framework Angular, que permite desenvolver aplicações web e mobile mantido pela Google.
Apesar de ter mantido o nome Angular é outro conceito de desenvolvimento, sendo assim quem quiser aprender sugiro a nova versão.
Verificações de instalações!
Antes de começar é necessário certificar que o Nodejs 6.9.5 (ou Superior) e o NPM 3 (ou Superior) estão instalados.
$ node -v
$ npm -v
Instalei uma das maiores novidades do Angular o @angular/cli (para aplicações angulares baseadas no projeto ember-cli) e a linguagem adotada é o Typescript, para desenvolvimento JavaScript em larga escala. Com TypeScript é possível escrever código utilizando uma estrutura fortemente tipada e ter este código compilado para JavaScript puro. Qualquer navegador, qualquer host.
$ npm i -g typescript @angular/cli
Criei o projeto Angular dentro do diretório database_existente (projeto do post anterior)
$ ng new myApp
$ cd myApp
Testei o projeto utilizando o comando abaixo
$ ng serve
Gerando e atendendo um projeto Angular através de um servidor de desenvolvimento navegue até http://localhost:4200/. O aplicativo será recarregado automaticamente se você alterar qualquer um dos arquivos de origem.
Pode-se configurar o host e a porta HTTP padrão usados pelo servidor de desenvolvimento com duas opções de linha de comando:
$ ng serve --host 0.0.0.0 --port 4201
Pronto, projeto criado com sucesso.
Deixei o ng serve executando, assim toda alteração feita irá compilar e mostrar no navegador
Utilizei o framework Bootstrap, jquery e o fontawesome
$ npm i --save bootstrap jquery font-awesome
Alterei o .angular-cli.json
para adicionar o caminho dos mesmos
//Antes
"styles": [
"styles.css"
],
"scripts": []
//Depois adicionei o bootstrap, jquery e o font-awesome
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.css",
"../node_modules/font-awesome/css/font-awesome.css",
"styles.css"
],
"scripts": [
"../node_modules/jquery/dist/jquery.js",
"../node_modules/bootstrap/dist/js/bootstrap.js"
]
Construindo o Front-end
Adicinei um navbar no arquivo myApp/src/app/app.component.html
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Menu</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Cadastro <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Setor</a></li>
<li><a href="#">Produto</a></li>
</ul>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
Verifiquei se o ng serve
estava rodando.
Criei o Component (Representam uma unidade unificada de View + Model + Style que você deve compor para criar uma aplicação) dentro do diretório myApp/src/app/
$ ng g c setor
Generate / Component - gerou-se os arquivos setor.component.css
, setor.component.html
, setor.component.spec.ts
, setor.component.ts
e importei no app.module.ts
Criei um arquivo setor.service.ts
dentro do diretório src/app/setor
$ ng g service setor.service
Importei no @ngModule
do app.module.ts
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 { SetorComponent } from './setor/setor.component';
//Adicionei esse import
import { SetorService } from './setor/setor.service';
@NgModule({
declarations: [
AppComponent,
SetorComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
//Adicionei o service no providers - é um recurso que o Angular usa para fornecer (resultar, gerar) algo que queremos usar.
//Se planeja usar um objeto várias vezes, por exemplo serviço Http em diferentes componentes, você pode pedir a mesma instância desse serviço (reutilizá-lo)
providers: [SetorService],
bootstrap: [AppComponent]
})
export class AppModule { }
Incluí o código no arquivo setor.service.ts
import { Injectable } from '@angular/core';
//Importei o Http, Response, Headers, RequestOptions para requisições no Back-end
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class SetorService {
headers: any = new Headers({ 'Content-Type': 'application/json' });
options: any = new RequestOptions({ headers: this.headers });
constructor(private http: Http) { }
//Função que irá fazer uma request no server para buscar a lista de setores
getSetor() {
return this.http.get('/listSetor')
.map(response => response.json())
}
//Função que irá fazer uma request no server para buscar apenas o setor de um determinado ID
getSetorId(data) {
return this.http.post('/byIdSetor', { id: data }, this.options)
.map(response => response.json())
}
//Função que irá fazer uma request no server para inserir um setor
saveSetor(data) {
return this.http.post('/saveSetor', data, this.options)
.map(response => response.json())
}
//Função que irá fazer uma request no server para dar update em um setor
updateSetor(data) {
return this.http.post('/updateSetor', data, this.options)
.map(response => response.json())
}
//Função que irá fazer uma request no server para deletar um setor
delSetor(data) {
return this.http.post('/deleteSetor', data, this.options)
.map(response => response.json())
}
}
“Observable matriz cujos itens chegam de forma assíncrona ao longo do tempo. Observables ajudam a gerenciar dados assíncronos, como dados proveniêntes de um serviço back-end. Observables são usados dentro do próprio Angular, incluindo o sistema do evento do Angular e seu serviço do cliente do HTTP.”
Criei a interface adicionando o código no arquivo setor.component.html
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<div class="box-tools">
<div class="input-group">
<button class="btn btn-sm btn-primary" (click)="addSetor()"><i class="glyphicon glyphicon-plus"></i>Adicionar</button>
<input type="text" name="table_search" class="form-control input-sm pull-right" style="width: 150px;" placeholder="Pesquisa Setor" [(ngModel)]="filtro" />
<div class="input-group-btn">
<button class="btn btn-sm btn-default"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>Código</th>
<th>Descrição</th>
<th>Ação</th>
</tr>
<tr *ngFor="let setor of obterSetores()">
<td></td>
<td></td>
<td>
<div class="buttons">
<button class="btn btn-success btn-xs" (click)="editSetor(setor)">
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span>
Editar
</button>
<button class="btn btn-danger btn-xs" (click)="delSetor(setor)">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
Exluir
</button>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</section>
- (ngModel) - two-way data binding cria uma instância FormControl de um modelo de domínio e vincula a um elemento de controle de formulário.
- (click) - Event binding ( (event) ) sintaxe de vinculação de eventos que consiste em um nome de evento de destino entre (parênteses) à esquerda de um sinal de igual e uma indicação de modelo citado à direita.
Criei um arquivo setor.ts
que é uma classe com os atributos:
export class Setor {
id: number;
descricao: string;
}
Adicionei as funções dos events no arquivo setor.component.ts
import { Component, OnInit } from '@angular/core';
//Importei o Router devido os eventos click "editar", "excluir" e "adicionar"
import { Router } from '@angular/router';
//Importei o SetorService que foi criado anteriormente e a class Setor
import { SetorService } from './setor.service';
import { Setor } from './setor';
@Component({
selector: 'app-setor',
templateUrl: './setor.component.html',
styleUrls: ['./setor.component.css']
})
export class SetorComponent implements OnInit {
public setores: Setor[];
filtro: string;
private errorMessage: string;
//Adicionei as instâncias SetorService, e Router
constructor(private service: SetorService, private router: Router) { }
ngOnInit() {
this.onLoad()
}
onLoad() {
//Estou invocando o método getSetor do SetorService
this.service.getSetor()
.subscribe(data => this.setores = data,
error => this.errorMessage = <any>error);
}
addSetor() {
this.router.navigate(['/app-manutencao']) //Criarei o component "app-manutenção"
}
editSetor(setor) {
this.router.navigate(['/app-manutencao', {id: setor.id}]) //Passando parâmetro pelo Router Navigate
}
delSetor(setor) {
if (confirm(`Deseja excluir o setor ${setor.id} ?`)) {
this.service.delSetor(setor)
.subscribe(data => {
if(data){
alert('Registro excluído com sucesso')
this.onLoad()
}else{
alert('Registro não foi excluído')
}
}, error => this.errorMessage = <any>error)
}
}
//Função para filtrar dados a partir de um input do usuário
obterSetores() {
if(this.setores === undefined || this.filtro === undefined || this.filtro.trim() === '' ) {
return this.setores
}
return this.setores.filter((setor) => {
if (setor.descricao.toLowerCase().indexOf(this.filtro.toLowerCase()) >= 0 ) {
return true
} else {
return false
}
})
}
}
- Router permite a navegação de uma visão para a próxima, à medida que os usuários executam tarefas de aplicação.
- Constructor é inicializado pelo mecanismo de JavaScript, e o TypeScript nos permite dizer ao Angular quais dependências precisam ser mapeadas em relação a uma propriedade específica.
- ngOnInit ciclo de vida que é chamado após as propriedades de uma diretiva vinculadas a dados são inicializados.
Criei a manutenção dos dados (Insert/Update), dentro do diretório myApp/src/app/setor/
$ ng g c manutencao
Dentro do diretório myApp/src/app/setor/manutencao/
geraram-se os arquivos manutencao.component.css
, manutencao.component.html
, manutencao.component.spec.ts
, manutencao.component.ts
e importei no app.module.ts
Criei a interface adicionando o código no arquivo manutencao.component.html
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<form name="form" (ngSubmit)="save()" #setor="ngForm">
<div class="box-header">
<div class="box-tools">
<div class="input-group">
<button type="submit" class="btn btn-sm btn-success" [disabled]="!setor.form.valid"><i name="save" id="save" class="glyphicon glyphicon-ok"></i>Salvar</button>
<button class="btn btn-sm btn-danger" (click)="cancel()"><i class="glyphicon glyphicon-remove"></i>Cancelar</button>
</div>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">Informe os dados</h3>
</div>
<div class="form-group">
<input type="text" id="nameSetor" name="nameSetor" class="form-control" placeholder="Informe a descrição do setor" maxlength="100" [(ngModel)]="model.descricao" #nameSetor="ngModel" required/>
<span [hidden]="nameSetor.valid || nameSetor.pristine" class="alert alert-danger">descrição do setor</span>
</div>
</div>
<!-- /.box -->
</form>
</div>
</div>
</div>
</section>
Incluí as funções dos events do código anterior no arquivo manutencao.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Subscription, Observable } from 'rxjs/Rx';
import { SetorService } from '../setor.service';
import { Setor } from '../setor';
@Component({
selector: 'app-manutencao',
templateUrl: './manutencao.component.html',
styleUrls: ['./manutencao.component.css']
})
export class ManutencaoComponent implements OnInit {
public model: Setor;
private isNew: boolean = true;
private subscription: Subscription;
private errorMessage: string;
constructor(private service: SetorService, private router: Router, private route: ActivatedRoute) { }
ngOnInit(): void {
this.model = new Setor();
this.subscription = this.route.params.subscribe((params: any) => {
if (params.hasOwnProperty('id')) {
this.isNew = false;
this.service.getSetorId(params['id'])
.subscribe(data => this.model = data[0],
error => this.errorMessage = <any>error);
} else {
this.isNew = true;
}
}
)
}
save() {
let alerta = 'Registro gravado com sucesso!';
let alError = 'Verificar os dados, não foi gravado';
if(this.isNew){
this.service.saveSetor(this.model)
.subscribe(data => {
if(data){
alert(alerta)
this.router.navigate(['/app-setor'])
}else{
alert(alError)
}
}, error => this.errorMessage = <any>error)
}else{
this.service.updateSetor(this.model)
.subscribe(data => {
if(data){
alert(alerta)
this.router.navigate(['/app-setor'])
}else{
alert(alError)
}
}, error => this.errorMessage = <any>error)
}
}
cancel() {
this.router.navigate(['/app-setor'])
}
}
Adicionando o Router…
Importei o Router no arquivo app.module.ts
para funcionar o navigate e o router-link que irei incluir no Menu
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
//Acrescentei esse código
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { SetorComponent } from './setor/setor.component';
import { SetorService } from './setor/setor.service';
import { ManutencaoComponent } from './setor/manutencao/manutencao.component';
//Acrecentei esse código
const appRoutes: Routes = [
{ path: 'app-setor', component: SetorComponent },
{ path: 'app-manutencao', component: ManutencaoComponent }
{ path: '**', redirectTo: '/' }
];
@NgModule({
declarations: [
AppComponent,
SetorComponent,
ManutencaoComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(appRoutes, {useHash: true}) //Adicionei essa linha
],
providers: [SetorService],
bootstrap: [AppComponent]
})
export class AppModule { }
Modifiquei o app.component.html
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Menu</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Cadastro <span class="caret"></span></a>
<ul class="dropdown-menu">
//Alterado href="#" para [routerLink]
<li><a [routerLink]="['/app-setor']">Setor</a></li>
<li><a href="#">Produto</a></li>
</ul>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
//Incluí o router-outlet
<router-outlet></router-outlet>
router-outlet atua como um espaço reservado que o Angular preenche dinamicamente com base no estado atual do roteador.
Para fazer o produto é o mesmo processo.
Criei o Component produto dentro do diretório myApp/src/app/
$ ng g c produto
Gerou-se os arquivos produto.component.css
, produto.component.html
, produto.component.spec.ts
, produto.component.ts
e importei no app.module.ts
Criei um arquivo produto.service.ts
dentro do diretório src/app/produto/
$ ng g service produto.service
Importei no @ngModule
do app.module.ts
e adicionei no Router a rota do produto
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { SetorComponent } from './setor/setor.component';
import { SetorService } from './setor/setor.service';
import { ManutencaoComponent } from './setor/manutencao/manutencao.component';
import { ProdutoComponent } from './produto/produto.component';
import { ProdutoService } from './produto/produto.service'; //Incluí essa linha
const appRoutes: Routes = [
{ path: 'app-setor', component: SetorComponent },
{ path: 'app-manutencao', component: ManutencaoComponent },
{ path: 'app-produto', component: ProdutoComponent } //Adicionei essa linha
{ path: '**', redirectTo: '/' }
];
@NgModule({
declarations: [
AppComponent,
SetorComponent,
ManutencaoComponent,
ProdutoComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(appRoutes, {useHash: true})
],
providers: [SetorService, ProdutoService], //Adicionei ProdutoService
bootstrap: [AppComponent]
})
export class AppModule { }
Adicionei o código no arquivo produto.service.ts
import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class ProdutoService {
headers: any = new Headers({ 'Content-Type': 'application/json' });
options: any = new RequestOptions({ headers: this.headers });
constructor(private http: Http) { }
getProduto() {
return this.http.get('/listProduto')
.map(response => response.json())
}
getProdutoId(data) {
return this.http.post('/byIdProduto', { id: data }, this.options)
.map(response => response.json())
}
saveProduto(data) {
return this.http.post('/saveProduto', data, this.options)
.map(response => response.json())
}
updateProduto(data) {
return this.http.post('/updateProduto', data, this.options)
.map(response => response.json())
}
delProduto(data) {
return this.http.post('/deleteProduto', data, this.options)
.map(response => response.json())
}
}
Criei a interface adicionando o código no arquivo produto.component.html
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<div class="box-tools">
<div class="input-group">
<button class="btn btn-sm btn-primary" (click)="addProduto()"><i class="glyphicon glyphicon-plus"></i>Adicionar</button>
<input type="text" name="table_search" class="form-control input-sm pull-right" style="width: 150px;" placeholder="Pesquisa Produto" [(ngModel)]="filtro" />
<div class="input-group-btn">
<button class="btn btn-sm btn-default"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>Código</th>
<th>Descrição</th>
<th>Barra</th>
<th>Setor</th>
<th>Ação</th>
</tr>
<tr *ngFor="let produto of obterProdutos()">
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<div class="buttons">
<button class="btn btn-success btn-xs" (click)="editProduto(produto)">
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span>
Editar
</button>
<button class="btn btn-danger btn-xs" (click)="delProduto(produto)">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
Exluir
</button>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</section>
Criei um arquivo produto.ts
que é uma classe com os atributos:
export class Produto {
id: number;
descricao: string;
barra: string;
id_setor: number;
}
Incluí as funções dos events no arquivo produto.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ProdutoService } from './produto.service';
import { Produto } from './produto';
@Component({
selector: 'app-produto',
templateUrl: './produto.component.html',
styleUrls: ['./produto.component.css']
})
export class ProdutoComponent implements OnInit {
public produtos: Produto[];
filtro: string;
private errorMessage: string;
constructor(private service: ProdutoService, private router: Router) { }
ngOnInit() {
this.onLoad()
}
onLoad() {
this.service.getProduto()
.subscribe(data => this.produtos = data,
error => this.errorMessage = <any>error);
}
addProduto() {
this.router.navigate(['/app-manutencao-produto'])
}
editProduto(produto) {
this.router.navigate(['/app-manutencao-produto', {id: produto.id}])
}
delProduto(produto) {
if (confirm(`Deseja excluir o produto ${produto.id} ?`)) {
this.service.delProduto(produto)
.subscribe(data => {
if(data){
alert('Registro excluído com sucesso')
this.onLoad()
}else{
alert('Registro não foi excluído')
}
}, error => this.errorMessage = <any>error)
}
}
obterProdutos() {
if(this.produtos === undefined || this.filtro === undefined || this.filtro.trim() === '' ) {
return this.produtos
}
return this.produtos.filter((produto) => {
if (produto.descricao.toLowerCase().indexOf(this.filtro.toLowerCase()) >= 0 ) {
return true
} else {
return false
}
})
}
}
Criei a manutenção dos dados (Insert/Update), dentro do diretório myApp/src/app/produto/
$ ng g c manutencao
Dentro do diretório myApp/src/app/produto/manutencao/ geraram-se os arquivos manutencao.component.css
, manutencao.component.html
, manutencao.component.spec.ts
, manutencao.component.ts
e importei no app.module.ts
Criei a interface adicionando o código no arquivo manutencao.component.html
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<form (ngSubmit)="save()" #produtoForm="ngForm">
<div class="box-header">
<div class="box-tools">
<div class="input-group">
<button type="submit" class="btn btn-sm btn-success" [disabled]="!produtoForm.form.valid"><i name="save" id="save" class="glyphicon glyphicon-ok"></i>Salvar</button>
<button class="btn btn-sm btn-danger" (click)="cancel()"><i class="glyphicon glyphicon-remove"></i>Cancelar</button>
</div>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">Informe os dados</h3>
</div>
<div class="form-group">
<input type="text" id="descricao" name="descricao" class="form-control" placeholder="Informe a descrição do produto" maxlength="100" [(ngModel)]="model.descricao" #descricao="ngModel" required/>
<div [hidden]="descricao.valid || descricao.pristine" class="alert alert-danger">Descrição do produto</div>
</div>
<div class="form-group">
<input type="text" id="barra" name="barra" class="form-control" placeholder="Informe o código de barras" maxlength="14" [(ngModel)]="model.barra" #barra="ngModel" required/>
<div [hidden]="barra.valid || barra.pristine" class="alert alert-danger">Código de barras</div>
</div>
<div class="form-group">
<select class="form-control" id="setor" required [(ngModel)]="model.id_setor" name="setor" #setor="ngModel">
<option *ngFor="let setor of setores" [value]="setor.id"></option>
</select>
<div [hidden]="setor.valid || setor.pristine" class="alert alert-danger">Setor</div>
</div>
</div>
<!-- /.box -->
</form>
</div>
</div>
</div>
</section>
Adicionei as funções dos events do código anterior no arquivo manutencao.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Subscription, Observable } from 'rxjs/Rx';
import { ProdutoService } from '../produto.service';
import { SetorService } from '../../setor/setor.service';
import { Produto } from '../produto';
import { Setor } from '../../setor/setor'
@Component({
selector: 'app-manutencao-produto', //Alterei o nome de app-manutencao para app-manutencao-produto não da conflito
templateUrl: './manutencao.component.html',
styleUrls: ['./manutencao.component.css']
})
export class ManutencaoProdutoComponent implements OnInit {
public model: Produto;
public setores: Setor[];
private isNew: boolean = true;
private subscription: Subscription;
private errorMessage: string;
constructor(
private service: ProdutoService,
private setorService: SetorService,
private router: Router,
private route: ActivatedRoute
) { }
ngOnInit(): void {
this.model = new Produto();
this.onGetSetores()
this.subscription = this.route.params.subscribe((params: any) => {
if (params.hasOwnProperty('id')) {
this.isNew = false;
this.service.getProdutoId(params['id'])
.subscribe(data => this.model = data[0],
error => this.errorMessage = <any>error);
} else {
this.isNew = true;
}
}
)
}
onGetSetores() {
return this.setorService.getSetor()
.subscribe(data => this.setores = data,
error => this.errorMessage = <any>error);
}
save() {
let alerta = 'Registro gravado com sucesso!';
let alError = 'Verificar os dados, não foi gravado';
if(this.isNew){
this.service.saveProduto(this.model)
.subscribe(data => {
if(data){
alert(alerta)
this.router.navigate(['/app-produto'])
}else{
alert(alError)
}
}, error => this.errorMessage = <any>error)
}else{
this.service.updateProduto(this.model)
.subscribe(data => {
if(data){
alert(alerta)
this.router.navigate(['/app-produto'])
}else{
alert(alError)
}
}, error => this.errorMessage = <any>error)
}
}
cancel() {
this.router.navigate(['/app-produto'])
}
}
Incluí a rota no arquivo app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { SetorComponent } from './setor/setor.component';
import { SetorService } from './setor/setor.service';
import { ManutencaoComponent } from './setor/manutencao/manutencao.component';
import { ProdutoComponent } from './produto/produto.component';
import { ProdutoService } from './produto/produto.service';
import { ManutencaoProdutoComponent } from './produto/manutencao/manutencao.component';
const appRoutes: Routes = [
{ path: 'app-setor', component: SetorComponent },
{ path: 'app-manutencao', component: ManutencaoComponent },
{ path: 'app-produto', component: ProdutoComponent },
{ path: 'app-manutencao-produto', component: ManutencaoProdutoComponent }, //Adicionei essa linha
{ path: '**', redirectTo: '/' }
];
@NgModule({
declarations: [
AppComponent,
SetorComponent,
ManutencaoComponent,
ProdutoComponent,
ManutencaoProdutoComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(appRoutes, {useHash: true})
],
providers: [SetorService, ProdutoService],
bootstrap: [AppComponent]
})
export class AppModule { }
Modifiquei o app.component.html
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Menu</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Cadastro <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a [routerLink]="['/app-setor']">Setor</a></li>
//Alterado href="#" para [routerLink]
<li><a [routerLink]="['/app-produto']">Produto</a></li>
</ul>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
<router-outlet></router-outlet>
Gerei o build com o comando:
$ ng build
Gerou-se o diretório dist dentro do /myApp/
Modifiquei o arquivo setor.js
do back-end /modulos/setor/controller/
exports.update = (req, res) => {
const dados = req.body;
model.Setor
.update(dados, {
where: {
id: dados.id //alterei essa linha
}
})
.then((data) => }
res.send(true);
}).catch((error) => {
console.log(error);
res.send(false);
});
};
exports.delete = (req, res) => {
const dados = req.body;
model.Setor
.destroy({
where: {
id: dados.id //alterei essa linha
}
})
.then((rowDeleted) => {
res.send(true);
}, (err) => {
console.log(err);
res.send(false);
});
};
Modifiquei o arquivo produto.js
do back-end /modulos/produto/controller/
exports.update = (req, res) => {
const dados = req.body;
model.Produto
.update(dados, {
where: {
id: dados.id //alterei essa linha
}
})
.then((data) => }
res.send(true);
}).catch((error) => {
console.log(error);
res.send(false);
});
};
exports.delete = (req, res) => {
const dados = req.body;
model.Produto
.destroy({
where: {
id: dados.id //alterei essa linha
}
})
.then((rowDeleted) => {
res.send(true);
}, (err) => {
console.log(err);
res.send(false);
});
};
Modifiquei o arquivo app.js
do back-end que se encontra na raíz do projeto.
'use strict'
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const setor = require('./modulos/setor/setor');
const produto = require('./modulos/produto/produto');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
//alterei a linha abaixo adicionando o diretório "dist"
app.use(express.static(path.join(__dirname, './myApp/dist/')));
setor.init(app);
produto.init(app);
module.exports = app;
Para rodar a api…
$ node bin/www
Listening on port 3000
Conclusão
Com o @angular/cli é possivel criar um single-page application (SPA) simples em pouco tempo. Traduzir de uma linguagem para outra é principalmente uma questão de mudar a maneira como você organiza seu código e acessa APIs Angulares, qualquer coisa que você pode fazer com Angular no TypeScript você também pode fazer em JavaScript.
Estou disponibilizando o projeto no GitHub , acesse lá.