restangular
今までこんな感じで$resourceをラップするだけのサービスを使って、各Controllerで使いまわしてたけど、いくつか問題点が。
angular.module('models', ['ngResource'])
.factory('User', ['$resource', function ($resource) {
return $resource(
'/api/:Version/users/:Id',
{ Id: '@Id', Version: '@Version' },
{
'update': { method: 'PUT' },
'query': { method: 'GET', isArray: false, cache: true }
}
);
}]);
- apiから返ってくる形式がちょっと違ったりすると、Controllerでデータを操作して$scopeに格納しなくてはないけない場面が多かった
- $resourceだけだとmodel同士のリレーションとか階層とか表現しづらいので、またControllerに処理を書いてしまう。
- apiごとの共通の設定とか個別の設定とか渡しにくいので、引数を増やして対応してしまって(古いバージョンのapiを呼んでるところとか)可読性やメンテナンス性が落ちていく。
などなど、色々と悩ましいことに。
Angularを書いてて思うのは、Controllerが太るけど、どうやって処理を移譲するのがいいんだろう・・。というのはよくある。
なので偶然見つけたrestangularを試してみた
angular.module('app', [
'restangular'
]);
angular.module('app').config(function(RestangularProvider) {
// そのまんまだけどproviderにbaseのurlを渡しておける。
RestangularProvider.setBaseUrl('/api/v3');
// apiの返り値を共通化する処理などを書ける
RestangularProvider.setResponseExtractor(function(response, operation) {
return response.results;
});
});
// 共通処理が多いので、RestangularをControllerで直接操作しないで、serviceでラップする
angular.module('services.rest', ['restangular']).
factory('UserSvc', function(Restangular) {
return Restangular.all('users');
}).
factory('IssueSvc', function(Restangular) {
// api毎に異なる設定も可能
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v2');
}).all('issues');
});
// GET http://localhost:8080/api/v3/usersが呼ばれて、{page:0, results: [aaa,bbb,ccc]} 結果のresultsの部分がusersに格納される
UserSvc.getList().then(
function(users) {
$scope.users = users;
}
);
とりあえず$resourceの置き換えには困らなそう。
なにか便利な機能があったら追記していく。
イメージしやすいと思うのでこんなふうに使ってますを貼っておく(汚いけど)
angular.module('services.rest', ['restangular'])
.config(['RestangularProvider', function(RestangularProvider) {
RestangularProvider.setBaseUrl('/api/v3');
RestangularProvider.setRequestInterceptor(function(elem, operation) {
if (operation === 'remove') {
return undefined;
}
return elem;
});
RestangularProvider.setResponseExtractor(function(data, operation, what/*, url, response*/) {
if (what === 'search' || what === 'feature' || what === 'conversations' || what === 'simple_search') {
return data;
}
if (data instanceof Array) {
return data;
}
if (data.result && data.result.users) {
return data.result.users;
}
if (data.result && data.result.posts) {
return data.result.posts;
}
if (data.result && data.result.notifications) {
return data.result;
}
return data.results || data.result || data;
});
RestangularProvider.setDefaultHeaders({ 'X-Requested-With': 'XMLHttpRequest' });
}
])
.factory('AuthSvc', function(Restangular) {
var svc = Restangular.all('auth');
svc.addRestangularMethod('login', 'post', 'signin');
svc.addRestangularMethod('signup', 'post', 'signup');
svc.addRestangularMethod('register', 'post', 'register');
svc.addRestangularMethod('changePassword', 'post', 'changePassword');
svc.addRestangularMethod('inactivate', 'post', 'inactivate');
return svc;
})
.factory('PasswordSvc', function(Restangular) {
var svc = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v3/auth');
}).all('password');
svc.addRestangularMethod('set', 'post', 'set');
svc.addRestangularMethod('change', 'post', 'change');
svc.addRestangularMethod('getKey', 'post', 'reset');
return svc;
})
.factory('BankSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api');
}).all('accounts');
})
.factory('ImageUploadSvc', function(Restangular) {
var svc = Restangular.all('images');
svc.addRestangularMethod('getUploadUrl', 'get', 'get_upload_url');
return svc;
})
.factory('MixSvc', function(Restangular) {
var svc = Restangular.all('common');
svc.addRestangularMethod('search', 'get', 'search');
svc.addRestangularMethod('activities', 'get', 'activities');
return svc;
})
.factory('CategorySvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('categories');
})
.factory('UserSvc', function(Restangular) {
var svc = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setOnElemRestangularized(function(elem) {
elem.addRestangularMethod('getTopicList', 'getList', 'topics');
elem.addRestangularMethod('postTopic', 'post', 'topics');
elem.addRestangularMethod('connectTags', 'post', 'tags');
elem.addRestangularMethod('getTags', 'get', 'tags');
elem.addRestangularMethod('getSlug', 'get', 'slug');
return elem;
});
}).all('users');
return svc;
})
.factory('MeSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v3/profiles');
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('me');
})
.factory('IssueSvc', function(Restangular) {
var svc = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setOnElemRestangularized(function(elem) {
elem.addRestangularMethod('publish', 'put', 'publish');
elem.addRestangularMethod('reuse', 'post', 'reuse');
elem.addRestangularMethod('start', 'put', 'start');
elem.addRestangularMethod('end', 'put', 'end');
return elem;
});
}).all('issues');
svc.addRestangularMethod('search', 'get', 'search');
svc.addRestangularMethod('getMyList', 'getList', '', {'related_me': 1});
return svc;
})
.factory('ProposalSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setOnElemRestangularized(function(elem) {
elem.addRestangularMethod('getCommentList', 'getList', 'comments');
elem.addRestangularMethod('comment', 'post', 'comments');
elem.addRestangularMethod('approve', 'put', 'approve');
elem.addRestangularMethod('reject', 'put', 'reject');
elem.addRestangularMethod('abort', 'put', 'abort');
elem.addRestangularMethod('decide', 'put', 'decide');
return elem;
});
}).all('proposals');
})
.factory('CardSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api');
RestangularConfigurer.setOnElemRestangularized(function(elem) {
elem.addRestangularMethod('pay', 'post', 'pay');
return elem;
});
}).all('payments');
})
.factory('TopicSvc', function(Restangular) {
var svc = Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setOnElemRestangularized(function(elem) {
elem.addRestangularMethod('like', 'post', 'like');
elem.addRestangularMethod('unlike', 'post', 'unlike');
elem.addRestangularMethod('likedUsers', 'get', 'likes');
return elem;
});
}).all('topics');
svc.addRestangularMethod('search', 'get', 'search');
svc.addRestangularMethod('simpleSearch', 'get', 'simple_search');
svc.addRestangularMethod('featureList', 'get', 'feature');
svc.addRestangularMethod('getFavList', 'getList', '', {'liked_by_me': 1, 'page_size': 50});
return svc;
})
.factory('ConversationSvc', function(Restangular) {
var svc = Restangular.all('conversations');
svc.addRestangularMethod('getPage', 'get');
return svc;
})
.factory('FacebookSvc', function(Restangular) {
var svc = Restangular.all('facebook');
svc.addRestangularMethod('send', 'post', 'post');
return svc;
})
.factory('TagsMapSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v3/tags');
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('map');
})
.factory('ContactSvc', function(Restangular) {
return Restangular.all('contacts');
})
.factory('NewsSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api');
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('news_feed');
})
.factory('BlogSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api');
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('posts');
})
.factory('SelectionSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('selections');
})
.factory('IssueSelectionSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('issue_selections');
})
.factory('FaqSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/templates');
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('faq_ja');
})
.factory('MessageSvc', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setDefaultHttpFields({cache: true});
}).all('notifications');
});
ついでに
当社ではAngular.jsでサービスを開発したいエンジニアを募集しています!とか、書いちゃいけないのかな。