import {Component} from 'angular2/core';
import {TicketComponent} from './ticket.component'
import {TicketService} from './ticket.service';
import {Ticket} from './ticket.entity';
import {Response} from 'angular2/http';
import {MockBackend} from 'angular2/http/testing';
// globally loaded lodash
declare let _: any;
@Component({
selector: 'my-app',
templateUrl: 'app/templates/index.html',
directives: [TicketComponent],
providers: [TicketService]
})
export class AppComponent {
// Fake Tickets DB
private db: Ticket[] = [
new Ticket(
'1', 'Missing Exception', 'John Smith',
'Method XYZ should throw exception in case ABC', 0),
new Ticket(
'2', 'Log errors', 'John Smith',
'Logs need to be persisted to a local file', 24),
new Ticket(
'3', 'Update AngularJS', 'John Smith',
'Need to update the App to AngularJS version 1.5', 0),
new Ticket(
'4', 'Border is missing', 'Jane Doe',
'The element div.demo has no border defined', 100),
new Ticket(
'5', 'Introduce responsive grid', 'Jane Doe',
'Implement reponsive grid for better displays on mobile devices', 17)
];
constructor(private service: TicketService, private backend: MockBackend) {
this.backend.connections.subscribe( c => {
let singleTicketMatcher = /\/api\/ticket\/([0-9]+)/i;
// return all tickets
// GET: /ticket
if (c.request.url === "http://localhost:8080/api/ticket" && c.request.method === 0) {
let res = new Response({
body: JSON.stringify(this.db)
});
c.mockRespond(res);
}
// return ticket matching the given id
// GET: /ticket/:id
else if (c.request.url.match(singleTicketMatcher) && c.request.method === 0) {
let matches = this.db.filter( (t) => {
return t._id == c.request.url.match(singleTicketMatcher)[1]
});
c.mockRespond(new Response({
body: JSON.stringify(matches[0])
}));
}
// Add or update a ticket
// POST: /ticket
else if (c.request.url === 'http://localhost:8080/api/ticket' && c.request.method === 1) {
let newTicket: Ticket = JSON.parse(c.request._body);
let existingTicket = this.db.filter( (ticket: Ticket) => { return ticket._id == newTicket._id});
if (existingTicket && existingTicket.length === 1) {
Object.assign(existingTicket[0], newTicket);
c.mockRespond(new Response({
body: JSON.stringify(existingTicket[0])
}));
} else {
newTicket._id = parseInt(_.max(this.db, function(t) {
return t._id;
})._id || 0, 10) + 1 + '';
this.db.push(newTicket);
c.mockRespond(new Response({
body: JSON.stringify(newTicket)
}));
}
}
// Delete a ticket
// DELETE: /ticket/:id
else if (c.request.url.match(singleTicketMatcher) && c.request.method === 3) {
let ticketId = c.request.url.match(singleTicketMatcher)[1];
let pos = _.indexOf(_.pluck(this.db, '_id'), ticketId);
this.db.splice(pos, 1);
c.mockRespond(new Response({
body: JSON.stringify({})
}));
}
});
}
public ngOnInit() {
this.service.loadAllTickets();
}
}
import {bootstrap} from 'angular2/platform/browser';
import {provide} from 'angular2/core';
import {MockBackend} from 'angular2/http/testing';
import {HTTP_PROVIDERS, Http, BaseRequestOptions} from 'angular2/http';
import {AppComponent} from './app.component'
bootstrap(AppComponent, [
HTTP_PROVIDERS,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: (backend, options) => {
return new Http(backend, options);
},
deps: [MockBackend, BaseRequestOptions]
})
]);
<!DOCTYPE html>
<html>
<head>
<title>ng2 Ticketing System</title>
<!-- 1. Load libraries -->
<script src="https://code.angularjs.org/2.0.0-beta.0/angular2-polyfills.js"></script>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.0/Rx.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.0/angular2.dev.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.0/http.dev.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.0/testing.dev.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<!-- 2. Configure SystemJS -->
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'app': {defaultExtension: 'ts'}}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
<meta charset="utf-8">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/cerulean/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading ...</my-app>
</body>
</html>
import {Ticket} from './ticket.entity';
import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
import 'rxjs/add/operator/map';
@Injectable()
export class TicketService {
tickets: Ticket[] = [];
constructor(private http: Http) {
}
addNewTicket() {
var headers = new Headers();
headers.append('Content-Type', 'application/json');
var newTicket = new Ticket("0", 'New Ticket', 'Nobody', 'Enter ticket description here', 0);
this.http
.post('http://localhost:8080/api/ticket', JSON.stringify(newTicket), headers)
.map(res => res.json())
.subscribe(
data => this.tickets.push(data),
err => this.logError(err),
() => console.log('Updated Ticket')
);
}
saveTicket(ticket: Ticket) {
var headers = new Headers();
headers.append('Content-Type', 'application/json');
this.http
.post('http://localhost:8080/api/ticket', JSON.stringify(ticket), headers)
.map(res => res.json())
.subscribe(
null,
err => this.logError(err),
() => console.log('Updated Ticket')
);
}
deleteTicket(ticket: Ticket) {
this.http
.delete('http://localhost:8080/api/ticket/' + ticket._id)
.map(res => res.text())
.subscribe(
data => {
var midx = -1;
this.tickets.forEach( (t, idx) => {
if (t._id == ticket._id) {
midx = idx;
}
});
this.tickets.splice(midx, 1);
},
err => this.logError(err),
() => console.log('Request for all tickets completed successfully')
);
}
loadAllTickets() {
this.http
.get('http://localhost:8080/api/ticket')
.map(res => {
return res.json()
})
.subscribe(
data => {
this.tickets = data;
},
err => this.logError(err),
() => console.log("Loaded all tickets")
);
}
loadTicketById(id) {
this.http
.get('http://localhost:8080/api/ticket/' + id)
.map(res => res.json())
.subscribe(
data => this.tickets = [data],
err => this.logError(err),
() => console.log("Loaded ticket with id " + id)
);
}
logError(err) {
console.error('There was an error: ' + err);
}
}
export class Ticket {
public _id: string;
public title: string;
public assignedTo: string;
public description: string;
public percentageComplete: number;
constructor(id: string, title: string, assignedTo: string, description: string, percentageComplete: number) {
this._id = id;
this.title = title;
this.assignedTo = assignedTo;
this.description = description;
this.percentageComplete = percentageComplete;
}
}
import {Component} from 'angular2/core';
import {Ticket} from './ticket.entity';
import {TicketService} from './ticket.service';
@Component({
selector: 'ticket',
templateUrl: 'app/templates/ticket.html',
inputs: ['ticket'],
//providers: [TicketService] <-- this would override the parent DI instance
})
export class TicketComponent {
public ticket: Ticket;
constructor(private service: TicketService) { }
}
.ticket {
clear:both;
border: black solid 1px;
margin-bottom: 5px;
padding: 10px;
width: 500px;
}
.ticket .assigned-to {
margin-right: 20px;
}
.ticket .percentage-complete input {
width: 30px;
text-align: right;
}
.ticket .description {
width: 400px;
height: 100px;
margin-top: 10px;
}
.ticket-to-search {
display: inline-block;
width: auto;
vertical-align: middle;
}
nav * {
float: left;
}
nav button {
margin-left: 5px;
}
nav input {
margin-left: 20px;
}
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">NG2 Ticketing system</a>
</div>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<button class="btn btn-default" (click)="service.addNewTicket()"><span class="glyphicon glyphicon-plus"> </span> Add new ticket</button>
<button class="btn btn-default" (click)="service.loadAllTickets()"><span class="glyphicon glyphicon-download"> </span> Load all tickets</button>
<input type="text" class="form-control ticket-to-search" [(ngModel)]="ticketToSearch" />
<button class="btn btn-default" (click)="service.loadTicketById(ticketToSearch)"><span class="glyphicon glyphicon-download"> </span> Load ticket</button>
</div>
</form>
</div>
</nav>
<div class="container">
<ticket *ngFor="#ticket of service.tickets" [ticket]="ticket"></ticket>
</div>
<div class="ticket panel panel-default" >
<div class="panel-body" ng-class="ticket.percentageComplete == '100' ? 'well' : ''">
<button class="btn btn-success" (click)="service.saveTicket(ticket)"><span class="glyphicon glyphicon-save"> </span> Save</button>
<button class="btn btn-danger" (click)="service.deleteTicket(ticket)"><span class="glyphicon glyphicon-trash"> </span> Delete</button>
<h3>({{ticket._id}}) <input [(ngModel)]="ticket.title" /></h3>
<span class="assigned-to">Assigned to: <input [(ngModel)]="ticket.assignedTo" /></span>
<span class="percentage-complete">Completed %: <input [(ngModel)]="ticket.percentageComplete" /></span><br />
<textarea [(ngModel)]="ticket.description" class="description"></textarea>
</div>
</div>
Look ma, no server! Don't be held up waiting for the API to be built; Vildan Softic shows how to develop apps with the Angular 2 MockBackend class.
http://www.sitepoint.com/angular-2-mockbackend/