Skip to content

Instantly share code, notes, and snippets.

@billjohnston
Created January 12, 2015 17:14
Show Gist options
  • Save billjohnston/3d6bb61fdc7542c19b03 to your computer and use it in GitHub Desktop.
Save billjohnston/3d6bb61fdc7542c19b03 to your computer and use it in GitHub Desktop.
Old version of Resource Directives
var resourceDirectives = angular.module('resourceDirectives', ['ui.bootstrap']);
var head = angular.element(document.querySelector('head'));
head.prepend('<style> .rd_loading .rd_text{visibility: hidden;} .rd_loading .rd_spinner{display: block;} .rd_loading .rd_spinner{display: block;} .rd_spinner{display: none;} .rd_spinner .fa{vertical-align: top;} .rd_position-relative{position: relative;} .rd_absolute-center{position: absolute; margin: auto; text-align: center; top:0; left:0; right:0; bottom:0; width: 1em; height: 1em;}</style>');
resourceDirectives.factory('interpolate_url', [
'resourceDirectivesConfig', '$location',
function(resourceDirectivesConfig, $location){
return function(url, params, data){
var match = url.match(/(\:[a-zA-Z_-]*)/g);
var strip_slashes;
var sub_with = params || data || {};
_.each(match, function(url_sub){
var regex = new RegExp(url_sub);
var param_key = url_sub.replace(':', '');
if(param_key in sub_with){
url = url.replace(regex, sub_with[param_key]);
delete sub_with[param_key];
}
else{
url = url.replace(regex, '');
}
});
url = url.replace(/(\/{2,})/, '/');
try{
strip_slashes = resourceDirectivesConfig.settings.strip_slashes;
}
catch(e){
strip_slashes = true;
}
if(strip_slashes){
url = url.replace(/\/+$/i, "");
}
if(params && !_.isEmpty(params)){
var parts = [];
for (var i in params) {
if (params.hasOwnProperty(i)) {
parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(params[i]));
}
}
url = url + '?' + parts.join("&");
}
return url;
};
}]);
resourceDirectives.factory('parse_pse', ['$rootScope', function($rootScope){
return function(parse_this){
if ($rootScope.$$phase) {
$rootScope.$evalAsync(parse_this);
} else {
$rootScope.$apply(parse_this);
}
};
}]);
var trim_split = function(string_var){
return string_var.replace(/^\s*|\s*$/g,'').split(/\s*,\s*/);
};
var clean_boolean_string = function(str){
return (str === 'true' || str === 'yes') || ((str === 'false' || str === 'no') ? false : str);
};
resourceDirectives.factory(
'ajax',
[
'$rootScope',
'$parse',
'resourceDirectivesConfig',
'$http',
'$modal',
'interpolate_url',
'parse_pse',
function($rootScope, $parse, resourceDirectivesConfig, $http, $modal, interpolate_url, parse_pse){
var reset_loading = function(loading){
if(_.isString(loading)){
$rootScope[loading] = false;
}
else{
loading.loading = false;
}
};
var error_callbacks = function(form, loading){
var modal_alert = function(data, status){
var print_response = '';
if(_.isObject(data)){
_.each(data, function(val, key){
print_response += '<li>' + key + ': ' + val + '</li>';
});
}
else if(_.isArray(data)){
_.each(data, function(val, key){
print_response += '<li>' + val + '</li>';
});
}
else if(_.isString(data)){
print_response = data;
}
else{
if(data){
print_response = data;
}
else{
print_response = 'Nothing returned';
}
}
$rootScope.error_modal = $modal.open({
windowClass: 'alert-modal',
template:
'<div class="alert alert-danger alert-dismissible margin-bottom-0" role="alert">' +
'<button class="close" ng-click="error_modal.dismiss(\'cancel\')">' +
'<span>&times;</span>' +
'</button>' +
'<h4 class="margin-bottom-0">' +
print_response +
'</h4>' +
'</div>'
});
reset_loading(loading);
};
if(form){
if(status == 400){
return function(data, status){
form.$error = data;
reset_loading(loading);
};
}
else{
return modal_alert;
}
}
else{
return modal_alert;
}
};
return function(manager, model_name, method_obj, params, form, pse, alias, exclusive_field){
var method, url, base_url, loading;
var alias_or_model_name = alias || model_name;
if(method_obj){ //false = manager
loading = method_obj;
method_obj.loading = true;
base_url = resourceDirectivesConfig.settings.base_url +
method_obj._resource.url;
var contains_this = [];
_.each(pse, function(value, key){
if(value && value.indexOf('this') != -1){
contains_this.push(key);
}
});
if(contains_this.length){
var resource_name = method_obj._resource.alias || method_obj._resource.name;
var method_obj_index = _.findIndex(
$rootScope[resource_name],
{$$hashKey: method_obj.$$hashKey}
);
_.each(contains_this, function(key){
pse[key] = pse[key].replace(
'this',
resource_name + '[' + method_obj_index + ']'
);
});
}
}
else{
loading = alias_or_model_name + '_loading';
$rootScope[loading] = true;
var model_config = resourceDirectivesConfig.models[model_name];
model_config.name = model_name;
model_config.alias = alias;
base_url = resourceDirectivesConfig.settings.base_url +
resourceDirectivesConfig.models[model_name].url;
}
if(pse.pre){
parse_pse(pse.pre);
}
switch(manager){
case 'rdToggle':
case 'rdUpdate':
method = resourceDirectivesConfig.settings.update_method.toLowerCase() || 'post';
args = [interpolate_url(base_url, null, params), params];
break;
case 'rdCreate':
method = 'post';
args = [interpolate_url(base_url, null, params), params];
break;
case 'rdQuery':
case 'rdQueryOnce':
$rootScope[model_name] = undefined;
method = 'get';
args = [interpolate_url(base_url, params)];
break;
case 'rdGetSingle':
case 'rdGet':
method = 'get';
args = [interpolate_url(base_url, params)];
break;
}
var ajax_call = $http[method].apply(this, args)
.success(function(response){
var objects = angular.isArray(response) ? response : [response];
if(!method_obj){
objects = _.map(objects, function(map_response){
return _.merge(
map_response,
{_resource: model_config, _errors: {}}
);
});
}
switch(manager){
case 'rdGet':
if (alias_or_model_name in $rootScope){
_.forEach(response, function(each_resp){
var found_index = _.findIndex(
$rootScope[alias_or_model_name],
{id: each_resp.id}
);
if(found_index != -1){
$rootScope[alias_or_model_name][found_index] = each_resp;
}
else{
$rootScope[alias_or_model_name].push(each_resp);
}
});
}
else{
$rootScope[alias_or_model_name] = response;
}
break;
case 'rdGetSingle':
if(_.isArray(response)){
$rootScope[alias_or_model_name] = response[0];
}
else{
$rootScope[alias_or_model_name] = response;
}
break;
case 'rdCreate':
if (alias_or_model_name in $rootScope){
$rootScope[alias_or_model_name].push(response);
}
else{
$rootScope[alias_or_model_name] = [response];
}
break;
case 'rdQuery':
case 'rdQueryOnce':
$rootScope[alias_or_model_name] = objects;
break;
case 'rdUpdate':
if(!method_obj){
var found_index = _.findIndex(
$rootScope[alias_or_model_name],
{id: response.id}
);
if(found_index != -1){
$rootScope[alias_or_model_name][found_index] = response;
}
else{
$rootScope[alias_or_model_name].push(response);
}
}
else{
if(exclusive_field){
$rootScope[alias_or_model_name] = _.map(
$rootScope[alias_or_model_name], function(obj){
obj[exclusive_field] = !response[exclusive_field];
return obj;
}
);
}
method_obj = _.assign(method_obj, response);
}
break;
}
reset_loading(loading);
})
.error(error_callbacks(form, loading))
.then(function(response){
_.defer(function(){
if(response.status == 200 && pse.success){
parse_pse(pse.success);
}
else if(pse.error){
parse_pse(pse.error);
}
});
});
};
}]);
// Button loading directive
resourceDirectives.directive('loading', function() {
return {
restrict: 'A',
transclude: true,
scope: {
loading: '='
},
template:
"<div class='rd_position-relative' ng-class='{rd_loading: loading}'>" +
"<span class='rd_absolute-center rd_spinner'>" +
"<span class='fa fa-spin fa-circle-o-notch'></span>" +
"</span>" +
"<span class='rd_text' ng-transclude></span>" +
"</div>"
};
});
// Create directive for each model manager/method
var model_managers = ['rdGet', 'rdGetSingle', 'rdQuery', 'rdQueryOnce', 'rdCreate'];
var model_methods = ['rdUpdate', 'rdToggle'];
var combined = _.union(model_managers, model_methods);
_.each(combined, function(directive_attr){
resourceDirectives.directive(directive_attr, ['$rootScope', 'ajax', '$parse', function($rootScope, ajax, $parse) {
return {
restrict: 'A',
require: '^?form',
link: function(scope, element, attrs, form){
var method = (model_methods.indexOf(directive_attr) != -1 && !('rdManager' in attrs));
var bind_event;
var params = {};
switch(element.prop('tagName')){
case 'FORM':
bind_event = 'submit';
break;
case 'BUTTON':
case 'A':
bind_event = 'click';
break;
case 'LI':
// If its a tab (ui-bootstrap)
if(element.parent().hasClass("nav-[[type || 'tabs']]")){
// This parts a little janky
bind_event = 'select';
var elem = element[0];
var elindex = Array.prototype.indexOf.call(
elem.parentNode.childNodes,
elem
);
var modified_index = (elindex - 1) / 2;
scope.$parent.tabs[modified_index].$watch('active', function(active){
if(active){
element.triggerHandler('select');
}
});
}
break;
}
var run_resource_fn = function(event){
var method_obj = method ? scope[attrs[directive_attr]] : false;
var model_name = method ? method_obj._resource.name : attrs[directive_attr];
var params;
if(directive_attr == 'rdQueryOnce'){
element.off(bind_event);
}
if(element.prop('tagName') == 'FORM'){
params = {};
_.each(['input', 'select', 'textarea'], function(tag){
_.each(element.find(tag), function(field){
var ng_field = angular.element(field);
var field_name = ng_field.attr('name');
if(!_.isEmpty(field_name)){
if(ng_field.attr('type') == 'radio'){
if(ng_field.prop('checked')){
params[field_name] = clean_boolean_string(ng_field.val());
}
}
else{
params[field_name] = clean_boolean_string(ng_field.val());
}
}
});
});
}
//if any attribute contains rd-val-
else if(_.any(attrs, function(value, attr){return attr.indexOf('rdVal') != -1;})){
params = {};
_.each(attrs, function(value, attr){
if(attr.indexOf('rdVal') != -1){
// camelcase to underscores
var param_key = attr.replace(
/([A-Z])/g,
function($1){
return "_" + $1.toLowerCase();
}
).replace('rd_val_', '');
attrs[attr] = clean_boolean_string(attrs[attr]);
params[param_key] = attrs[attr];
}
});
}
else if(attrs.rdFields && method){
var params_from_obj = method_obj;
var fields_array = trim_split(attrs.rdFields);
if(directive_attr == 'rdToggle'){
_.each(fields_array, function(field){
params_from_obj[field] = !params_from_obj[field];
});
}
params = _.pick(params_from_obj, fields_array);
}
else if(method){
params = _.omit(method_obj, ['_resource', '_errors']);
}
var alias = attrs.rdAlias;
_.each(['rdPre', 'rdSuccess', 'rdError'], function(pse){
if(attrs[pse]){
var fn = $parse(attrs[pse], null, true);
attrs[pse] = function() {
fn(scope, {$event: event});
};
}
});
ajax(
directive_attr,
model_name,
method_obj,
params,
form,
{
pre: attrs.rdPre,
success: attrs.rdSuccess,
error: attrs.rdError
},
alias,
attrs.rdExclusiveField
);
};
if(bind_event){
element.on(bind_event, run_resource_fn);
}
else{
run_resource_fn();
}
}
};
}]);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment