<!doctype html>
<html ng-app="ngResourceDemo">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="./style.css">
</head>
<body ng-controller="MainCtrl">
<nav>
<button ng-click="service.addNewTicket()">Neues Ticket anlegen</button>
<button ng-click="service.loadAllTickets()">Alle Tickets laden</button>
<div>
<input type="text" ng-model="ticketToSearch" />
<button ng-click="service.loadTicketById(ticketToSearch)">Lade Ticket</button>
</div>
</nav>
<div class="ticket" ng-repeat="ticket in service.getTickets()">
<button ng-click="service.saveTicket(ticket)">Speichern</button>
<button ng-click="service.deleteTicket(ticket)">Löschen</button>
<h3>({{ticket.id}}) <input ng-model="ticket.title" /></h3>
<span class="assigned-to">Zugeordnet zu: <input ng-model="ticket.assignedTo" /></span>
<span class="percentage-complete">Erledigt %: <input ng-model="ticket.percentageComplete"</span><br />
<textarea ng-model="ticket.description" class="description"></textarea>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-resource.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-mocks.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<script src="./script.js"></script>
</body>
</html>
'use strict';
var app = angular.module('ngResourceDemo', ['ngResource', 'ngMockE2E']);
app.constant('baseUrl', '/ticket/:id');
app.factory('TicketService', function($resource, baseUrl) {
// den $resource-Service initialisieren
var TicketResource = $resource(baseUrl);
// Ticket-Container für bereits geladene Tickets
var tickets = [];
var loadAllTickets = function() {
TicketResource.query(function(data) {
tickets = data;
});
};
var loadTicketById = function(id) {
if(!id || !angular.isNumber(parseInt(id, 10)))
return;
// Zugriff auf das Promise des zugrundeliegenden $http-Services
TicketResource.get({id: id}).$promise.then(function(ticket) {
tickets = [ticket];
});
};
var saveTicket = function(ticket) {
ticket.$save();
};
var addNewTicket = function() {
var newTicket = new TicketResource({
id: 0,
title: 'Neues Ticket',
assignedTo: 'Niemand',
description: 'Ticket Nachricht hier einfügen',
percentageComplete: 0
});
newTicket.$save(function(repliedTicket) {
tickets.unshift(repliedTicket);
});
};
var deleteTicket = function(ticket) {
TicketResource.delete({ id: ticket.id }, function() {
var pos = _.indexOf(_.pluck(tickets, 'id'), ticket.id);
tickets.splice(pos, 1);
});
};
// Revealing Module Pattern
return {
getTickets: function() {
return tickets;
},
loadAllTickets: loadAllTickets,
loadTicketById: loadTicketById,
saveTicket: saveTicket,
addNewTicket: addNewTicket,
deleteTicket: deleteTicket
};
});
app.controller('MainCtrl', function($scope, TicketService) {
$scope.service = TicketService;
$scope.ticketToSearch = "";
});
// httpBackend um Delay erweitern
app.config(function ($provide) {
$provide.decorator('$httpBackend', function($delegate) {
// $delegate ==>
// function $httpBackend(method, url, data, callback,
// headers, timeout, withCredentials)
// Parameter callback soll verzögert ausgeführt werden
// Proxy für $httpBackend erstellen
var wrapper = function(method, url, data, callback,
headers, timeout, withCredentials) {
// Proxy für Parameter callback erstellen
var delayedCallback = function() {
// Zufällige Netzwerklast simulieren
var delay = Math.random(0, 10) * 1000;
// Verzögerter Aufruf
setTimeout((function() {
console.log('Antwortdauer: ' + delay);
callback.apply(this, arguments[0]);
}.bind(this, arguments)), delay);
};
$delegate(method, url, data, delayedCallback,
headers, timeout, withCredentials);
};
// Der äußere Proxy muss auch alle Eigenschaften und Methoden
// wie das Original aufweisen
angular.extend(wrapper, $delegate);
// Dekorierten Service zurückgeben
return wrapper;
});
});
// Verwendung von MockE2E um eine Backend-Attrappe zu erzeugen
app.run(function($httpBackend) {
// Beispielhafte Ticket-Entität
var ticket = function(
id, title, assignedTo, description, percentageComplete) {
this.id = id;
this.title = title;
this.assignedTo = assignedTo;
this.description = description;
this.percentageComplete = percentageComplete;
};
// Exemplarische Datenbank mit Sammlung von Tickets
var tickets = [
new ticket(
'1', 'Fehlende Exception', 'Max Mustermann',
'Die Methode XYZ liefert keine Exception im Fehlerfall ABC', 0),
new ticket(
'2', 'Ticket-Service einbinden', 'Max Mustermann',
'Fehler sollen im Ticketing-Service protokolliert werden', 24),
new ticket(
'3', 'AngularJS updaten', 'Max Mustermann',
'Es soll auf die aktuelle Version 1.3 upgedated werden', 0),
new ticket(
'4', 'Rahmen fehlt', 'Susi Sorglos',
'Das div.demo hat keinen Border definiert', 100),
new ticket(
'5', 'Responsive Grid einführen', 'Susi Sorglos',
'Die Applikation sollte ein Responsive Grid verwenden', 17),
];
// Regex-Ausdruck um auf das Vorhandensein des ID Parameters abzuprüfen
var regexGetTicket = new RegExp('/ticket/([0-9]+)');
// alle Tickets retournieren
// GET: /ticket
$httpBackend.whenGET('/ticket').respond(tickets);
// ein Ticket mittels übereinstimmender ID retournieren
// GET: /ticket/:id
$httpBackend.whenGET({
test: function(url) {
return regexGetTicket.test(url);
}
}).respond(function(method, url) {
var id = url.match(regexGetTicket)[1];
var match = _.find(tickets, function(t) {
return t.id === id;
});
// Die Antwort ist ein HTTP-Response-Status mit einer Payload.
// Abhängig davon ob das Objekt gefunden wurde, liefert die Methode
// den passenden Status und Wert zurück
return match === undefined ? [404, 'Not found'] : [200, match];
});
// Hinzufügen oder Überschreiben eines Tickets
// POST: /ticket
$httpBackend.whenPOST('/ticket').respond(function(method, url, data) {
var newTicket = angular.fromJson(data);
var existingTicket = _.find(tickets, function(t) {
return t.id === newTicket.id;
});
if(existingTicket) {
_.extend(existingTicket, newTicket);
return [200, existingTicket];
} else {
newTicket.id = parseInt(_.max(tickets, function(t) {
return t.id;
}).id || 0, 10) + 1 + '';
tickets.push(newTicket);
return [200, newTicket];
}
});
// Löchsen eines Tickets
// DELETE: /ticket/:id
$httpBackend.whenDELETE({
test: function(url) {
return regexGetTicket.test(url);
}
}).respond(function(method, url) {
var id = url.match(regexGetTicket)[1];
var match = _.find(tickets, function(t) {
return t.id === id;
});
var pos = _.indexOf(_.pluck(tickets, 'id'), id);
tickets.splice(pos, 1);
return match === undefined ? [404, 'Not found'] : [200, match];
});
// Hinweis auf Attrappe
console.log('Backend-Attrappe aktiv!');
});
.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;
}
nav * {
float: left;
}
nav input {
margin-left: 20px;
}