Versão
Idioma

Tutorial Angular - Parte I

Sobre este tutorial

Nesta série de tutoriais, você criará um aplicativo usado para gerenciar uma lista de livros e seus autores. Angular será usado como estrutura da interface do usuário e MongoDB será usado como provedor de banco de dados.

Esta é a primeira 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 o projeto

Crie um novo projeto nomeado Acme.BookStoreselecionando Angular como a estrutura da interface do usuário e MongoDB como o provedor de banco de dados, crie o banco de dados e execute o aplicativo seguindo o documento Introdução .

Estrutura da solução (back-end)

É assim que a estrutura da solução em camadas cuida da criação:

solução de back-end da livraria

Você pode ver o documento do modelo de aplicativo para entender a estrutura da solução em detalhes. No entanto, você entenderá o básico com este tutorial.

Criar a entidade do livro

A camada de domínio no modelo de inicialização é separada em dois projetos:

  • Acme.BookStore.Domaincontém suas entidades , serviços de domínio e outros objetos principais de domínio.
  • Acme.BookStore.Domain.Shared contém constantes, enumerações ou outros objetos relacionados ao domínio que podem ser compartilhados com os clientes.

Defina entidades na camada de domínio ( Acme.BookStore.Domainprojeto) da solução. A entidade principal do aplicativo é a Book. Crie uma classe, chamada Book, no Acme.BookStore.Domainprojeto, como mostrado abaixo:

using System;
using Volo.Abp.Domain.Entities.Auditing;

namespace Acme.BookStore
{
    public class Book : AuditedAggregateRoot<Guid>
    {
        public string Name { get; set; }

        public BookType Type { get; set; }

        public DateTime PublishDate { get; set; }

        public float Price { get; set; }
    }
}
  • O ABP possui duas classes base fundamentais para entidades: AggregateRoote Entity. A raiz agregada é um dos conceitos de DDD (Domain Driven Design) . Consulte o documento da entidade para obter detalhes e melhores práticas.
  • Bookentidade herda AuditedAggregateRootque adiciona algumas propriedades de auditoria ( CreationTime, CreatorId, LastModificationTime... etc.) no topo da AggregateRootclasse.
  • Guidé o tipo de chave primária da Bookentidade.

BookType Enum

Defina a BookTypeenumeração no Acme.BookStore.Domain.Sharedprojeto:

namespace Acme.BookStore
{
    public enum BookType
    {
        Undefined,
        Adventure,
        Biography,
        Dystopia,
        Fantastic,
        Horror,
        Science,
        ScienceFiction,
        Poetry
    }
}

Adicionar entidade de livro ao seu DbContext

Adicione uma IMongoCollectionpropriedade ao BookStoreMongoDbContextinterior do Acme.BookStore.MongoDBprojeto:

public class BookStoreMongoDbContext : AbpMongoDbContext
{
    public IMongoCollection<Book> Books => Collection<Book>();
    ...
}

Adicionar dados de semente (amostra)

Esta seção é opcional, mas seria bom ter um dado inicial no banco de dados na primeira execução. O ABP fornece um sistema de semente de dados . Crie uma classe derivada de IDataSeedContributorno .Domainprojeto:

using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;

namespace Acme.BookStore
{
    public class BookStoreDataSeederContributor
        : IDataSeedContributor, ITransientDependency
    {
        private readonly IRepository<Book, Guid> _bookRepository;

        public BookStoreDataSeederContributor(IRepository<Book, Guid> bookRepository)
        {
            _bookRepository = bookRepository;
        }

        public async Task SeedAsync(DataSeedContext context)
        {
            if (await _bookRepository.GetCountAsync() > 0)
            {
                return;
            }

            await _bookRepository.InsertAsync(
                new Book
                {
                    Name = "1984",
                    Type = BookType.Dystopia,
                    PublishDate = new DateTime(1949, 6, 8),
                    Price = 19.84f
                }
            );

            await _bookRepository.InsertAsync(
                new Book
                {
                    Name = "The Hitchhiker's Guide to the Galaxy",
                    Type = BookType.ScienceFiction,
                    PublishDate = new DateTime(1995, 9, 27),
                    Price = 42.0f
                }
            );
        }
    }
}

BookStoreDataSeederContributorsimplesmente insere dois livros no banco de dados se não houver nenhum livro adicionado antes. O ABP descobre e executa automaticamente essa classe quando você propaga o banco de dados executando o Acme.BookStore.DbMigratorprojeto.

