Last active
July 10, 2016 19:45
-
-
Save TylerJPresley/b23ffdddabdf32e5ded12e6247597fc1 to your computer and use it in GitHub Desktop.
Services Example // Aurelia (CLI/RequireJS)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default { | |
debug: true, | |
testing: true, | |
apiUrl: 'http://localhost:3000/' | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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