HTTP

The categories are currently hardcoded values; however, they should be loaded from the RESTful API and through HTTP calls. Angular provides its own abstraction for HTTP, in the form of a service that you can use when necessary.

Next, let's use Angular's HttpClient service to load the categories from the backend RESTful API:

  1. Import HttpClientModule into app.module.ts to make the HttpClient service available to the entire app:

import { HttpClientModule } from '@angular/common/http';

@NgModule({

imports: [
HttpClientModule,

],

})
  1. Use the HttpClient service in CategoriesService as follows. If the HTTP address differs in your setup, then make sure to change it accordingly:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { CoreModule } from '../core.module';
import { Category } from '../../../model';

@Injectable({
providedIn: CoreModule,
})
export class CategoriesService {
constructor(private readonly http: HttpClient) {}

loadCategories(): Promise<Category[]> {
return this.http
.get('http://localhost:55564/api/products/categories')
.toPromise()
.then(result => result as Category[]);

}
}

  1. Adjust ProductsPageComponent to handle a promise:

ngOnInit() {
this.categoriesService.loadCategories()
.then(r => this.categories = r);

}

That's it! You can now run the app and see that the categories are loaded by calling the RESTful API using Angular's HttpClient service—just make sure that the backend API is running.

Angular provides several tools to handle asynchrony and event pipelines, such as RxJs Observables and AsyncPipe.
You can read more about these here:
https://angular.io/guide/observables
https://angular.io/guide/observables-in-angular
https://angular.io/guide/rx-library
https://angular.io/api/common/AsyncPipe

Armed with these tools, let's continue by implementing the product listing. The following diagram depicts the data and interaction flow you implement:

Now implement the product listing with the following steps:

  1. Create the product-related model representations in the src/app/model folder.
    Start by creating a file named product-media.ts and write the following content:
export interface ProductMedia {
url: string;
}
  1. Re-export ProductMedia in the src/app/model/index.ts file, by using the following command:
export * from './product-media';

  1. Create a file named product.ts and write in the following content:
import { ProductMedia } from '.';

export interface Product {
title: string;
description: string;
media: ProductMedia[];
}
  1. Re-export Product in the src/app/model/index.ts with the following command:
export * from './product';
  1. Generate a ProductCardComponent component to display a product card with this command:
 ng generate component modules/market/product-card
  1. Edit /src/app/modules/market/product-card/product-card.component.ts to receive the product as input and implement a function to resolve the primary image URL, as follows:
import { Component, Input } from '@angular/core';
import { Product } from '../../../model';

@Component({
selector: 'app-product-card',
templateUrl: './product-card.component.html',
styleUrls: ['./product-card.component.css']
})
export class ProductCardComponent {
@Input() product: Product;

get primaryImageSrc() {
const hasMedia =
this.product && this.product.media && this.product.media.length > 0;
return hasMedia
? this.product.media[0].url
: null;
}
}
  1. Replace the HTML file /src/app/modules/market/product-card/product-card.component.html contents with the following:
<div class="item-card">
<div class="item-card-content-container">
<img class="item-card-content-container-img"
[src]="primaryImageSrc" />
<span class="item-card-content-container-title">
{{product.title}}</span>
<span class="item-card-content-container-text">
{{product.description}}
</span>
</div>
</div>
  1. Replace the CSS file /src/app/modules/market/product-card/product-card.component.css contents with the following:
.item-card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
background-color: white;
width: 280px;
height: 200px;
float: left;
margin: 10;
}

.item-card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}

.item-card-content-container {
padding: 2px 16px;
display: flex;
height: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
}

.item-card-content-container-img {
display: block;
max-width: 80px;
max-height: 80px;
width: auto;
height: auto;
text-align: center;
}

.item-card-content-container-title {
display: block;
font-size: 1.6rem;
}

.item-card-content-container-text {
word-wrap: normal;
font-size: 1.2rem;
overflow: hidden;
}
  1. Generate ProductListComponent with the following command so that we can display a list of product cards:
ng generate component modules/market/product-list
  1. Edit /src/app/modules/market/product-list/product-list.component.ts and implement the behavior to receive a product array as input, as follows:
import { Component, Input } from '@angular/core';
import { Product } from '../../../model';

@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent {
@Input() products: Product[];
}
  1. Replace the HTML file /src/app/modules/market/product-list/product-list.component.html contents with the following:
<div>
<ul class="products-container">
<li *ngFor="let p of products">
<app-product-card [product]="p"></app-product-card>
</li>
</ul>
</div>

  1. Replace the CSS file /src/app/modules/market/product-list/product-list.component.css contents with the following:
ul, li {
list-style: none;
padding: 0;
margin: 0;
display: inline;
}

.products-container {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
}
  1. Generate ProductsService responsible for managing products-related state and actions:
ng generate service modules/core/services/products
  1. Edit /src/app/modules/core/services/products.service.ts and implement its behavior as follows:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { CoreModule } from '../core.module';
import { Product } from '../../../model';

@Injectable({
providedIn: CoreModule,
})
export class ProductsService {
constructor(private readonly http: HttpClient) { }

loadProducts(categoryName: string): Promise<Product[]> {
return this.http
.get(`http://localhost:55564/api/products/searchcategory/${categoryName}`)
.toPromise()
.then(result => result as Product[]);
}
}

  1. In ProductsPageComponent, load products when the category is changed:
...
export class ProductsPageComponent implements OnInit {

products: Product[];

constructor(
private readonly categoriesService: CategoriesService,
private readonly productsService: ProductsService,
) { }



onCategoryChanged(category: Category) {
this.selectedCategory = category;
this.productsService.loadProducts(category.name)
.then (r => this.products = r);

}
}
  1. Edit /src/app/modules/market/products-page/products-page.component.html to render the products:
<app-category-menu
[categories]="categories"
(categoryChanged)="onCategoryChanged($event)"
></app-category-menu>

<app-product-list [products]="products"></app-product-list>

   Next, let's show the indication for the selected category:

  1. In CategoryMenuItemComponent, add an input to reflect whether the category is selected, and then set a class name accordingly:
export class CategoryMenuItemComponent {

@Input() checked = false;

}

  1. Edit /src/app/modules/market/category-menu-item/category-menu-item.component.html to set a class name according to whether the menu item is selected, as follows:
<div class="container" (click)="onSelected()" [class.selected]="checked">
<span>{{ categoryName }}</span>
</div>

The preceding is an example of a specialized binding in Angular: the class binding, formatted as class.<name>. This is a useful feature, since toggling elements' class names conditionally according to state is often needed. In this case, the div element is set with a selected class when the value of the component property checked is true; otherwise, selected is removed.

In addition to class binding, there's also a specialized binding for styles.
Alternatively, there are NgStyle and NgClass directives, which are generally preferred in cases of multiple style properties or class names.

In CategoryMenuComponent, maintain the selected category as follows:


export class CategoryMenuComponent {

selectedCategoryName: string;

onCategorySelected(categoryName: string) {
const cat = this.categories.find(c => c.name === categoryName);
this.selectedCategoryName = cat.name;
this.categoryChanged.emit(cat);
}
}

Edit /src/app/modules/market/category-menu/category-menu.component.html to specify the checked input as follows:

<ul>
<li *ngFor="let c of categories">
<app-category-menu-item
[categoryName]="c.name"
[checked]="c.name === selectedCategoryName"
(selected)="onCategorySelected($event)"
>
</app-category-menu-item>
</li>
</ul>

Congratulations! You have just completed setting up the core functionality of this page. You can run the app and see the products when selecting a category, which should look similar to the following screenshot:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.103.210