Crie o serviço de aplicativo

O próximo passo é criar um serviço de aplicativo para gerenciar (criar, listar, atualizar, excluir ...) os livros. A camada de aplicativo no modelo de inicialização é separada em dois projetos:

  • Acme.BookStore.Application.Contracts contém principalmente seus DTOs e interfaces de serviço de aplicativo.
  • Acme.BookStore.Application contém as implementações dos seus serviços de aplicativo.

BookDto

Crie uma classe DTO denominada BookDtono Acme.BookStore.Application.Contractsprojeto:

using System;
using Volo.Abp.Application.Dtos;

namespace Acme.BookStore
{
    public class BookDto : AuditedEntityDto<Guid>
    {
        public string Name { get; set; }

        public BookType Type { get; set; }

        public DateTime PublishDate { get; set; }

        public float Price { get; set; }
    }
}
  • As classes DTO são usadas para transferir dados entre a camada de apresentação e a camada de aplicativo . Consulte o documento Objetos de transferência de dados para obter mais detalhes.
  • BookDto é usado para transferir dados do livro para a camada de apresentação para mostrar as informações do livro na interface do usuário.
  • BookDtoé derivado do AuditedEntityDto<Guid>que possui propriedades de auditoria exatamente como a Bookclasse definida acima.

Será necessário converter Bookentidades em BookDtoobjetos enquanto retorna os livros para a camada de apresentação. A biblioteca do AutoMapper pode automatizar essa conversão quando você define o mapeamento adequado. O modelo de inicialização é fornecido com o AutoMapper configurado, para que você possa definir o mapeamento na BookStoreApplicationAutoMapperProfileclasse no Acme.BookStore.Applicationprojeto:

using AutoMapper;

namespace Acme.BookStore
{
    public class BookStoreApplicationAutoMapperProfile : Profile
    {
        public BookStoreApplicationAutoMapperProfile()
        {
            CreateMap<Book, BookDto>();
        }
    }
}

CreateUpdateBookDto

Crie uma classe DTO denominada CreateUpdateBookDtono Acme.BookStore.Application.Contractsprojeto:

using System;
using System.ComponentModel.DataAnnotations;

namespace Acme.BookStore
{
    public class CreateUpdateBookDto
    {
        [Required]
        [StringLength(128)]
        public string Name { get; set; }

        [Required]
        public BookType Type { get; set; } = BookType.Undefined;

        [Required]
        public DateTime PublishDate { get; set; }

        [Required]
        public float Price { get; set; }
    }
}
  • Essa classe DTO é usada para obter informações do livro a partir da interface do usuário ao criar ou atualizar um livro.
  • Ele define atributos de anotação de dados (como [Required]) para definir validações para as propriedades. Os DTOs são validados automaticamente pela estrutura ABP.

Em seguida, adicione um mapeamento BookStoreApplicationAutoMapperProfiledo CreateUpdateBookDtoobjeto à Bookentidade:

CreateMap<CreateUpdateBookDto, Book>();

IBookAppService

Defina uma interface nomeada IBookAppServiceno Acme.BookStore.Application.Contractsprojeto:

using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;

namespace Acme.BookStore
{
    public interface IBookAppService :
        ICrudAppService< //Defines CRUD methods
            BookDto, //Used to show books
            Guid, //Primary key of the book entity
            PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
            CreateUpdateBookDto, //Used to create a new book
            CreateUpdateBookDto> //Used to update a book
    {

    }
}
  • A definição de interfaces para serviços de aplicativos não é requerida pela estrutura. No entanto, é sugerido como uma prática recomendada.
  • ICrudAppServicedefine comuns CRUD métodos: GetAsync, GetListAsync, CreateAsync, UpdateAsynce DeleteAsync. Não é necessário estendê-lo. Em vez disso, você pode herdar da IApplicationServiceinterface vazia e definir seus próprios métodos manualmente.
  • Existem algumas variações de ICrudAppServiceonde você pode usar DTOs separados para cada método.

BookAppService

Implemente IBookAppServicecomo nomeado BookAppServiceno Acme.BookStore.Applicationprojeto:

