Last active
May 6, 2021 10:22
-
-
Save Sander-Kastelein/53460dc67352decd6c1b to your computer and use it in GitHub Desktop.
just a little hack to make it work for me
This file contains 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
/*! angular-sails-bind - v1.0.5 - 2014-05-20 | |
* https://github.com/diegopamio/angular-sails-bind | |
* Copyright (c) 2014 Diego Pamio; Licensed MIT */ | |
/*global angular:false */ | |
/*global io:false */ | |
/** | |
* Angular service to handle SailsJs resources. | |
* | |
* @author Diego Pamio - Github: diegopamio | |
* @return {object} Object of methods | |
*/ | |
function parseRecursive(object){ | |
if(typeof object === 'string'){ | |
try{ | |
object = JSON.parse(object); | |
}catch(e){ | |
} | |
}else if(typeof object === 'object'){ | |
for(k in object){ | |
object[k] = parseRecursive(object[k]); | |
} | |
} | |
return object; | |
} | |
var app = angular.module("ngSailsBind", []); | |
app.factory('$sailsBind', [ | |
'$q', "$rootScope", "$timeout", "$log", | |
function ($q, $rootScope, $timeout, $log) { | |
'use strict'; | |
/** | |
* This function basically does three things: | |
* 1. Creates an array inside $scope and fills it with a socket get call to backend pointed by the | |
* resourceName endpoint. | |
* 2. Setup the socket's incoming messages (created, updated and destroyed) to update the model. | |
* 3. Setup watchers to the model to persist the changes via socket to the backend. | |
* @param resourceName {string} is the name of the resource in the backend to bind, can have prefix route. | |
* @param $scope {object} is the scope where to attach the bounded model. | |
* @param subset {json} is the query parameters where you can filter and sort your initial model fill. | |
* check http://beta.sailsjs.org/#!documentation/reference/Blueprints/FindRecords.html to see | |
* what you can send. | |
*/ | |
var bind = function (resourceName, $scope, subset) { | |
var prefix = resourceName.split('/'); | |
if(prefix.length>1) { | |
resourceName = prefix.splice(prefix.length - 1, 1); | |
prefix = prefix.join('/') + '/'; | |
}else{ | |
prefix = ''; | |
} | |
var defer_bind = new $q.defer(); | |
//1. Get the initial data into the newly created model. | |
var requestEnded = _get("/" + prefix + resourceName, subset); | |
requestEnded.then(function (data) { | |
if ( ! Array.isArray(data) ) { | |
data=[data]; | |
} | |
$scope[resourceName + "s"] = data; | |
addCollectionWatchersToSubitemsOf(data, $scope, resourceName, prefix); | |
init(); | |
defer_bind.resolve(); | |
}); | |
//2. Hook the socket events to update the model. | |
function onMessage(message) { | |
var elements = $scope[resourceName + "s"], | |
actions = { | |
created: function () { | |
$scope[resourceName + "s"].push(message.data); | |
return true; | |
}, | |
updated: function () { | |
var updatedElement = elements.find( | |
function (element) { | |
return message.id == element.id; | |
} | |
); | |
if (updatedElement) { | |
message.data = parseRecursive(message.data); | |
angular.extend(updatedElement, message.data); | |
return true; | |
} | |
return false; | |
}, | |
destroyed: function () { | |
var deletedElement = elements.find( | |
function (element) { | |
return message.id == element.id; | |
} | |
); | |
if (deletedElement) { | |
elements.splice(elements.indexOf(deletedElement), 1); | |
return true; | |
} | |
return false; | |
} | |
}; | |
if (actions[message.verb]) { | |
if (actions[message.verb]()) | |
$timeout(function(){ $scope.$apply(); }); | |
} else { | |
$log.log("Unknown action »"+message.verb+"«"); | |
} | |
} | |
io.socket.on(resourceName, onMessage); | |
$scope.$on(resourceName, function (event, message) { | |
if ($scope.$id!=message.scope) | |
onMessage(message); | |
}); | |
//3. Watch the model for changes and send them to the backend using socket. | |
function init() { | |
$scope.$watchCollection(resourceName + "s", function (newValues, oldValues) { | |
var addedElements, removedElements; | |
newValues = newValues || []; | |
oldValues = oldValues || []; | |
addedElements = diff(newValues, oldValues); | |
removedElements = diff(oldValues, newValues); | |
removedElements.forEach(function (item) { | |
_get("/" + prefix + resourceName + "?id=" + item.id ).then(function (itemIsOnBackend) { | |
if (itemIsOnBackend && !itemIsOnBackend.error) { | |
$rootScope.$broadcast(resourceName, { id: item.id, verb: 'destroyed', scope: $scope.$id }); | |
io.socket.delete("/" + prefix + resourceName + '/destroy/' + item.id); | |
} | |
}); | |
}); | |
addedElements.forEach(function (item) { | |
if (!item.id) { //if is a brand new item w/o id from the database | |
io.socket.put("/" + prefix + resourceName + '/create/', item, function (data) { | |
_get("/" + prefix + resourceName + "/" + data.id ).then(function (newData) { | |
angular.extend(item, newData); | |
$rootScope.$broadcast(resourceName, { id: item.id, verb: 'created', scope: $scope.$id, data: angular.copy(item) }); | |
}); | |
}); | |
} | |
}); | |
// Add Watchers to each added element | |
addCollectionWatchersToSubitemsOf(addedElements, $scope, resourceName,prefix); | |
}); | |
}; | |
return defer_bind.promise; | |
}; | |
/** | |
* Adds watchers to each item in the model to perform the "post" when something there changes. | |
* @param model is the model to watch | |
* @param scope is the scope where the model belongs to | |
* @param resourceName is the "singular" version of the model as used by sailsjs | |
*/ | |
var addCollectionWatchersToSubitemsOf = function (model, scope, resourceName, prefix) { | |
model.forEach(function (item) { | |
scope.$watchCollection( | |
resourceName + 's' + '[' + scope[resourceName + "s"].indexOf(item) + ']', | |
function (newValue, oldValue) { | |
if (oldValue && newValue) { | |
if (!angular.equals(oldValue, newValue) && // is in the database and is not new | |
oldValue.id == newValue.id && //not a shift | |
oldValue.updatedAt === newValue.updatedAt) { //is not an update FROM backend | |
$rootScope.$broadcast(resourceName, { id: oldValue.id, verb: 'updated', scope: scope.$id, data: angular.extend(angular.copy(newValue),{ updatedAt: (new Date()).toISOString() }) }); | |
io.socket.post("/" + prefix + resourceName + '/update/' + oldValue.id, | |
angular.copy(newValue)); | |
} | |
} | |
} | |
); | |
}); | |
}; | |
/** | |
* Internal "get" function inherited. it does the standard request, but it also returns a promise instead | |
* of calling the callback. | |
* | |
* @param url url of the request. | |
* @param additional extra info (usually a query restriction) | |
* @returns {Deferred.promise|*} | |
* @private | |
*/ | |
var _get = function (url, additional) { | |
var defer = new $q.defer(); | |
additional = additional || {}; | |
io.socket.get(url, additional, function (res) { | |
$rootScope.$apply(defer.resolve(res)); | |
}); | |
return defer.promise; | |
}; | |
return { | |
bind: bind | |
}; | |
} | |
]); | |
if (!Array.prototype.find) { | |
Object.defineProperty(Array.prototype, 'find', { | |
enumerable: false, | |
configurable: true, | |
writable: true, | |
value: function(predicate) { | |
if (this == null) { | |
throw new TypeError('Array.prototype.find called on null or undefined'); | |
} | |
if (typeof predicate !== 'function') { | |
throw new TypeError('predicate must be a function'); | |
} | |
var list = Object(this); | |
var length = list.length >>> 0; | |
var thisArg = arguments[1]; | |
var value; | |
for (var i = 0; i < length; i++) { | |
if (i in list) { | |
value = list[i]; | |
if (predicate.call(thisArg, value, i, list)) { | |
return value; | |
} | |
} | |
} | |
return undefined; | |
} | |
}); | |
} | |
if(!Array.isArray) { | |
Array.isArray = function(arg) { | |
return Object.prototype.toString.call(arg) === '[object Array]'; | |
}; | |
} | |
function diff(arr1, arr2) { | |
return arr1.filter(function (i) { | |
return arr2.indexOf(i) < 0; | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment