Skip to content

Instantly share code, notes, and snippets.

@TylerJPresley
Last active July 10, 2016 19:45
Show Gist options
  • Save TylerJPresley/b23ffdddabdf32e5ded12e6247597fc1 to your computer and use it in GitHub Desktop.
Save TylerJPresley/b23ffdddabdf32e5ded12e6247597fc1 to your computer and use it in GitHub Desktop.
Services Example // Aurelia (CLI/RequireJS)
export default {
debug: true,
testing: true,
apiUrl: 'http://localhost:3000/'
};
<template>
<form submit.trigger="updateWidget()" validation-renderer="bootstrap-form" validation-errors.bind="errors">
<input type="text" value.bind="widget.name & validate">
<a click.trigger="deleteWidget()"><span class="fa fa-times"></span> Delete</a>
<input type="submit" class="btn btn-primary" value="Update">
</form>
</template>
/**
* Imports
*/
import {inject, NewInstance} from 'aurelia-dependency-injection';
import {ValidationController, validateTrigger} from 'aurelia-validation';
import {ValidationRules} from 'aurelia-validatejs';
import {Prompt} from 'resources/components/dialog-prompt';
import {Router} from 'aurelia-router';
import {Loader} from 'resources/loader';
import {MetaService} from 'resources/services/meta-service';
import {Notification} from 'resources/notification';
import {WidgetService} from 'resources/services/widget-service';
/**
* Decorators
*/
@inject(Router, NewInstance.of(ValidationController), Loader, MetaService, Notification, WidgetService)
/**
* IndexView Class
* @class
*/
export class IndexView {
/**
* Class fields
*/
widgets = [{ name: 'widg10304', sku: '33339999', color: 'silver' }];
widget = { name: 'widg10304', sku: '33339999', color: 'silver' };
widgetId = null;
/**
* Constructs an instance
* @param router {object} - router instance
* @param validation {object} - validation instance
* @param loader {object} - loader instance
* @param metaService {object} - metaService instance
* @param notification {object} - notification instance
* @param widgetService {object} - widgetService instance
* @constructor
*/
constructor(router, validation, loader, metaService, notification, widgetService) {
this.router = router;
this.validation = validation;
this.loader = loader;
this.metaService = metaService;
this.notification = notification;
this.widgetService = widgetService;
}
/**
* Called just before view is attached to the document
* @param params {object} - route parameters
* @param routeConfig {object} - router configuration object
* @param navigationInstruction {object} - navigation instruction object
* @returns {Promise}
*/
activate(params, routeConfig, navigationInstruction) {
/** Assign the widgetId to a class field */
this.widgetId = params.id;
/** Make service calls. Page will wait on all the Promises to return */
return Promise.all([
this.metaService.getMeta(),
this.getWidget()
]);
}
/**
* Called when class is attached to the document
*/
attached() {
this.setupValidationRules();
}
/**
* gets an array of widgets
* @returns {Promise}
*/
getWidgets() {
/** Show the loader */
this.loader.show();
/** Call the delete session service method */
return this.widgetService.getWidgets()
.then(response => {
if (response && response.widgets) {
/** Assign to the class field */
this.widgets = response.widgets;
}
/** Hide loader */
this.loader.hide();
})
.catch(() => { this.loader.hide(); });
}
/**
* gets a widget
* @returns {Promise}
*/
getWidget() {
/** Show the loader */
this.loader.show();
/** Call the delete session service method */
return this.widgetService.getWidget(this.widgetId)
.then(response => {
if (response && response.widget) {
/** Assign to the class field */
this.widget = response.widget;
}
/** Hide loader */
this.loader.hide();
})
.catch(() => { this.loader.hide(); });
}
/**
* Deletes the session
* @returns {Promise}
*/
deleteWidget() {
this.dialogService.open({ viewModel: Prompt, model: { title: 'Confirmation', message: 'Are you sure you want to delete this?'}}).then(r => {
if (!r.wasCancelled) {
/** Show the loader */
this.loader.show();
/** Call the delete session service method */
return this.widgetService.deleteWidget(this.widgetId)
.then(response => {
if (response && response.widget) {
/** Show a success message */
this.notification.showSuccess('Widget Removed!');
/** remove the item from array */
this.router.navigate('/widgets');
}
/** Hide loader */
this.loader.hide();
})
.catch(() => { this.loader.hide(); });
}
});
}
/**
* Updates a session
* @returns {Promise}
*/
updateWidget() {
/** Validate */
this.errors = this.validation.validate();
/** Proceed if there's no errors */
if (this.errors.length === 0) {
/** Show the loader */
this.loader.show();
/** Call the update session service method */
return this.widgetService.updateWidget(this.widget)
.then(response => {
if (response && response.model) {
/** Show a message */
this.notification.showSuccess('Widget Updated!');
/** Adjust the title */
this.heading = this.widget.name;
}
/** Hide the loader */
this.loader.hide();
}).catch(() => { this.loader.hide(); });
}
}
/**
* Sets up the validation rules for this class
*/
setupValidationRules() {
ValidationRules
.ensure('name').required().length({ maximum: 150 })
.ensure('sku').required().length({ maximum: 15 })
.ensure('color').required()
.on(this.widget);
}
}
/**
* Imports
*/
import {HttpClient} from 'aurelia-fetch-client';
import {transient, inject} from 'aurelia-framework';
import {AppSession} from 'resources/app-session';
import {ServiceUtility} from 'resources/service-utility';
import 'whatwg-fetch';
/**
* Decorators
*/
@transient()
@inject(HttpClient, AppSession, ServiceUtility)
/**
* MetaService Class
* This class is a dual use class that stores the meta info locally while the app
* is running, but can also make the API call to get fresh data.
* @class
*/
export class MetaService {
/**
* Constructs a MetaService instance
* @param http {object} - http instance object
* @param appSession {object} - appSession instance object
* @param serviceUtility {object} - serviceUtility instance object
* @constructor
*/
constructor(http, appSession, serviceUtility) {
this.serviceUtility = serviceUtility;
this.appSession = appSession;
http.configure(config => serviceUtility.standardConfiguration(config));
this.http = http;
}
/**
* Gets the locally stored meta information, or loads it from the API
* @returns {Promise}
*/
getMeta() {
/** Return the instance member */
if (this.appSession.meta) {
return new Promise((accept, reject) => {
return accept(true);
});
}
/** Call the API to get the meta data */
return this.refreshMetaData();
}
/**
* Calls the service method
* @returns {Promise}
*/
refreshMetaData() {
return this.getMetaData()
.then(response => {
if (response && response.model) {
this.appSession.meta = response.model;
}
return response;
});
}
/**
* Gets the meta data
* @return {promise}
*/
getMetaData() {
return this.http.fetch('meta')
.then(response => this.serviceUtility.responseFilter(response))
.then(data => this.serviceUtility.standardThen(data))
.catch(() => this.serviceUtility.standardCatch());
}
}
/**
* Imports
*/
import environment from 'environment';
import {transient, inject} from 'aurelia-framework';
import {AppStorage} from 'resources/app-storage';
import {Notification} from 'resources/notification';
import {Utility} from 'resources/utility';
import nprogress from 'nprogress';
/**
* Decorators
*/
@transient()
@inject(AppStorage, Notification)
/**
* ServiceUtility Class
* @class
*/
export class ServiceUtility {
/**
* Constructs an instance
* @param appStorage {object} - appStorage instance
* @param notification {object} - notification instance
* @constructor
*/
constructor(appStorage, notification) {
this.appStorage = appStorage;
this.notification = notification;
}
/**
* Sets up the standard configuration for http-client/fetch
* @param config {object} - http-client config object
*/
standardConfiguration(config) {
config
.withDefaults({
headers: {
'Accept': 'application/json',
'X-Requested-With': 'Fetch',
'Authorization': `Bearer ${this.appStorage.get('token')}`
}
})
.withInterceptor({
request: request => { return this.requestInterceptor(request); },
requestError: requestError => { return this.requestErrorInterceptor(requestError); },
response: response => { return this.responseInterceptor(response); },
responseError: responseError => { return this.responseErrorInterceptor(responseError); }
})
.withBaseUrl(environment.apiUrl);
}
/**
* Handles the second promise of the fetch and returns the data after checking it
* @param data {object} - data object from the first fetch promise
* @return {object}
*/
standardThen(data) {
/** Show the data in the console if we're debugging */
if (environment.debug) { console.debug('ServiceUtility.standardThen(data)', Utility.cloneObject(data)); }
/** If it's null then just return an empty object */
if (data === null || data === false) { return {}; }
/** Show messages if there's any coming back from the server */
this.notification.handleServerMessages(data.messages);
/** return the data */
return data;
}
/**
* Handles the initial response from fetch and returns the json or handles http errors
* @param response {object} - the response object
* @return {object}
*/
responseFilter(response) {
/** Show the response in the console if we're debugging */
if (environment.debug) { console.debug('responseFilter(response)', response); }
/** If the status code is ok then we can return the json data */
if (response.status === 200) {
return response.json();
}
/** If we have an auth error we need to redirect to the logout */
if (response.status === 401) {
this.appStorage.remove('token');
window.location = '/#/logout';
return false;
}
/** If the status code is 0 it means the connection failed */
if (response.status === 0) {
this.notification.showError('Request timed out.');
return false;
}
/** If we've gotten this far.... I have no idea what happened. Show a message and return false */
this.notification.showError('Unknown error. Please contact support if this persists. <a href="mailto:[email protected]">Contact Us</a>');
return false;
}
/**
* Catches errors and displays a message. This is usually a connection issue.
* @return {object}
*/
standardCatch() {
this.notification.showError('Unable to connect. Please let us know if this issue persists. <a href="mailto:[email protected]">Contact Us</a>');
return {};
}
/**
* Request interceptor
* @param request {object} - request object
* @return {object}
*/
requestInterceptor(request) {
/** Show progress increments */
nprogress.inc();
/** Debugging */
if (environment.debug) { console.debug('request[' + request.method + '][' + request.url + ']', request); }
/** Return the request */
return request;
}
/**
* Request error interceptor
* @param requestError {object} - request error object
* @return {object}
*/
requestErrorInterceptor(requestError) {
/** Stop the progress bar */
nprogress.done();
/** Debugging */
if (environment.debug) { console.debug('requestError[' + requestError.url + ']', requestError); }
/** Return the request */
return requestError;
}
/**
* Response interceptor
* @param response {object} - request object
* @return {object}
*/
responseInterceptor(response) {
/** Stop the progress bar */
nprogress.done();
/** Debugging */
if (environment.debug) { console.debug('response[' + response.url + ']', response); }
/** Return the response */
return response;
}
/**
* Response error interceptor
* @param responseError {object} - response error object
* @return {object}
*/
responseErrorInterceptor(responseError) {
/** Stop the progress bar */
nprogress.done();
/** Debugging */
if (environment.debug) { console.debug('responseError[' + responseError.url + ']', responseError); }
/** Return the response */
return responseError;
}
}
/**
* Imports
*/
import {HttpClient, json} from 'aurelia-fetch-client';
import {transient, inject} from 'aurelia-framework';
import {ServiceUtility} from 'resources/service-utility';
import 'whatwg-fetch';
/**
* Decorators
*/
@transient()
@inject(HttpClient, ServiceUtility)
/**
* WidgetService Class
* @class
*/
export class WidgetService {
/**
* Constructs an instance
* @param http {object} - http instance object
* @param serviceUtility {object} - serviceUtility instance object
* @constructor
*/
constructor(http, serviceUtility) {
this.serviceUtility = serviceUtility;
http.configure(config => serviceUtility.standardConfiguration(config));
this.http = http;
}
/**
* Gets widgets
* @returns {Promise}
*/
getWidgets() {
return this.http.fetch('widgets')
.then(response => this.serviceUtility.responseFilter(response))
.then(data => this.serviceUtility.standardThen(data))
.catch(() => this.serviceUtility.standardCatch());
}
/**
* Gets a widget
* @param id {Number} - id
* @returns {Promise}
*/
getWidget(id) {
return this.http.fetch(`widgets/${id}`)
.then(response => this.serviceUtility.responseFilter(response))
.then(data => this.serviceUtility.standardThen(data))
.catch(() => this.serviceUtility.standardCatch());
}
/**
* Creates a widget
* @param widget {object} - widget object
* @returns {Promise}
*/
getWidget(widget) {
return this.http.fetch('widgets', { method: 'post', body: json(widget) })
.then(response => this.serviceUtility.responseFilter(response))
.then(data => this.serviceUtility.standardThen(data))
.catch(() => this.serviceUtility.standardCatch());
}
/**
* Update a widget
* @param widget {object} - widget object
* @returns {Promise}
*/
getWidget(widget) {
return this.http.fetch('widgets', { method: 'put', body: json(widget) })
.then(response => this.serviceUtility.responseFilter(response))
.then(data => this.serviceUtility.standardThen(data))
.catch(() => this.serviceUtility.standardCatch());
}
/**
* Deletes a widget
* @param id {Number} - id
* @returns {Promise}
*/
deleteWidget(id) {
return this.http.fetch(`widgets/${id}`, { method: 'delete' })
.then(response => this.serviceUtility.responseFilter(response))
.then(data => this.serviceUtility.standardThen(data))
.catch(() => this.serviceUtility.standardCatch());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment