mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Initial state
This commit is contained in:
3
samples/angular/MusicStore/wwwroot/css/site.css
Normal file
3
samples/angular/MusicStore/wwwroot/css/site.css
Normal file
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
14
samples/angular/MusicStore/wwwroot/css/styles.less
Normal file
14
samples/angular/MusicStore/wwwroot/css/styles.less
Normal file
@@ -0,0 +1,14 @@
|
||||
@base: #f938ab;
|
||||
|
||||
.box-shadow(@style, @c) when (iscolor(@c)) {
|
||||
-webkit-box-shadow: @style @c;
|
||||
box-shadow: @style @c;
|
||||
}
|
||||
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
|
||||
.box-shadow(@style, rgba(0, 0, 0, @alpha));
|
||||
}
|
||||
.box {
|
||||
color: saturate(@base, 5%);
|
||||
border-color: lighten(@base, 30%);
|
||||
div { .box-shadow(0 0 5px, 30%) }
|
||||
}
|
||||
BIN
samples/angular/MusicStore/wwwroot/images/home-showcase.png
Normal file
BIN
samples/angular/MusicStore/wwwroot/images/home-showcase.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 248 KiB |
BIN
samples/angular/MusicStore/wwwroot/images/logo.png
Normal file
BIN
samples/angular/MusicStore/wwwroot/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
BIN
samples/angular/MusicStore/wwwroot/images/placeholder.png
Normal file
BIN
samples/angular/MusicStore/wwwroot/images/placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<h1>Store Manager</h1>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import { AlbumsList } from '../albums-list/albums-list';
|
||||
import { AlbumDetails } from '../album-details/album-details';
|
||||
import { AlbumEdit } from '../album-edit/album-edit';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'admin-home'
|
||||
})
|
||||
@router.RouteConfig([
|
||||
{ path: 'albums', as: 'Albums', component: AlbumsList },
|
||||
{ path: 'album/details/:albumId', as: 'AlbumDetails', component: AlbumDetails },
|
||||
{ path: 'album/edit/:albumId', as: 'AlbumEdit', component: AlbumEdit }
|
||||
])
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/admin/admin-home/admin-home.html',
|
||||
directives: [router.ROUTER_DIRECTIVES]
|
||||
})
|
||||
export class AdminHome {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="modal fade">
|
||||
<div class="modal-dialog" *ng-if="album">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Delete {{ album.Title }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Really delete <strong>{{ album.Title }}</strong> by <strong>{{ album.Artist.Name }}</strong>?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger">Confirm Delete</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as models from '../../../models/models';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'album-delete-prompt'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/admin/album-delete-prompt/album-delete-prompt.html',
|
||||
directives: [ng.NgIf]
|
||||
})
|
||||
export class AlbumDeletePrompt {
|
||||
private modalElement: any;
|
||||
public album: models.Album;
|
||||
|
||||
constructor(@ng.Inject(ng.ElementRef) elementRef: ng.ElementRef) {
|
||||
if (typeof window !== 'undefined') {
|
||||
this.modalElement = (<any>window).jQuery(".modal", elementRef.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
public show(album: models.Album) {
|
||||
this.album = album;
|
||||
this.modalElement.modal();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<h2>Album <small>Details</small></h2>
|
||||
<hr />
|
||||
|
||||
<form class="form-horizontal" role="form" *ng-if="albumData">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Artist</label>
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ albumData.Artist.Name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Genre</label>
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ albumData.Genre.Name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Title</label>
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ albumData.Title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Price</label>
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ albumData.Price | currency:'USD':true }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Album Art URL</label>
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ albumData.AlbumArtUrl }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Album Art</label>
|
||||
<div class="col-md-10">
|
||||
<img alt="{{ albumData.Title }}" src="{{ albumData.AlbumArtUrl }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<a class="btn btn-primary" [router-link]="['/Admin/AlbumEdit', { albumId: albumData.AlbumId }]">Edit</a>
|
||||
<button type="button" class="btn btn-danger" (click)="deleteprompt.show(albumData)">Delete</button>
|
||||
<a class="btn btn-default" [router-link]="['/Admin/Albums']">Back to List</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<album-delete-prompt #deleteprompt></album-delete-prompt>
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import * as models from '../../../models/models';
|
||||
import { Http, HTTP_BINDINGS } from 'angular2/http';
|
||||
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'album-details'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/admin/album-details/album-details.html',
|
||||
directives: [router.ROUTER_DIRECTIVES, ng.NgIf, AlbumDeletePrompt]
|
||||
})
|
||||
export class AlbumDetails {
|
||||
public albumData: models.Album;
|
||||
|
||||
constructor(http: Http, routeParam: router.RouteParams) {
|
||||
http.get('/api/albums/' + routeParam.params['albumId']).subscribe(result => {
|
||||
this.albumData = result.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<h2>Album <small>Edit</small></h2>
|
||||
<hr />
|
||||
|
||||
<form class="form-horizontal" [ng-form-model]="form" (ng-submit)="onSubmitModelBased()">
|
||||
<form-field label="Artist" [validate]="form.controls.artist">
|
||||
<select class="form-control" ng-control="artist">
|
||||
<option value="">-- choose Artist --</option>
|
||||
<option *ng-for="#artist of artists" [value]="artist.ArtistId">{{ artist.Name }}</option>
|
||||
</select>
|
||||
</form-field>
|
||||
|
||||
<form-field label="Genre" [validate]="form.controls.genre">
|
||||
<select class="form-control" ng-control="genre">
|
||||
<option value="">-- choose Genre --</option>
|
||||
<option *ng-for="#genre of genres" [value]="genre.GenreId">{{ genre.Name }}</option>
|
||||
</select>
|
||||
</form-field>
|
||||
|
||||
<form-field label="Title" [validate]="form.controls.title">
|
||||
<input class="form-control" type="text" ng-control="title">
|
||||
</form-field>
|
||||
|
||||
<form-field label="Price" [validate]="form.controls.price">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">$</span>
|
||||
<input class="form-control" type="text" ng-control="price">
|
||||
</div>
|
||||
</form-field>
|
||||
|
||||
<form-field label="Album Art URL" [validate]="form.controls.albumArtUrl">
|
||||
<input class="form-control" ng-control="albumArtUrl">
|
||||
</form-field>
|
||||
|
||||
<form-field label="Album Art">
|
||||
<img src="{{ form.controls.albumArtUrl.value }}">
|
||||
</form-field>
|
||||
|
||||
<form-field>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<button type="button" class="btn btn-danger" (click)="deleteprompt.show(originalAlbum)">Delete</button>
|
||||
<a class="btn btn-default" [router-link]="['/Admin/Albums']">Back to List</a>
|
||||
</form-field>
|
||||
</form>
|
||||
|
||||
<album-delete-prompt #deleteprompt></album-delete-prompt>
|
||||
@@ -0,0 +1,82 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import * as models from '../../../models/models';
|
||||
import { Http, HTTP_BINDINGS, Headers } from 'angular2/http';
|
||||
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
|
||||
import { FormField } from '../form-field/form-field';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'album-edit'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/admin/album-edit/album-edit.html',
|
||||
directives: [router.ROUTER_DIRECTIVES, ng.NgIf, ng.NgFor, AlbumDeletePrompt, FormField, ng.FORM_DIRECTIVES]
|
||||
})
|
||||
export class AlbumEdit {
|
||||
public form: ng.ControlGroup;
|
||||
public artists: models.Artist[];
|
||||
public genres: models.Genre[];
|
||||
public originalAlbum: models.Album;
|
||||
|
||||
private _http: Http;
|
||||
|
||||
constructor(fb: ng.FormBuilder, http: Http, routeParam: router.RouteParams) {
|
||||
this._http = http;
|
||||
|
||||
http.get('/api/albums/' + routeParam.params['albumId']).subscribe(result => {
|
||||
var json = result.json();
|
||||
this.originalAlbum = json;
|
||||
(<ng.Control>this.form.controls['title']).updateValue(json.Title);
|
||||
(<ng.Control>this.form.controls['price']).updateValue(json.Price);
|
||||
(<ng.Control>this.form.controls['artist']).updateValue(json.ArtistId);
|
||||
(<ng.Control>this.form.controls['genre']).updateValue(json.GenreId);
|
||||
(<ng.Control>this.form.controls['albumArtUrl']).updateValue(json.AlbumArtUrl);
|
||||
});
|
||||
|
||||
http.get('/api/artists/lookup').subscribe(result => {
|
||||
this.artists = result.json();
|
||||
});
|
||||
|
||||
http.get('/api/genres/genre-lookup').subscribe(result => {
|
||||
this.genres = result.json();
|
||||
});
|
||||
|
||||
this.form = fb.group(<any>{
|
||||
artist: fb.control('', ng.Validators.required),
|
||||
genre: fb.control('', ng.Validators.required),
|
||||
title: fb.control('', ng.Validators.required),
|
||||
price: fb.control('', ng.Validators.compose([ng.Validators.required, AlbumEdit._validatePrice])),
|
||||
albumArtUrl: fb.control('', ng.Validators.required)
|
||||
});
|
||||
}
|
||||
|
||||
public onSubmitModelBased() {
|
||||
// Force all fields to show any validation errors even if they haven't been touched
|
||||
Object.keys(this.form.controls).forEach(controlName => {
|
||||
this.form.controls[controlName].markAsTouched();
|
||||
});
|
||||
|
||||
if (this.form.valid) {
|
||||
var controls = this.form.controls;
|
||||
var albumId = this.originalAlbum.AlbumId;
|
||||
(<any>window).fetch(`/api/albums/${ albumId }/update`, {
|
||||
method: 'put',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
AlbumArtUrl: controls['albumArtUrl'].value,
|
||||
AlbumId: albumId,
|
||||
ArtistId: controls['artist'].value,
|
||||
GenreId: controls['genre'].value,
|
||||
Price: controls['price'].value,
|
||||
Title: controls['title'].value
|
||||
})
|
||||
}).then(response => {
|
||||
console.log(response);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static _validatePrice(control: ng.Control): { [key: string]: boolean } {
|
||||
return /^\d+\.\d+$/.test(control.value) ? null : { price: true };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<h2>Albums</h2>
|
||||
|
||||
<album-delete-prompt #deleteprompt></album-delete-prompt>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a>Genre</a></th>
|
||||
<th><a>Artist</a></th>
|
||||
<th><a (click)="sortBy('Title')">Title</a></th>
|
||||
<th><a (click)="sortBy('Price')">Price</a></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ng-for="#row of rows">
|
||||
<td>{{ row.Genre.Name }}</td>
|
||||
<td>{{ row.Artist.Name }}</td>
|
||||
<td>{{ row.Title }}</td>
|
||||
<td>{{ row.Price | currency:'USD':true }}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a class="btn btn-default" [router-link]="['/Admin/AlbumDetails', { albumId: row.AlbumId }]">Details</a>
|
||||
<a class="btn btn-default" [router-link]="['/Admin/AlbumEdit', { albumId: row.AlbumId }]">Edit</a>
|
||||
<a class="btn btn-default" (click)="deleteprompt.show(row)">Delete</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-default" [disabled]="!canGoBack" (click)="goToPage(1)">First</button>
|
||||
<button class="btn btn-default" [disabled]="!canGoBack" (click)="goToPage(pageIndex - 1)">Previous</button>
|
||||
<button class="btn" *ng-for="#page of pageLinks"
|
||||
[ng-class]="{ 'btn-info': page.isCurrent, 'btn-default': !page.isCurrent }"
|
||||
(click)="goToPage(page.index)">
|
||||
{{ page.text }}
|
||||
</button>
|
||||
<button class="btn btn-default" [disabled]="!canGoForward" (click)="goToPage(pageIndex + 1)">Next</button>
|
||||
<button class="btn btn-default" [disabled]="!canGoForward" (click)="goToLast()">Last</button>
|
||||
</div>
|
||||
|
||||
<p>{{ totalCount }} total albums</p>
|
||||
@@ -0,0 +1,70 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import { Http, HTTP_BINDINGS } from 'angular2/http';
|
||||
import * as models from '../../../models/models';
|
||||
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'albums-list'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/admin/albums-list/albums-list.html',
|
||||
directives: [ng.NgFor, ng.NgClass, router.ROUTER_DIRECTIVES, AlbumDeletePrompt]
|
||||
})
|
||||
export class AlbumsList {
|
||||
public rows: models.Album[];
|
||||
public canGoBack: boolean;
|
||||
public canGoForward: boolean;
|
||||
public pageLinks: any[];
|
||||
public totalCount: number;
|
||||
public get pageIndex() {
|
||||
return this._pageIndex;
|
||||
}
|
||||
|
||||
private _http: Http;
|
||||
private _pageIndex = 1;
|
||||
private _sortBy = "Title";
|
||||
private _sortByDesc = false;
|
||||
|
||||
constructor(http: Http) {
|
||||
this._http = http;
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
public sortBy(col: string) {
|
||||
this._sortByDesc = col === this._sortBy ? !this._sortByDesc : false;
|
||||
this._sortBy = col;
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
public goToPage(pageIndex: number) {
|
||||
this._pageIndex = pageIndex;
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
public goToLast() {
|
||||
this.goToPage(this.pageLinks[this.pageLinks.length - 1].index);
|
||||
}
|
||||
|
||||
refreshData() {
|
||||
var sortBy = this._sortBy + (this._sortByDesc ? ' DESC' : '');
|
||||
this._http.get(`/api/albums?page=${ this._pageIndex }&pageSize=50&sortBy=${ sortBy }`).subscribe(result => {
|
||||
var json = result.json();
|
||||
this.rows = json.Data;
|
||||
|
||||
var numPages = Math.ceil(json.TotalCount / json.PageSize);
|
||||
this.pageLinks = [];
|
||||
for (var i = 1; i <= numPages; i++) {
|
||||
this.pageLinks.push({
|
||||
index: i,
|
||||
text: i.toString(),
|
||||
isCurrent: i === json.Page
|
||||
});
|
||||
}
|
||||
|
||||
this.canGoBack = this.pageLinks.length && !this.pageLinks[0].isCurrent;
|
||||
this.canGoForward = this.pageLinks.length && !this.pageLinks[this.pageLinks.length - 1].isCurrent;
|
||||
this.totalCount = json.TotalCount;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="form-group" [class.has-error]="errorMessages.length">
|
||||
<label class="col-md-2 control-label">{{ label }}</label>
|
||||
<div class="col-md-5">
|
||||
<ng-content></ng-content>
|
||||
<div class="alert alert-danger" role="alert" *ng-if="errorMessages.length">
|
||||
<p *ng-for="#message of errorMessages">{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'form-field',
|
||||
properties: ['label', 'validate']
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/admin/form-field/form-field.html',
|
||||
directives: [ng.NgIf, ng.NgFor]
|
||||
})
|
||||
export class FormField {
|
||||
private validate: ng.AbstractControl;
|
||||
|
||||
public get errorMessages() {
|
||||
var errors = (this.validate && this.validate.touched && this.validate.errors) || {};
|
||||
return Object.keys(errors).map(key => {
|
||||
return 'Error: ' + key;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" [router-link]="['/Home']">Music Store</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a [router-link]="['/Home']">Home</a></li>
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown">Store <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ng-for="#genre of genres">
|
||||
<a title="{{ genre.Description }}" [router-link]="['/Genre', { genreId: genre.GenreId }]">
|
||||
{{ genre.Name }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a [router-link]="['/GenresList']">More…</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a [router-link]="['/Admin/Albums']">Admin</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container body-content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import { Http, HTTP_BINDINGS } from 'angular2/http';
|
||||
import { Home } from '../public/home/home';
|
||||
import { AlbumDetails } from '../public/album-details/album-details';
|
||||
import { GenreContents } from '../public/genre-contents/genre-contents';
|
||||
import { GenresList } from '../public/genres-list/genres-list';
|
||||
import { AdminHome } from '../admin/admin-home/admin-home';
|
||||
import * as models from '../../models/models';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'app'
|
||||
})
|
||||
@router.RouteConfig([
|
||||
{ path: '/', component: Home, as: 'Home' },
|
||||
{ path: '/album/:albumId', component: AlbumDetails, as: 'Album' },
|
||||
{ path: '/genre/:genreId', component: GenreContents, as: 'Genre' },
|
||||
{ path: '/genres', component: GenresList, as: 'GenresList' },
|
||||
{ path: '/admin/...', component: AdminHome, as: 'Admin' }
|
||||
])
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/app/app.html',
|
||||
styleUrls: ['./ng-app/components/app/app.css'],
|
||||
directives: [router.ROUTER_DIRECTIVES, ng.NgFor]
|
||||
})
|
||||
export class App {
|
||||
public genres: models.Genre[];
|
||||
|
||||
constructor(http: Http) {
|
||||
http.get('/api/genres/menu').subscribe(result => {
|
||||
this.genres = result.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import { Http, HTTP_BINDINGS } from 'angular2/http';
|
||||
import { App } from './app';
|
||||
|
||||
ng.bootstrap(App, [router.ROUTER_BINDINGS, HTTP_BINDINGS, ng.FormBuilder]);
|
||||
@@ -0,0 +1,26 @@
|
||||
<div *ng-if="albumData">
|
||||
<h2>{{ albumData.Title }}</h2>
|
||||
|
||||
<p>
|
||||
<img alt="{{ albumData.Title }}" src="{{ albumData.AlbumArtUrl }}">
|
||||
</p>
|
||||
|
||||
<div id="album-details">
|
||||
<p>
|
||||
<em>Genre:</em>
|
||||
{{ albumData.Genre.Name }}
|
||||
</p>
|
||||
<p>
|
||||
<em>Artist:</em>
|
||||
{{ albumData.Artist.Name }}
|
||||
</p>
|
||||
<p>
|
||||
<em>Price:</em>
|
||||
{{ albumData.Price | currency:'USD':true }}
|
||||
</p>
|
||||
<p class="button">
|
||||
<!-- TODO: Shopping cart functionality -->
|
||||
Add to cart
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import { Http } from 'angular2/http';
|
||||
import * as models from '../../../models/models';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'album-details'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/public/album-details/album-details.html',
|
||||
directives: [ng.NgIf]
|
||||
})
|
||||
export class AlbumDetails {
|
||||
public albumData: models.Album;
|
||||
|
||||
constructor(http: Http, routeParam: router.RouteParams) {
|
||||
http.get('/api/albums/' + routeParam.params['albumId']).subscribe(result => {
|
||||
this.albumData = result.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<a [router-link]="['/Album', { albumId: albumData.AlbumId }]">
|
||||
<img alt="{{ albumData.Title }}" src="{{ albumData.AlbumArtUrl }}">
|
||||
<h4>{{ albumData.Title }}</h4>
|
||||
</a>
|
||||
@@ -0,0 +1,14 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import * as models from '../../../models/models';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'album-tile',
|
||||
properties: ['albumData: albumdata']
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/public/album-tile/album-tile.html',
|
||||
directives: [router.ROUTER_DIRECTIVES]
|
||||
})
|
||||
export class AlbumTile {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<h3>Albums</h3>
|
||||
|
||||
<ul class="list-unstyled">
|
||||
<li *ng-for="#album of albums" class="col-lg-2 col-md-2 col-sm-2 col-xs-4 container">
|
||||
<album-tile [albumData]="album"></album-tile>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import { Http } from 'angular2/http';
|
||||
import * as models from '../../../models/models';
|
||||
import { AlbumTile } from '../album-tile/album-tile';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'genre-contents'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/public/genre-contents/genre-contents.html',
|
||||
directives: [ng.NgFor, AlbumTile]
|
||||
})
|
||||
export class GenreContents {
|
||||
public albums: models.Album[];
|
||||
|
||||
constructor(http: Http, routeParam: router.RouteParams) {
|
||||
http.get(`/api/genres/${ routeParam.params['genreId'] }/albums`).subscribe(result => {
|
||||
this.albums = result.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<h3>Browse Genres</h3>
|
||||
|
||||
<p *ng-if="genres">
|
||||
Select from {{ genres.length }} genres:
|
||||
</p>
|
||||
|
||||
<ul class="list-group">
|
||||
<li *ng-for="#genre of genres" class="list-group-item">
|
||||
<a title="{{genre.Description}}" [router-link]="['/Genre', { genreId: genre.GenreId }]">
|
||||
{{ genre.Name }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import * as router from 'angular2/router';
|
||||
import { Http } from 'angular2/http';
|
||||
import * as models from '../../../models/models';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'genres-list'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/public/genres-list/genres-list.html',
|
||||
directives: [router.ROUTER_DIRECTIVES, ng.NgIf, ng.NgFor]
|
||||
})
|
||||
export class GenresList {
|
||||
public genres: models.Genre[];
|
||||
|
||||
constructor(http: Http) {
|
||||
http.get('/api/genres').subscribe(result => {
|
||||
this.genres = result.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="jumbotron">
|
||||
<h1>MVC Music Store</h1>
|
||||
<img src="/Images/home-showcase.png">
|
||||
</div>
|
||||
|
||||
<ul class="row list-unstyled" id="album-list">
|
||||
<li *ng-for="#album of mostPopular" class="col-lg-2 col-md-2 col-sm-2 col-xs-4 container">
|
||||
<album-tile [albumData]="album"></album-tile>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as ng from 'angular2/angular2';
|
||||
import { Http } from 'angular2/http';
|
||||
import { AlbumTile } from '../album-tile/album-tile';
|
||||
import * as models from '../../../models/models';
|
||||
|
||||
@ng.Component({
|
||||
selector: 'home'
|
||||
})
|
||||
@ng.View({
|
||||
templateUrl: './ng-app/components/public/home/home.html',
|
||||
directives: [ng.NgFor, AlbumTile]
|
||||
})
|
||||
export class Home {
|
||||
public mostPopular: models.Album[];
|
||||
|
||||
constructor(http: Http) {
|
||||
http.get('/api/albums/mostPopular').subscribe(result => {
|
||||
this.mostPopular = result.json();
|
||||
});
|
||||
}
|
||||
}
|
||||
16
samples/angular/MusicStore/wwwroot/ng-app/models/models.ts
Normal file
16
samples/angular/MusicStore/wwwroot/ng-app/models/models.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export interface Album {
|
||||
AlbumId: number;
|
||||
Title: string;
|
||||
AlbumArtUrl: string;
|
||||
}
|
||||
|
||||
export interface Genre {
|
||||
GenreId: number;
|
||||
Name: string;
|
||||
Description: string;
|
||||
}
|
||||
|
||||
export interface Artist {
|
||||
ArtistId: number;
|
||||
Name: string;
|
||||
}
|
||||
3
samples/angular/MusicStore/wwwroot/system.config.js
Normal file
3
samples/angular/MusicStore/wwwroot/system.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
System.config({
|
||||
defaultJSExtensions: true
|
||||
});
|
||||
9
samples/angular/MusicStore/wwwroot/web.config
Normal file
9
samples/angular/MusicStore/wwwroot/web.config
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
|
||||
</handlers>
|
||||
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false"/>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user