diff --git a/lib/services.js b/lib/services.js
index 033f71e..f986f77 100644
--- a/lib/services.js
+++ b/lib/services.js
@@ -35,7 +35,117 @@ function describeModels(app) {
return;
}
+ // The URL of prototype methods include sharedCtor parameters like ":id"
+ // Because all $resource methods are static (non-prototype) in ngResource,
+ // the sharedCtor parameters should be added to the parameters
+ // of prototype methods.
+ c.methods.forEach(function fixArgsOfPrototypeMethods(method) {
+ var ctor = method.restClass.ctor;
+ if (!ctor || method.sharedMethod.isStatic) return;
+ method.accepts = ctor.accepts.concat(method.accepts);
+ });
+
result[name] = c;
});
+
+ buildScopes(result);
+
return result;
}
+
+var SCOPE_METHOD_REGEX = /^prototype.__([^_]+)__(.+)$/;
+
+function buildScopes(models) {
+ for (var modelName in models) {
+ buildScopesOfModel(models, modelName);
+ }
+}
+
+function buildScopesOfModel(models, modelName) {
+ var modelClass = models[modelName];
+
+ modelClass.scopes = {};
+ modelClass.methods.forEach(function(method) {
+ buildScopeMethod(models, modelName, method);
+ });
+
+ return modelClass;
+}
+
+// reverse-engineer scope method
+// defined by loopback-datasource-juggler/lib/scope.js
+function buildScopeMethod(models, modelName, method) {
+ var modelClass = models[modelName];
+ var match = method.name.match(SCOPE_METHOD_REGEX);
+ if (!match) return;
+
+ var op = match[1];
+ var scopeName = match[2];
+ var modelPrototype = modelClass.sharedClass.ctor.prototype;
+ var targetClass = modelPrototype[scopeName]._targetClass;
+
+ if (modelClass.scopes[scopeName] === undefined) {
+ if (!targetClass) {
+ console.error(
+ 'Warning: scope %s.%s is missing _targetClass property.' +
+ '\nThe Angular code for this scope won\'t be generated.' +
+ '\nPlease upgrade to the latest version of' +
+ '\nloopback-datasource-juggler to fix the problem.',
+ modelName, scopeName);
+ modelClass.scopes[scopeName] = null;
+ return;
+ }
+
+ if (!findModelByName(models, targetClass)) {
+ console.error(
+ 'Warning: scope %s.%s targets class %j, which is not exposed ' +
+ '\nvia remoting. The Angular code for this scope won\'t be generated.',
+ modelName, scopeName, targetClass);
+ modelClass.scopes[scopeName] = null;
+ return;
+ }
+
+ modelClass.scopes[scopeName] = {
+ methods: {},
+ targetClass: targetClass
+ };
+ } else if (modelClass.scopes[scopeName] === null) {
+ // Skip the scope, the warning was already reported
+ return;
+ }
+
+ var apiName = scopeName;
+ if (op == 'get') {
+ // no-op, create the scope accessor
+ } else if (op == 'delete') {
+ apiName += '.destroyAll';
+ } else {
+ apiName += '.' + op;
+ }
+
+ method.deprecated = 'Use ' + modelName + '.' + apiName + '() instead.';
+
+ // build a reverse record to be used in ngResource
+ // Product.__find__categories -> Category.::find::product::categories
+ var reverseName = '::' + op + '::' + modelName + '::' + scopeName;
+
+ var reverseMethod = Object.create(method);
+ reverseMethod.name = reverseName;
+ delete reverseMethod.deprecated;
+ reverseMethod.internal = 'Use ' + modelName + '.' + apiName + '() instead.';
+
+ var reverseModel = findModelByName(models, targetClass);
+ reverseModel.methods.push(reverseMethod);
+
+ var scopeMethod = Object.create(method);
+ scopeMethod.name = reverseName;
+ delete scopeMethod.deprecated;
+ modelClass.scopes[scopeName].methods[apiName] = scopeMethod;
+}
+
+function findModelByName(models, name) {
+ for (var n in models) {
+ if (n.toLowerCase() == name.toLowerCase())
+ return models[n];
+ }
+}
diff --git a/lib/services.template b/lib/services.template
index 9fc1ec9..177c23a 100644
--- a/lib/services.template
+++ b/lib/services.template
@@ -42,8 +42,8 @@ var module = angular.module(<%-: moduleName | q %>, ['ngResource']);
*/
module.factory(
<%-: modelName | q %>,
- ['LoopBackResource', 'LoopBackAuth', function(Resource, LoopBackAuth) {
- return Resource(
+ ['LoopBackResource', 'LoopBackAuth', '$injector', function(Resource, LoopBackAuth, $injector) {
+ var R = Resource(
urlBase + <%-: meta.ctor.getFullPath() | q %>,
<% /*
Constructor arguments are hardcoded for now.
@@ -54,100 +54,7 @@ module.factory(
<% meta.methods.forEach(function(action) {
var methodName = action.name.split('.').join('$');
-%>
- /**
- * @ngdoc method
- * @name lbServices.<%- modelName %>#<%- methodName %>
- * @methodOf lbServices.<%- modelName %>
- *
- * @description
- *
-<% if (!action.description) {
-action.description = '\n' +
- '(The remote method definition does not provide any description.)\n' +
- '';
-} -%>
- * <%-: action.description | replace:/\n/g, '\n * ' %>
- *
-<%
-var params = action.accepts;
-var postData;
-if (action.getHttpMethod() == 'POST' || action.getHttpMethod() == 'PUT') {
- params = params.filter(function(arg) {
- return arg.http && (arg.http.source == 'query' || arg.http.source == 'path');
- });
- postData = action.accepts.filter(function(arg) {
- return params.indexOf(arg) == -1;
- });
-}
--%>
- * @param {Object=} parameters Request parameters.
-<% if (params.length == 0) { -%>
- *
- * This method does not accept any parameters.
- * Supply an empty object or omit this argument altogether.
-<% } else { params.forEach(function(arg) { -%>
- *
- * - `<%- arg.arg %>` – `{<%- getJsDocType(arg) %>}` - <%-
-(arg.description || '').replace(/\n/g, '\n * ') %>
-<% }); } -%>
-<% if (modelName === 'User' && methodName === 'login') { -%>
- *
- * - `rememberMe` - `boolean` - Whether the authentication credentials
- * should be remembered in localStorage across app/browser restarts.
- * Default: `true`.
-<% } -%>
-<% if (postData) { -%>
- *
- * @param {Object} postData Request data.
-<% if (postData.length == 0) { -%>
- *
- * This method does not accept any data. Supply an empty object.
-<% } else if (postData.length == 1 && postData[0].http &&
- postData[0].http.source == 'body') { -%>
- *
- * This method expects a subset of model properties as request parameters.
-<% } else {
-postData.forEach(function(arg) { -%>
- *
- * - `<%- arg.arg %>` – `{<%- getJsDocType(arg) %>}` - <%-
-(arg.description || '').replace(/\n/g, '\n * ') %>
-<% });
- }
-} -%>
- *
-<% var returnType = action.isReturningArray() ? 'Array.