16

How come in sails you cannot access other controller methods from within another one?

like this.

module.exports = 

   findStore: ->
       # do somthing

   index: ->
      @findStore(); # Error: undefined

Compiled

module.exports = {
  findStore: function() {},
  index: function() {
    return this.findStore(); // Error: undefined
  }
};

If you can't do this, then why not? how else should I be doing this...

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
iConnor
  • 19,997
  • 14
  • 62
  • 97
  • Have you tried just `findStore()` or `this.findStore()`? – Justin Wood Dec 22 '13 at 23:59
  • yes, that's what `@findStore()` is. I have just tried `findStore();`... no luck. – iConnor Dec 23 '13 at 00:02
  • 1
    14 years later, the **exact** answer to your question is because where' you're using `this` is out of your expected scope. if you had done `var fs=findstore: function(){}` and then called `fs` from within the `index:function`, it should have worked . . . I think... The ***practical, real world*** answer is the one below from @nidheeshdas – monsto Jun 02 '15 at 18:13
  • @monsto yeah it would have worked, but its not the same thing so i wouldn't be able to use it outside of that file.... – iConnor Aug 24 '15 at 14:45

7 Answers7

65

You can use sails.controllers.yourControllerName.findStore()

the sails global object has references to almost everything.

nidheeshdas
  • 1,097
  • 1
  • 11
  • 20
  • 20
    Note that `RabbitController` would be referenced as `sails.controllers.rabbit.feed()`. – dbasch Jul 20 '14 at 08:21
  • Awesome solution, worked great for me, note that it is case sensitive, IE Rabbit would not work, rabbit does. – edencorbin Jul 17 '15 at 04:08
  • Also worth a note, you can call blueprint actions via `sails.middleware.blueprints.create(req, res)` – Ricky Boyce Feb 23 '16 at 05:17
  • Also note that if you have a nested controller structure (e.g. controllers -> v2 -> RabbitController.js), using sails.controllers.rabbit.feed() or sails.controllers.v2.rabbit.feed() doesn't work. I had to use a Service as suggested by @lloop – Akinola Olayinka Oct 25 '17 at 04:47
27

One of the best ways to organize your code in Sails, at least for me and my team, has been to have all the real business logic in Services (/api/services). Those objects can be accessed globally from any controller.

Also, a good practice is working with promises in services (as Sails use them on the model methods)

Just create a Store service (StoreService.js), with your code:

module.exports = {
  findStore: function(storeId) {
    // here you call your models, add object security validation, etc...
    return Store.findOne(storeId);
  }
};

Your Controllers should handle all that is related to requests, calling services, and returning apropriate responses.

For example, in you example, the controller could have this:

module.exports = {
  index: function(req, res) {
    if(req.param('id')) {
      StoreService.findStore(req.param('id'))
        .then(res.ok)
        .catch(res.serverError);
    } else {
      res.badRequest('Missing Store id');
    }
  },
  findStore: function(req, res) {
    if(req.param('id')) {
      StoreService.findStore(req.param('id'))
        .then(res.ok)
        .catch(res.serverError);
    } else {
      res.badRequest('Missing Store id');
    }
  },
};

That way, you have really simple controllers, and all business logic is managed by services.

Luis Lobo Borobia
  • 994
  • 11
  • 25
  • 2
    The most popular post here literally answers the question, but this post right here is an actual solution... it answers the question with an eye towards future organization. It's the X solution to the really subtle XY Problem that exists here. – monsto Oct 27 '15 at 18:56
  • The code I shared should nowadays be converted to `async/await` but it is still relevant. – Luis Lobo Borobia Apr 02 '20 at 22:48
7

Having the same problem for last few hours. I used the api/services folder. It may not be exactly what you need but it is an option. A good explanation is here. What services would one add to the api/services folder in sails.js

Community
  • 1
  • 1
lloop
  • 98
  • 1
  • 5
2

It's slightly annoying when you're just trying to build something quickly, but in the long run it forces good code organization practice (by making it harder to shove all business logic into a controller).

Josh Liptzin
  • 746
  • 5
  • 12
1

I would like to suggest a solution that works but not the best possible way to do it. We can use bind function to bind the context with the calling source as shown below :

generateUrl is present in the Controller A

function generateUrl(){
  return 'www.google.com';
}

get URL is another method in Controller A

getURL(){
  A.generateURL.bind(A.generateURL) //func call with optional arg
}

I hope this helps!

saurabh
  • 601
  • 6
  • 8
0

A more elegant way to solve this problem is using the keyword this before the function name.

Example:

one: function() {
   console.log('First Function');
},

two: function() {
   // call the function one in the same controller
   this.one();
}
Marcos Mendes
  • 1,091
  • 1
  • 8
  • 15
0

You can do something like this:

//ArticleController
module.exports = {
  findStore: async () => {
    return await findStoreFunc(req.param('id'));
  },
  index: async () => {
    ...
    return await findStoreFunc(id);
  }
};

const findStoreFunc = async (id) => {...}

And to use the function from another controller:

const ArticleController = require('./ArticleController');
//CustomerController
module.exports = {
  index: async () => {
    ...
    let article = await ArticleController.findStore(id);
    ...
  }
};
Marcell
  • 101
  • 2