Browse Source

docs: create angular part 2 tutorial

pull/1616/head
mehmet-erim 7 years ago
parent
commit
e5105873cb
  1. 78
      docs/en/Tutorials/Angular/Part-I.md
  2. 572
      docs/en/Tutorials/Angular/Part-II.md
  3. BIN
      docs/en/Tutorials/Angular/images/bookstore-actions-buttons.png
  4. 0
      docs/en/Tutorials/Angular/images/bookstore-angular-file-tree.png
  5. BIN
      docs/en/Tutorials/Angular/images/bookstore-book-list.png
  6. 0
      docs/en/Tutorials/Angular/images/bookstore-creating-book-list-terminal.png
  7. 0
      docs/en/Tutorials/Angular/images/bookstore-creating-books-module-terminal.png
  8. BIN
      docs/en/Tutorials/Angular/images/bookstore-edit-modal.png
  9. BIN
      docs/en/Tutorials/Angular/images/bookstore-empty-new-book-modal.png
  10. BIN
      docs/en/Tutorials/Angular/images/bookstore-initial-book-list-page.png
  11. BIN
      docs/en/Tutorials/Angular/images/bookstore-initial-books-page-with-layout.png
  12. BIN
      docs/en/Tutorials/Angular/images/bookstore-initial-books-page.png
  13. BIN
      docs/en/Tutorials/Angular/images/bookstore-new-book-form-v2.png
  14. BIN
      docs/en/Tutorials/Angular/images/bookstore-new-book-form.png
  15. 0
      docs/en/Tutorials/Angular/images/bookstore-service-terminal-output.png
  16. BIN
      docs/en/Tutorials/Angular/images/initial-book-list-page.png
  17. BIN
      docs/en/Tutorials/Angular/images/initial-books-page-with-layout.png
  18. BIN
      docs/en/Tutorials/Angular/images/initial-books-page.png

78
docs/en/Tutorials/Angular/Part-I.md

