Versão
Idioma

Tutorial Angular - Parte II

Sobre este tutorial

Esta é a segunda parte da série de tutoriais angulares. Veja todas as peças:

Você pode acessar o código fonte do aplicativo no repositório GitHub .

Criando um novo livro

Nesta seção, você aprenderá como criar um novo formulário de diálogo modal para criar um novo livro.

Definição do tipo

Criar uma interface, com o nome CreateUpdateBookInputno books.tscomo mostrado abaixo:

export namespace Books {
  //...
  export interface CreateUpdateBookInput {
    name: string;
    type: BookType;
    publishDate: string;
    price: number;
  }
}

CreateUpdateBookInputinterface corresponde ao CreateUpdateBookDtono back-end.

Método de Serviço

Abra o books.service.tse adicione um novo método, nomeado createpara executar uma solicitação HTTP POST no servidor:

create(createBookInput: Books.CreateUpdateBookInput): Observable<Books.Book> {
  return this.restService.request<Books.CreateUpdateBookInput, Books.Book>({
    method: 'POST',
    url: '/api/app/book',
    body: createBookInput
  });
}
  • restService.requestA função obtém parâmetros genéricos para os tipos enviados e recebidos do servidor. Este exemplo envia um CreateUpdateBookInputobjeto e recebe um Bookobjeto (você pode definir o tipo voidde solicitação ou retorno, se não for usado).

Definições de estado

Adicione a CreateUpdateBookação ao books.actions.tsconforme mostrado abaixo:

import { Books } from '../models';

export class CreateUpdateBook {
  static readonly type = '[Books] Create Update Book';
  constructor(public payload: Books.CreateUpdateBookInput) {}
}

Abra books.state.tse defina o savemétodo que ouvirá uma CreateUpdateBookação para criar um livro:

import { ... , CreateUpdateBook } from '../actions/books.actions';
import { ... , switchMap } from 'rxjs/operators';
//...
@Action(CreateUpdateBook)
save(ctx: StateContext<Books.State>, action: CreateUpdateBook) {
  return this.booksService
      .create(action.payload)
      .pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}

Quando a SaveBookação é despachada, o método save é executado. Ele chama o createmétodo do BooksServicedefinido anteriormente. Após a chamada de serviço, BooksStatedespacha a GetBooksação para obter livros novamente do servidor para atualizar a página.

Adicionar um modal ao BookListComponent

Abra o book-list.component.htmle adicione o abp-modalpara mostrar / ocultar o modal para criar um novo livro.

<abp-modal [(visible)]="isModalOpen">
  <ng-template #abpHeader>
    <h3>New Book</h3>
  </ng-template>

  <ng-template #abpBody> </ng-template>

  <ng-template #abpFooter>
    <button type="button" class="btn btn-secondary" #abpClose>
      Cancel
    </button>
  </ng-template>
</abp-modal>

abp-modalé um componente pré-construído para mostrar os modais. Embora você possa usar outra abordagem para mostrar um modal, abp-modalfornece benefícios adicionais.

Adicione um botão rotulado New bookpara mostrar o modal:

<div class="row">
  <div class="col col-md-6">
    <h5 class="card-title">
      Books
    </h5>
  </div>
  <div class="text-right col col-md-6">
    <button id="create-role" class="btn btn-primary" type="button" (click)="createBook()">
      <i class="fa fa-plus mr-1"></i> <span>New book</span>
    </button>
  </div>
</div>

Abra a variável book-list.component.tse adicione isModalOpene createBookmétodo para mostrar / ocultar o modal.

isModalOpen = false;

//...

createBook() {
  this.isModalOpen = true;
}

modal vazio

Criar um formulário reativo

Os formulários reativos fornecem uma abordagem orientada a modelo para lidar com entradas de formulário cujos valores mudam ao longo do tempo.

Adicione uma formvariável e injete um FormBuilderserviço book-list.component.tscomo mostrado abaixo (lembre-se de adicionar a instrução de importação).

import { FormGroup, FormBuilder } from '@angular/forms';

form: FormGroup;

constructor(
  //...
  private fb: FormBuilder
) {}

O serviço FormBuilder fornece métodos convenientes para gerar controles. Reduz a quantidade de clichê necessária para criar formulários complexos.

Adicione o buildFormmétodo para criar um formulário de livro.

buildForm() {
  this.form = this.fb.group({
    name: ['', Validators.required],
    type: [null, Validators.required],
    publishDate: [null, Validators.required],
    price: [null, Validators.required],
  });
}
  • O groupmétodo de FormBuilder( fb) cria a FormGroup.
  • Adicionado Validators.requiredmétodo estático que valida o elemento de formulário relacionado.

Modifique o createBookmétodo como mostrado abaixo:

createBook() {
  this.buildForm();
  this.isModalOpen = true;
}

Crie os elementos DOM do formulário

Abra book-list.component.htmle adicione o formulário no modelo de corpo do modal.