using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace Acme.BookStore
{
    public class BookAppService :
        CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
                            CreateUpdateBookDto, CreateUpdateBookDto>,
        IBookAppService
    {
        public BookAppService(IRepository<Book, Guid> repository)
            : base(repository)
        {

        }
    }
}
  • BookAppServiceé derivado do CrudAppService<...>qual implementa todos os métodos CRUD definidos acima.
  • BookAppServiceinjeta IRepository<Book, Guid>qual é o repositório padrão da Bookentidade. O ABP cria automaticamente repositórios padrão para cada raiz (ou entidade) agregada. Veja o documento do repositório .
  • BookAppServiceusa IObjectMapperpara converter Bookobjetos em BookDtoobjetos e CreateUpdateBookDtoobjetos em Bookobjetos. O modelo de inicialização usa a biblioteca AutoMapper como o provedor de mapeamento de objetos. Você definiu os mapeamentos antes, para que funcionem conforme o esperado.

Controladores de API automática

Você normalmente cria controladores para expor serviços de aplicativos como pontos de extremidade da API HTTP . Assim, permite que navegadores ou clientes de terceiros os chamem via AJAX. O ABP pode configurar automaticamente seus serviços de aplicativo como controladores de API MVC por convenção.

UI do Swagger

O modelo de inicialização está configurado para executar a interface do usuário do swagger usando a biblioteca Swashbuckle.AspNetCore . Execute o Acme.BookStore.HttpApi.Hostaplicativo e insira https://localhost:XXXX/swagger/(substitua XXXX por sua própria porta) como URL no seu navegador.

Você verá alguns pontos de extremidade de serviço internos, bem como o Bookserviço e seus pontos de extremidade no estilo REST:

livraria-arrogância

O Swagger tem uma ótima interface para testar APIs. Você pode tentar executar a [GET] /api/app/bookAPI para obter uma lista de livros.

Crie a página de livros

Neste tutorial;

  • A CLI angular será usada para criar módulos, componentes e serviços
  • NGXS será usado como a biblioteca de gerenciamento de estado
  • O Bootstrap será usado como a biblioteca de componentes da interface do usuário.
  • O Visual Studio Code será usado como editor de código (você pode usar seu editor favorito).

Instalar pacotes NPM

Abra uma janela do terminal, vá para a angularpasta e execute o yarn comando para instalar os pacotes NPM:

yarn

BooksModule

Execute a seguinte linha de comando para criar um novo módulo, denominado BooksModule:

yarn ng generate module books --route books --module app.module

Creating-Books-Module.terminal

Execute yarn start, aguarde Angular para executar o aplicativo e abra http://localhost:4200/booksem um navegador:

página inicial dos livros

Encaminhamento

Abra app-routing.module.tse substitua booksconforme mostrado abaixo:

import { ApplicationLayoutComponent } from '@abp/ng.theme.basic';-

//...
{
  path: 'books',
  component: ApplicationLayoutComponent,
  loadChildren: () => import('./books/books.module').then(m => m.BooksModule),
  data: {
    routes: {
      name: 'Books',
    } as ABP.Route,
  },
},

ApplicationLayoutComponentconfiguração define o layout do aplicativo para a nova página. Se você deseja ver sua rota na barra de navegação (menu principal), também deve adicionar o dataobjeto com namepropriedade à sua rota.

página inicial dos livros

Componente da lista de livros

Primeiro, substitua pela books.component.htmlseguinte linha para colocar a saída do roteador:

<router-outlet></router-outlet>

Em seguida, execute o comando abaixo no terminal na pasta raiz para gerar um novo componente, chamado book-list:

yarn ng generate component books/book-list

terminal-criando-lista-de-livros

Importe SharedModulepara BooksModulepara reutilizar alguns componentes e serviços definidos em:

import { SharedModule } from '../shared/shared.module';

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

Em seguida, atualize o routesno books-routing.module.tspara adicionar o novo componente book-list:

import { BookListComponent } from './book-list/book-list.component';