@ -4,12 +4,12 @@
In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Angular** will be used as the UI framework and **MongoDB** will be used as the database provider.
This is the first part of the ASP.NET Core MVC tutorial series. See all parts:
This is the first part of the Angular tutorial series. See all parts:
- **Part I: Create the project and a book list page (this tutorial)**
- [Part II: Create, Update and Delete books](Part-II.md)
You can access to the **source code** of the application from the GitHub repository (TOD: link).
You can access to the **source code** of the application from the [GitHub repository](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb).
### Creating the Project
@ -81,19 +81,19 @@ namespace Acme.BookStore
Add a `IMongoCollection` property to the `BookStoreMongoDbContext` inside the `Acme.BookStore.MongoDB` project:
````csharp
```csharp
public class BookStoreMongoDbContext : AbpMongoDbContext
{
public IMongoCollection<Book> Books => Collection<Book>();
...
}
````
```
#### Add Seed (Sample) Data
This section is optional, but it would be good to have an initial data in the database in the first run. ABP provides a [data seed system](../../Data-Seeding.md). Create a class deriving from the `IDataSeedContributor` in the `.Domain` project:
````csharp
```csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
@ -142,7 +142,7 @@ namespace Acme.BookStore
}
}
````
```
`BookStoreDataSeederContributor` simply inserts two books into database if there is no book added before. ABP automatically discovers and executes this class when you seed the database by running the `Acme.BookStore.DbMigrator` project.
@ -176,7 +176,7 @@ namespace Acme.BookStore
}
```
- **DTO** classes are used to **transfer data** between the *presentation layer* and the *application layer*. See the [Data Transfer Objects document](../../Data-Transfer-Objects.md) for more details.
- **DTO** classes are used to **transfer data** between the _presentation layer_ and the _application layer_. See the [Data Transfer Objects document](../../Data-Transfer-Objects.md) for more details.
- `BookDto` is used to transfer book data to the presentation layer in order to show the book information on the UI.
- `BookDto` is derived from the `AuditedEntityDto<Guid>` which has audit properties just like the `Book` class defined above.
@ -245,7 +245,7 @@ using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
public interface IBookAppService :
public interface IBookAppService :
ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
@ -274,12 +274,12 @@ using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookAppService :
public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
@ -310,10 +310,10 @@ Swagger has a nice UI to test APIs. You can try to execute the `[GET] /api/app/b
In this tutorial;
* [Angular CLI](https://angular.io/cli) will be used to create modules, components and services
* [NGXS](https://ngxs.gitbook.io/ngxs/) will be used as the state management library
* [Ng Bootstrap](https://ng-bootstrap.github.io/#/home) will be used as the UI component library.
* [Visual Studio Code](https://code.visualstudio.com/) will be used as the code editor (you can use your favorite editor).
- [Angular CLI](https://angular.io/cli) will be used to create modules, components and services
- [NGXS](https://ngxs.gitbook.io/ngxs/) will be used as the state management library
- [Ng Bootstrap](https://ng-bootstrap.github.io/#/home) will be used as the UI component library.
- [Visual Studio Code](https://code.visualstudio.com/) will be used as the code editor (you can use your favorite editor).
#### Install NPM Packages
@ -331,11 +331,11 @@ Run the following command line to create a new module, named `BooksModule`:
yarn ng generate module books --route books --module app.module
```
![creating-books-module.terminal](images/creating-books-module-terminal.png)
![creating-books-module.terminal](images/bookstore-creating-books-module-terminal.png)
Run `yarn start`, wait Angular to run the application and open `http://localhost:4200/books` on a browser:
![initial-books-page](images/initial-books-page.png)
![initial-books-page](images/bookstore-initial-books-page.png)
#### Routing
@ -344,7 +344,7 @@ Open the `app-routing.module.ts` and replace `books` as shown below:
```typescript
import { LayoutApplicationComponent } from '@abp/ng.theme.basic';-
//...
//...
{
path: 'books',
component: LayoutApplicationComponent,
@ -359,11 +359,9 @@ import { LayoutApplicationComponent } from '@abp/ng.theme.basic';-
`LayoutApplicationComponent` configuration sets the application layout to the new page. If you would like to see your route on the navigation bar (main menu) you must also add the `data` object with `name` property in your route.
![initial-books-page](images/bookstore-initial-books-page-with-layout.png)
![initial-books-page](images/initial-books-page-with-layout.png)
#### Book List Component
#### Book List Component
First, replace the `books.component.html` to the following line to place the router-outlet:
@ -373,12 +371,11 @@ First, replace the `books.component.html` to the following line to place the rou
Then run the command below on the terminal in the root folder to generate a new component, named book-list:
````bash
```bash
yarn ng generate component books/book-list
````
```
![creating-books-list-terminal](images/creating-book-list-terminal.png)
![creating-books-list-terminal](images/bookstore-creating-book-list-terminal.png)
Import the `SharedModule` to the `BooksModule` to reuse some components and services defined in:
@ -389,7 +386,7 @@ import { SharedModule } from '../shared/shared.module';
//...
imports: [
//...
SharedModule
SharedModule,
],
})
export class BooksModule {}
@ -415,15 +412,15 @@ const routes: Routes = [
export class BooksRoutingModule {}
```
![initial-book-list-page](images/initial-book-list-page.png)
![initial-book-list-page](images/bookstore-initial-book-list-page.png)
#### Create BooksState
Run the following command in the terminal to create a new state, named `BooksState`:
````shell
```shell
yarn ng generate ngxs-schematic:state books
````
```
This command creates several new files and edits `app.modules.ts` to import the `NgxsModule` with the new state:
@ -470,7 +467,7 @@ export namespace Books {
id: string;
}
export enum Type {
export enum BookType {
Undefined,
Adventure,
Biography,
@ -484,7 +481,7 @@ export namespace Books {
}
```
Added `Book` interface that represents a book object and `Type` enum represents a book category.
Added `Book` interface that represents a book object and `BookType` enum represents a book category.
#### BooksService
@ -494,7 +491,7 @@ Now, create a new service, named `BooksService` to perform HTTP calls to the ser
yarn ng generate service books/shared/books
```
![service-terminal-output](images/service-terminal-output.png)
![service-terminal-output](images/bookstore-service-terminal-output.png)
Modify `book.service.ts` as shown below:
@ -521,7 +518,7 @@ export class BooksService {
}
```
Added a `get` method to get the list of books by performing an HTTP request to the related endpoint.
Added the `get` method to get the list of books by performing an HTTP request to the related endpoint.
Replace `books.actions.ts` content as shown below:
@ -566,9 +563,10 @@ export class BooksState {
}
}
```
* Added a `GetBooks` action that uses the `BookService` defined above to get the books and patch the state.
>NGXS requires to return the observable without subscribing it, as done in this sample (in the get function).
Added the `GetBooks` action that uses the `BookService` defined above to get the books and patch the state.
> NGXS requires to return the observable without subscribing it, as done in this sample (in the get function).
#### BookListComponent
@ -591,7 +589,7 @@ export class BookListComponent implements OnInit {
@Select(BooksState.getBooks)
books$: Observable<Books.Book[]>;
booksType = Books.Type;
booksType = Books.BookType;
loading = false;
@ -607,7 +605,7 @@ export class BookListComponent implements OnInit {
}
```
>See the [Dispatching Actions](https://ngxs.gitbook.io/ngxs/concepts/store#dispatching-actions) and [Select](https://ngxs.gitbook.io/ngxs/concepts/select) on the NGXS documentation for more information on these NGXS features.
> See the [Dispatching Actions](https://ngxs.gitbook.io/ngxs/concepts/store#dispatching-actions) and [Select](https://ngxs.gitbook.io/ngxs/concepts/select) on the NGXS documentation for more information on these NGXS features.
Replace `book-list.component.html` content as shown below:
@ -644,7 +642,8 @@ Replace `book-list.component.html` content as shown below:
</div>
</div>
```
> We've used [PrimeNG table](https://www.primefaces.org/primeng/#/table) used in this component.
> We've used [PrimeNG table](https://www.primefaces.org/primeng/#/table) in this component.
The resulting books page is shown below:
@ -652,11 +651,10 @@ The resulting books page is shown below:
And this is the folder & file structure by the end of this tutorial:
<img src="images/angular-file-tree.png" height="75%">
<img src="images/bookstore-angular-file-tree.png" height="75%">
> This tutorial follows the [Angular Style Guide](https://angular.io/guide/styleguide#file-tree).
### Next Part
See the [next part](Part-II.md) of this tutorial.

572
docs/en/Tutorials/Angular/Part-II.md

@ -0,0 +1,572 @@
## Angular Tutorial - Part I
### About this Tutorial
This is the second part of the Angular tutorial series. See all parts:
- [Part I: Create the project and a book list page](Part-I.md)
- **Part II: Create, Update and Delete books (this tutorial)**
You can access to the **source code** of the application from the [GitHub repository](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb).
### Type Definition
Create an interface, named `CreateUpdateBookInput` in the `books.ts` as shown below:
```typescript
export namespace Books {
//...
export interface CreateUpdateBookInput {
name: string;
type: BookType;
publishDate: string;
price: number;
}
}
```
`CreateUpdateBookInput` interface matches the `CreateUpdateBookDto` in the backend.
### Add HTTP POST Method
Open the `books.service.ts` and add a new method, named create to perform an HTTP POST request to the server:
```typescript
create(body: Books.CreateUpdateBookInput): Observable<Books.Book> {
const request: Rest.Request<Books.CreateUpdateBookInput> = {
method: 'POST',
url: '/api/app/book',
body,
};
return this.rest.request<Books.CreateUpdateBookInput, Books.Book>(request);
}
```
### State Definitions
Add the `CreateUpdateBook` action to `books.actions.ts` as shown below:
```typescript
import { Books } from '../models';
export class CreateUpdateBook {
static readonly type = '[Books] Create Update Book';
constructor(public payload: Books.CreateUpdateBookInput) {}
}
```
<!-- Added an `id` parameter to differentiate between create or update actions -->
Open `books.state.ts` and define the `save` method that will listen to a `CreateUpdateBook` action to create a book:
```typescript
@Action(CreateUpdateBook)
save({ dispatch }: StateContext<Books.State>, { payload }: CreateUpdateBook) {
return this.booksService.create(payload).pipe(switchMap(() => dispatch(new GetBooks())));
}
```
When the `SaveBook` action dispached, the save method runs and then call `create` method of the `BooksService`. After, `BooksService` sends a POST request to the backend. When finished this HTTP call, `BooksState` dispatches `GetBooks` action for getting up-to-date books.
### Add a Modal to BookListComponent
Open the `book-list.component.html` and add the `abp-modal` to show/hide the book form.
```html
<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>
```
Then, add the `New book` button to show the modal.
```html
<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)="onAdd()">
<i class="fa fa-plus mr-1"></i> <span>New book</span>
</button>
</div>
</div>
```
Open `book-list.component.ts` and add the `isModalOpen` variable and `onAdd` method to show/hide the modal.
```typescript
//...
loading = false;
isModalOpen = false;
```
```typescript
// ...
ngOnInit() {
// ...
}
onAdd() {
this.isModalOpen = true;
}
```
![empty-modal](images/bookstore-empty-new-book-modal.png)
### Create a Reactive Form
[Reactive forms](https://angular.io/guide/reactive-forms) provide a model-driven approach to handling form inputs whose values change over time.
Add the `form` variable and import to the FormGroup from `@angular/core`
```typescript
//...
isModalOpen = false;
form: FormGroup;
```
Inject `FormBuilder` dependency by adding it to the `BookListComponent` constructor.
```typescript
import { FormBuilder } from '@angular/forms';
//...
constructor(
//...
private fb: FormBuilder
) {}
```
> The [FormBuilder](https://angular.io/api/forms/FormBuilder) service provides convenient methods for generating controls. It reduces the amount of boilerplate needed to build complex forms.
Add the `buildForm` method for create book form.
```typescript
buildForm() {
this.form = this.fb.group({
name: ['', Validators.required],
type: [null, Validators.required],
publishDate: [null, Validators.required],
price: [null, Validators.required],
});
}
```
- The `group` method of `FormBuilder` creates a `FormGroup`.
- Added `Validators.required` static method that validation of form element.
Modify the `onAdd` method as shown below:
```typescript
onAdd() {
this.buildForm();
this.isModalOpen = true;
}
```
### Create the DOM Elements of the Form
Open `book-list.component.html` and add the form in the body template of the modal.
```html
<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 bookTypeArr"> {{ 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>
```
TODO: Add a short description.
> We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component.
Open the `book-list.component.ts` and then add the `bookTypes`.
```typescript
//...
form: FormGroup;
bookTypeArr = Object.keys(Books.BookType).filter(bookType => typeof this.booksType[bookType] === 'number');
```
The `bookTypes` variable added to generate array from `BookType` enum. The `bookTypes` equals like this:
```js
['Adventure', 'Biography', 'Dystopia', 'Fantastic' ...]
```
### Add the Datepicker Requirements
Import `NgbDatepickerModule` to the `books.module.ts`.
```typescript
import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
imports: [
// ...
NgbDatepickerModule,
],
})
export class BooksModule {}
```
Open the `book-list.component.html` and then add `providers` as shown below:
```typescript
@Component({
// ...
styleUrls: ['./book-list.component.scss'],
providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }],
})
export class BookListComponent implements OnInit {
// ...
```
> The `NgbDateAdapter` convert Datepicker value type to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
![new-book-form](images/bookstore-new-book-form.png)
### Create a New Book
Add the `save` method to `BookListComponent`
```typescript
save() {
if (this.form.invalid) {
return;
}
this.store.dispatch(new CreateUpdateBook(this.form.value)).subscribe(() => {
this.isModalOpen = false;
this.form.reset();
});
}
```
TODO: description ??
Then, open the `book-list.component.html` and add the `abp-button` for the run `save` method.
```html
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
Cancel
</button>
<abp-button iconClass="fa fa-check" (click)="save()">Save</abp-button>
</ng-template>
```
Now, You can add a new book.
![bookstore-new-book-form-v2](images/bookstore-new-book-form-v2.png)
### Add HTTP GET and PUT Methods
TODO: Description
Open the `book.service.ts` and then add the `getById` and `update` methods.
```typescript
getById(id: string): Observable<Books.Book> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/app/book/${id}`,
};
return this.rest.request<null, Books.Book>(request);
}
update(body: Books.CreateUpdateBookInput, id: string): Observable<Books.Book> {
const request: Rest.Request<Books.CreateUpdateBookInput> = {
method: 'PUT',
url: `/api/app/book/${id}`,
body,
};
return this.rest.request<Books.CreateUpdateBookInput, Books.Book>(request);
}
```
- Added the `getById` method to get the editing book by performing an HTTP request to the related endpoint.
- Added the `update` method to update a book with the `id` by performing an HTTP request to the related endpoint.
### Update a Book
Inject `BooksService` dependency by adding it to the `book-list.component.ts` constructor and add variable named `selectedBook`.
```typescript
selectedBook = {} as Books.Book;
constructor(
//...
private booksService: BooksService
)
```
- Added `booksService` to get the detail of selected book by `id` before creating the form
- Added `selectedBook` variable to reuse detail of selected book.
Modify the `buildForm` method to reuse the same form while editing a book.
```typescript
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,
});
}
```
Add the `onEdit` method as shown below:
```typescript
onEdit(id: string) {
this.booksService.getById(id).subscribe(book => {
this.selectedBook = book;
this.buildForm();
this.isModalOpen = true;
});
}
```
- Added `onEdit` method to get selected book detail, build form and then show the modal.
Add the `selectedBook` definition to `onAdd` method for reuse same form while adding a new book.
```typescript
onAdd() {
this.selectedBook = {} as Books.Item;
//...
}
```
Add the `this.selectedBook.id` to `CreateUpdateBook` action in `save` method.
```typescript
save() {
//...
this.store.dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id))
//...
}
```
Added the `this.selectedBook.id` property to reuse the `CreateUpdateBook` action.
#### Add "Actions" Dropdown to the Table
Open the `book-list.component.html` and add modify the `p-table` as shown below:
```html
<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)="onEdit(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>
>
```
Actions button added to each row of the table.
> We've used to [NgbDropdown](https://ng-bootstrap.github.io/#/components/dropdown/examples) for the dropdown menu of actions.
The Actions buttons looks like this:
![actions-buttons](images/bookstore-actions-buttons.png)
Update the modal header for reuse the same modal.
```html
<ng-template #abpHeader>
<h3>{{ (selectedBook.id ? 'Edit' : 'New Book') }}</h3>
</ng-template>
```
![actions-buttons](images/bookstore-edit-modal.png)
TODO: header ??
Open the `books.actins.ts` and add `id` parameter.
```typescript
export class CreateUpdateBook {
static readonly type = '[Books] Create Update Book';
constructor(public payload: Books.CreateUpdateBookInput, public id?: string) {}
}
```
Added `id` parameter to reuse the `BooksSave` while updating and creating a book.
Open `books.state.ts` and then modify the `save` method as show below:
```typescript
@Action(CreateUpdateBook)
save({ dispatch }: StateContext<Books.State>, { payload, id }: CreateUpdateBook) {
let request;
if (id) {
request = this.booksService.update(payload, id);
} else {
request = this.booksService.create(payload);
}
return request.pipe(switchMap(() => dispatch(new GetBooks())));
}
```
TODO: description & screenshot ??
### Delete a Book
Open `books.service.ts` and the the `delete` method for delete a book with the `id` by performing an HTTP request to the related endpoint.
```typescript
delete(id: string): Observable<null> {
const request: Rest.Request<null> = {
method: 'DELETE',
url: `/api/app/book/${id}`,
};
return this.rest.request<null, null>(request);
}
```
### State Definitions
Add an action named `BooksDelete` to `books.actions.ts`
```typescript
export class DeleteBook {
static readonly type = '[Books] Delete';
constructor(public id: string) {}
}
```
Then, open the `books.state.ts` and add the `delete` method that will listen to a `CreateUpdateBook` action to create a book
```typescript
@Action(DeleteBook)
delete({ dispatch }: StateContext<Books.State>, { id }: DeleteBook) {
return this.booksService.delete(id).pipe(switchMap(() => dispatch(new GetBooks())));
}
```
TODO: description??
### Add a Delete Button
Open `book-list.component.html` and modify the `ngbDropdownMenu` for add the delete button as shown below:
```html
<div ngbDropdownMenu>
...
<button ngbDropdownItem (click)="delete(data.id, data.name)">
Delete
</button>
</div>
```
### Open Confirmation Popup
Open `book-list.component.ts` and inject the `ConfirmationService` for show confirmation popup.
```typescript
import { ConfirmationService } from '@abp/ng.theme.shared';
//...
constructor(
//...
private confirmationService: ConfirmationService
)
```
Add the following method to `BookListComponent`.
```typescript
import { ConfirmationService, 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));
}
});
}
```
The `delete` method shows confirmation popup and listens to them. When close the popup, the subscribe block runs. If confirmed this popup, it will dispatch the `DeleteBook` action.

BIN
docs/en/Tutorials/Angular/images/bookstore-actions-buttons.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

0
docs/en/Tutorials/Angular/images/angular-file-tree.png → docs/en/Tutorials/Angular/images/bookstore-angular-file-tree.png

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-book-list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 88 KiB

0
docs/en/Tutorials/Angular/images/creating-book-list-terminal.png → docs/en/Tutorials/Angular/images/bookstore-creating-book-list-terminal.png

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

0
docs/en/Tutorials/Angular/images/creating-books-module-terminal.png → docs/en/Tutorials/Angular/images/bookstore-creating-books-module-terminal.png

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-edit-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-empty-new-book-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-initial-book-list-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-initial-books-page-with-layout.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-initial-books-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-new-book-form-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-new-book-form.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

0
docs/en/Tutorials/Angular/images/service-terminal-output.png → docs/en/Tutorials/Angular/images/bookstore-service-terminal-output.png

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

BIN
docs/en/Tutorials/Angular/images/initial-book-list-page.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

BIN
docs/en/Tutorials/Angular/images/initial-books-page-with-layout.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

BIN
docs/en/Tutorials/Angular/images/initial-books-page.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Loading…
Cancel
Save