Inventory Displayer app.
This app shows list of products on the inventory of a store. Every product is shown in one row. A product is represented by a component namely Product. A ProductRow component is composed of three different component, namely ProductImage, ProductDepartment, and PriceDisplay.
We use component ProductList for rendering all ProductRows and for storing the currently selected Product. InventoryApp component uses component ProductList to display the whole list of product available. The model part in the MVC pattern is for the time being inside the component inventory-app as we are creating products inside the constructor in the controller/class of the component inventory-app.
Here is the code for the app.ts file:
import { Component, EventEmitter } from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
class Product {
constructor(
public sku: string,
public name: string,
public imageUrl: string,
public department: string[],
public price: number) {
}
}
/***************************************************************************************/
@Component({
selector: 'product-image',
host: {class: 'ui small image'},
inputs: ['product'],
template: `
<img class="product-image" [src]="product.imageUrl">
`
})
class ProductImage {
product: Product;
}
/***************************************************************************************/
@Component({
selector: 'product-department',
inputs: ['product'],
template: `
<div class="product-department">
<span *ngFor="#name of product.department; #i=index">
<a href="#">{{ name }}</a>
<span>{{i < (product.department.length-1) ? '>' : ''}}</span>
</span>
</div>
`
})
class ProductDepartment {
product: Product;
}
/***************************************************************************************/
@Component({
selector: 'price-display',
inputs: ['price'],
template: `
<div class="price-display">\${{ price }}</div>
`
})
class PriceDisplay {
price: number;
}
/***************************************************************************************/
@Component({
selector: 'product-row',
inputs: ['product'],
host: {'class': 'item'},
directives: [ProductImage, ProductDepartment, PriceDisplay],
template: `
<product-image [product]="product"></product-image>
<div class="content">
<div class="header">{{ product.name }}</div>
<div class="meta">
<div class="product-sku">SKU #{{ product.sku }}</div>
</div>
<div class="description">
<product-department [product]="product"></product-department>
</div>
</div>
<price-display [price]="product.price"></price-display>
`
})
class ProductRow {
product: Product;
}
/***************************************************************************************/
/**
* @ProductsList: A component for rendering all ProductRows and
* storing the currently selected Product
*/
@Component({
selector: 'products-list',
directives: [ProductRow],
inputs: ['productList'],
outputs: ['onProductSelected'],
template: `
<div class="ui items">
<product-row
*ngFor="#myProduct of productList"
[product]="myProduct"
(click)='clicked(myProduct)'
[class.selected]="isSelected(myProduct)">
</product-row>
</div>
`
})
class ProductsList {
productList: Product[];
onProductSelected: EventEmitter<Product>;
currentProduct: Product;
constructor() {
this.onProductSelected = new EventEmitter();
}
clicked(product: Product): void {
this.currentProduct = product;
this.onProductSelected.emit(product);
}
isSelected(product: Product): boolean {
if (!product || !this.currentProduct) {
return false;
}
/* return true if the product passed as parameter to this method is the same as pointed by currentProduct i.e., clicked by user. Otherwise return false.*/
return product.sku === this.currentProduct.sku;
}
}//class ProductList ends here.
/***************************************************************************************/
@Component({
selector: 'inventory-app',
directives: [ProductsList],/* This component will use product-list component and the name of the controller/class of that component is ProductList. */
/* The view in the MVC pattern begins below. */
template: `
<div class="inventory-app">
<products-list
[productList]="products"
(onProductSelected)="productWasSelected($event)">
</products-list>
</div>
`
})
/* The controller (in the MVC pattern) for the component begins here. */
class InventoryApp {
products: Product[];
constructor() {
/* Here is the Model in the MVC pattern. */
this.products = [
new Product(
'MYSHOES', 'Black Running Shoes',
'/resources/images/products/black-shoes.jpg',
['Men', 'Shoes', 'Running Shoes'],
109.99),
new Product(
'NEATOJACKET', 'Blue Jacket',
'/resources/images/products/blue-jacket.jpg',
['Women', 'Apparel', 'Jackets & Vests'],
238.99),
new Product(
'NICEHAT', 'A Nice Black Hat',
'/resources/images/products/black-hat.jpg',
['Men', 'Accessories', 'Hats'],
29.99)
];
}
productWasSelected(product: Product): void {
console.log('Product clicked: ', product);
}
}//class InventoryApp ends here.
/***************************************************************************************/
bootstrap(InventoryApp);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Here is the index.html file:
<!doctype html>
<html>
<head>
<title>ng-book 2: Inventory App</title>
<link rel="icon" type="image/png" href="resources/images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" href="resources/images/favicon.ico" />
<!-- Libraries -->
<script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<!-- Stylesheet -->
<link rel="stylesheet" type="text/css" href="resources/vendor/semantic.min.css">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<!-- Configure System.js, our module loader -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app.js')
.then(null, console.error.bind(console));
</script>
<!-- Menu Bar -->
<div class="ui menu">
<div class="ui container">
<a href="#" class="header item">
<img class="logo" src="resources/images/ng-book-2-minibook.png">
ng-book 2
</a>
<div class="header item borderless">
<h1 class="ui header">
Angular 2 Inventory App
</h1>
</div>
</div>
</div>
<div class="ui main text container">
<inventory-app></inventory-app> <!-- <--- Our app loads here! -->
</div>
</body>
</html>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Here is the css used in the app. styles.css
.ui.menu .item img.logo {
margin-right: 1.5em;
}
.ui.items>.item {
border: 1px solid rgba(255, 255, 255, 0.0);
border-radius: 5px;
}
.ui.items>.item.selected {
border: 1px solid rgba(163, 51, 200, 0.41);
border-radius: 5px;
}
.ui.items>.item>.content>.header {
padding-top: 1em;
}
.products-list {
border-top: 1px solid black;
border-left: 1px solid black;
border-right: 1px solid black;
}
.product-row {
border-bottom: 1px solid black;
}
.product-image {
float: left;
}
.product-info {
padding: 30px;
float: left;
}
.price-display {
padding: 30px;
float: right;
}
.product-image {
width: 100px;
height: 100px;
padding: 20px;
}
.cf:before,
.cf:after {
content: " "; /* 1 */
display: table; /* 2 */
}
.cf:after {
clear: both;
}
cf {
*zoom: 1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////