const routes: Routes = [
  {
    path: '',
    component: BooksComponent,
    children: [{ path: '', component: BookListComponent }],
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class BooksRoutingModule {}

página inicial da lista de livros

Criar BooksState

Execute o seguinte comando no terminal para criar um novo estado, denominado BooksState:

yarn ng generate ngxs-schematic:state books

Este comando cria vários novos arquivos e edições app.modules.tspara importar o NgxsModulecom o novo estado:

// app.module.ts

import { BooksState } from './store/states/books.state';

@NgModule({
  imports: [
    //...
    NgxsModule.forRoot([BooksState]),
  ],
  //...
})
export class AppModule {}

Obter dados de livros do back-end

Primeiro, crie tipos de dados para mapear os dados que retornam do back-end (você pode verificar a interface do swagger ou a API do back-end para conhecer o formato dos dados).

Modifique o books.tscomo mostrado abaixo:

export namespace Books {
  export interface State {
    books: Response;
  }

  export interface Response {
    items: Book[];
    totalCount: number;
  }

  export interface Book {
    name: string;
    type: BookType;
    publishDate: string;
    price: number;
    lastModificationTime: string;
    lastModifierId: string;
    creationTime: string;
    creatorId: string;
    id: string;
  }

  export enum BookType {
    Undefined,
    Adventure,
    Biography,
    Dystopia,
    Fantastic,
    Horror,
    Science,
    ScienceFiction,
    Poetry,
  }
}

Adicionada Bookinterface que representa um objeto de livro e BookTypeenum representa uma categoria de livro.

BooksService

Agora, crie um novo serviço, nomeado BooksServicepara executar chamadas HTTP para o servidor:

yarn ng generate service books/shared/books

serviço-terminal-saída

Modifique books.service.tscomo mostrado abaixo:

import { Injectable } from '@angular/core';
import { RestService } from '@abp/ng.core';
import { Books } from '../../store/models';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class BooksService {
  constructor(private restService: RestService) {}

  get(): Observable<Books.Response> {
    return this.restService.request<void, Books.Response>({
      method: 'GET',
      url: '/api/app/book'
    });
  }
}

Adicionado o getmétodo para obter a lista de livros executando uma solicitação HTTP no terminal relacionado.

Substitua o books.actions.tsconteúdo conforme mostrado abaixo:

export class GetBooks {
  static readonly type = '[Books] Get';
}

Implementar o BooksState

Abra o books.state.tse altere o arquivo, como mostrado abaixo:

import { State, Action, StateContext, Selector } from '@ngxs/store';
import { GetBooks } from '../actions/books.actions';
import { Books } from '../models/books';
import { BooksService } from '../../books/shared/books.service';
import { tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';

@State<Books.State>({
  name: 'BooksState',
  defaults: { books: {} } as Books.State,
})
@Injectable()
export class BooksState {
  @Selector()
  static getBooks(state: Books.State) {
    return state.books.items || [];
  }

  constructor(private booksService: BooksService) {}

  @Action(GetBooks)
  get(ctx: StateContext<Books.State>) {
    return this.booksService.get().pipe(
      tap(booksResponse => {
        ctx.patchState({
          books: booksResponse,
        });
      }),
    );
  }
}

Adicionada a GetBooksação que usa o BookServicedefinido acima para obter os livros e corrigir o estado.

O NGXS exige retornar o observável sem assiná-lo, conforme feito nesta amostra (na função get).

BookListComponent

Modifique o book-list.component.tscomo mostrado abaixo:

import { Component, OnInit } from '@angular/core';
import { Store, Select } from '@ngxs/store';
import { BooksState } from '../../store/states';
import { Observable } from 'rxjs';
import { Books } from '../../store/models';
import { GetBooks } from '../../store/actions';

@Component({
  selector: 'app-book-list',
  templateUrl: './book-list.component.html',
  styleUrls: ['./book-list.component.scss'],
})
export class BookListComponent implements OnInit {
  @Select(BooksState.getBooks)
  books$: Observable<Books.Book[]>;

  booksType = Books.BookType;

  loading = false;

  constructor(private store: Store) {}

  ngOnInit() {
    this.loading = true;
    this.store.dispatch(new GetBooks()).subscribe(() => {
      this.loading = false;
    });
  }
}

Consulte as ações de despacho e selecione na documentação do NGXS para obter mais informações sobre esses recursos do NGXS.

Substitua o book-list.component.htmlconteúdo conforme mostrado abaixo:

<div id="wrapper" class="card">
  <div class="card-header">
    <div class="row">
      <div class="col col-md-6">
        <h5 class="card-title">
          Books
        </h5>
      </div>
    </div>
  </div>
  <div class="card-body">
    <p-table [value]="books$ | async" [loading]="loading" [paginator]="true" [rows]="10">
      <ng-template pTemplate="header">
        <tr>
          <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>{{ data.name }}</td>
          <td>{{ booksType[data.type] }}</td>
          <td>{{ data.publishDate | date }}</td>
          <td>{{ data.price }}</td>
        </tr>
      </ng-template>
    </p-table>
  </div>
</div>

Usamos a tabela PrimeNG neste componente.

A página de livros resultante é mostrada abaixo:

livraria-lista-de-livros

E esta é a estrutura de pastas e arquivos no final deste tutorial:

img

Este tutorial segue o Guia de estilo angular .

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.