<ng-template #abpBody>
  <form [formGroup]="form">
    <div class="form-group">
      <label for="book-name">Name</label><span> * </span>
      <input type="text" id="book-name" class="form-control" formControlName="name" autofocus />
    </div>

    <div class="form-group">
      <label for="book-price">Price</label><span> * </span>
      <input type="number" id="book-price" class="form-control" formControlName="price" />
    </div>

    <div class="form-group">
      <label for="book-type">Type</label><span> * </span>
      <select class="form-control" id="book-type" formControlName="type">
        <option [ngValue]="null">Select a book type</option>
        <option [ngValue]="booksType[type]" *ngFor="let type of bookTypes"> {{ type }}</option>
      </select>
    </div>

    <div class="form-group">
      <label>Publish date</label><span> * </span>
      <input
        #datepicker="ngbDatepicker"
        class="form-control"
        name="datepicker"
        formControlName="publishDate"
        ngbDatepicker
        (click)="datepicker.toggle()"
      />
    </div>
  </form>
</ng-template>
  • Este modelo cria um formulário com os campos Nome, Preço, Tipo e Data de publicação.

Usamos o datepicker do NgBootstrap neste componente.

Abra o book-list.component.tse crie uma matriz chamada bookTypes:

//...
form: FormGroup;

bookTypes = Object.keys(Books.BookType).filter(
    bookType => typeof this.booksType[bookType] === 'number'
);

O bookTypescontém os campos da BookTypeenumeração. A matriz resultante é mostrada abaixo:

['Adventure', 'Biography', 'Dystopia', 'Fantastic' ...]

Essa matriz foi usada no modelo de formulário anterior (no ngForloop).

Requisitos do Datepicker

Você precisa importar NgbDatepickerModulepara o books.module.ts:

import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';

@NgModule({
  imports: [
    // ...
    NgbDatepickerModule,
  ],
})
export class BooksModule {}

Abra o book-list.component.tse adicione providerscomo mostrado abaixo:

import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';

@Component({
  // ...
  providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }],
})
export class BookListComponent implements OnInit {
// ...

O NgbDateAdaptervalor do Datepicker converte em Datetipo. Consulte os adaptadores datepicker para obter mais detalhes.

forma de livro novo

Salvando o livro

Abra o book-list.component.htmle adicione um abp-buttonpara salvar o formulário.

<ng-template #abpFooter>
  <button type="button" class="btn btn-secondary" #abpClose>
    Cancel
  </button>
  <button class="btn btn-primary" (click)="save()">
    <i class="fa fa-check mr-1"></i>
    Save
  </button>
</ng-template>

Isso adiciona um botão Salvar à área inferior do modal:

livraria-novo-livro-formulário-v2

Em seguida, defina um savemétodo no BookListComponent:

save() {
  if (this.form.invalid) {
    return;
  }

  this.store.dispatch(new CreateUpdateBook(this.form.value)).subscribe(() => {
    this.isModalOpen = false;
    this.form.reset();
  });
}

Atualizando um livro existente

BooksService

Abra o books.service.tse adicione os métodos getByIde update.

getById(id: string): Observable<Books.Book> {
  return this.restService.request<void, Books.Book>({
    method: 'GET',
    url: `/api/app/book/${id}`
  });
}

update(updateBookInput: Books.CreateUpdateBookInput, id: string): Observable<Books.Book> {
  return this.restService.request<Books.CreateUpdateBookInput, Books.Book>({
    method: 'PUT',
    url: `/api/app/book/${id}`,
    body: updateBookInput
  });
}

Ação CreateUpdateBook

Abra o parâmetro books.actins.tse adicione idà CreateUpdateBookação:

export class CreateUpdateBook {
  static readonly type = '[Books] Create Update Book';
  constructor(public payload: Books.CreateUpdateBookInput, public id?: string) {}
}

Abra books.state.tse modifique o savemétodo conforme mostrado abaixo:

@Action(CreateUpdateBook)
save(ctx: StateContext<Books.State>, action: CreateUpdateBook) {
  let request;

  if (action.id) {
    request = this.booksService.update(action.payload, action.id);
  } else {
    request = this.booksService.create(action.payload);
  }

  return request.pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}

BookListComponent

Injectar BooksServicedependência, adicionando-o ao book-list.component.tsconstrutor e adicione uma variável chamada selectedBook.

import { BooksService } from '../shared/books.service';
//...
selectedBook = {} as Books.Book;

constructor(
  //...
  private booksService: BooksService
)

booksServiceé usado para obter o livro de edição para preparar o formulário. Modifique o buildFormmétodo para reutilizar o mesmo formulário ao editar um livro.

buildForm() {
  this.form = this.fb.group({
    name: [this.selectedBook.name || '', Validators.required],
    type: this.selectedBook.type || null,
    publishDate: this.selectedBook.publishDate ? new Date(this.selectedBook.publishDate) : null,
    price: this.selectedBook.price || null,
  });
}

Adicione o editBookmétodo como mostrado abaixo:

  editBook(id: string) {
    this.booksService.getById(id).subscribe(book => {
      this.selectedBook = book;
      this.buildForm();
      this.isModalOpen = true;
    });
  }

Adicionado editBookmétodo para obter o livro de edição, criar o formulário e mostrar o modal.

Agora, adicione a selectedBookdefinição ao createBookmétodo para reutilizar o mesmo formulário ao criar um novo livro:

  createBook() {
    this.selectedBook = {} as Books.Book;
    //...
  }

Modifique o savemétodo para passar o ID do livro selecionado, como mostrado abaixo:

save() {
  if (this.form.invalid) {
    return;
  }

  this.store.dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id))
    .subscribe(() => {
      this.isModalOpen = false;
      this.form.reset();
    });
}

Adicione o menu suspenso "Ações" à tabela

Abra o book-list.component.html e adicione modifique o p-table como mostrado abaixo:

<p-table [value]="books$ | async" [loading]="loading" [paginator]="true" [rows]="10">
  <ng-template pTemplate="header">
    <tr>
      <th>Actions</th>
      <th>Book name</th>
      <th>Book type</th>
      <th>Publish date</th>
      <th>Price</th>
    </tr>
  </ng-template>
  <ng-template pTemplate="body" let-data>
    <tr>
      <td>
        <div ngbDropdown class="d-inline-block">
          <button
            class="btn btn-primary btn-sm dropdown-toggle"
            data-toggle="dropdown"
            aria-haspopup="true"
            ngbDropdownToggle
          >
            <i class="fa fa-cog mr-1"></i>Actions
          </button>
          <div ngbDropdownMenu>
            <button ngbDropdownItem (click)="editBook(data.id)">Edit</button>
          </div>
        </div>
      </td>
      <td>{{ data.name }}</td>
      <td>{{ booksType[data.type] }}</td>
      <td>{{ data.publishDate | date }}</td>
      <td>{{ data.price }}</td>
    </tr>
  </ng-template>
</p-table>
  • Adicionado um thpara a coluna "Ações".
  • Adicionado buttoncom ngbDropdownTogglepara abrir ações quando clicamos no botão.

Nós costumávamos usar o NgbDropdown no menu suspenso de ações.

A interface do usuário final é semelhante a:

botões de ações

Atualize o cabeçalho modal para alterar o título com base na operação atual:

<ng-template #abpHeader>
  <h3>{{ selectedBook.id ? 'Edit' : 'New Book' }}</h3>
</ng-template>

botões de ações

Exclusão de um livro existente

BooksService

Abra books.service.tse inclua um deletemétodo para excluir um livro com o id, executando uma solicitação HTTP no nó de extremidade relacionado:

delete(id: string): Observable<void> {
  return this.restService.request<void, void>({
    method: 'DELETE',
    url: `/api/app/book/${id}`
  });
}

Ação DeleteBook

Adicione uma ação chamada DeleteBookpara books.actions.ts:

export class DeleteBook {
  static readonly type = '[Books] Delete';
  constructor(public id: string) {}
}

Abra o books.state.tse adicione o deletemétodo que ouvirá a DeleteBookação para excluir um livro:

import { ... , DeleteBook } from '../actions/books.actions';
//...
@Action(DeleteBook)
delete(ctx: StateContext<Books.State>, action: DeleteBook) {
  return this.booksService.delete(action.id).pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}
  • Adicionado DeleteBookà lista de importação.
  • Usa bookServicepara excluir o livro.

#### Adicionar um botão Excluir

Abra book-list.component.htmle modifique ngbDropdownMenupara adicionar o botão excluir, como mostrado abaixo:

<div ngbDropdownMenu>
  ...
  <button ngbDropdownItem (click)="delete(data.id, data.name)">
    Delete
  </button>
</div>

A interface do usuário suspensa de ações finais é semelhante a abaixo:

livraria-final-ações-suspensa

#### Caixa de diálogo Excluir confirmação

Abra book-list.component.tse injete o ConfirmationService.

import { ConfirmationService } from '@abp/ng.theme.shared';
//...
constructor(
	//...
  private confirmationService: ConfirmationService
)

ConfirmationService é um serviço simples fornecido pela estrutura ABP que usa internamente o PrimeNG.

Adicione um método de exclusão ao BookListComponent:

import { ... , DeleteBook } from '../../store/actions';
import { ... , Toaster } from '@abp/ng.theme.shared';
//...
delete(id: string, name: string) {
  this.confirmationService
    .error(`${name} will be deleted. Do you confirm that?`, 'Are you sure?')
    .subscribe(status => {
      if (status === Toaster.Status.confirm) {
        this.store.dispatch(new DeleteBook(id));
      }
    });
}

O deletemétodo mostra um pop-up de confirmação e assina a resposta do usuário. DeleteBookação despachada somente se o usuário clicar no Yesbotão O pop-up de confirmação é exibido abaixo:

livraria-confirmação-pop-up

Próxima parte

Veja a próxima parte deste tutorial.

Esta página foi útil?
Por favor, faça uma seleção.
Obrigado pelo seu valioso feedback!

Observe que, embora não possamos responder aos comentários, nossa equipe usará seus comentários para melhorar a experiência.

Neste documento
Mastering ABP Framework Book
Dominando a estrutura ABP

Este livro o ajudará a obter uma compreensão completa da estrutura e das técnicas modernas de desenvolvimento de aplicativos da web.