1

I'm trying to create a fluent-like API for a JavaScript project.

Currently I have something like this:

function Foo(a, b) {
   this.a = a;
   this.b = b;
   this.Set = function(attr, val) {
      this[attr] = val;
      return this;
   },
   this.refresh = function() {
      //do something
   }
};

This allows me to write the following:

var foo = new Foo("Hello", "World");
// foo = { a : "Hello", b : "World" }
foo.Set("c", "!");
// foo = { a : "Hello", b : "World", c : "!" }
foo.Set(...);
//...

The benefit of writing a fluent API is to daisy-chain method calls, so I could more easily and readably write foo.Set(...).Set(...).Set(...)

Writing the basic API is fine, but I want to know how to detect the last Set call in the daisy-chain, because in this call I'd like to call refresh.

I'd like to do this to avoid the user from needing to manually append a .refresh().

Has anyone dealt with something like this?

Thanks, erip

EDIT

Desired effect in pseudocode:

function Foo(a, b) {
   this.a = a;
   this.b = b;
   this.Set = function(attr, val) {
      this[attr] = val;
      //if this Set is the last
         // return this.refresh();
      return this;
   },
   this.refresh = function() {
      //do something
   }
};
erip
  • 13,935
  • 9
  • 51
  • 102
  • So in `foo.Set().Set().Set()` you want the first two `Set` calls to return `this`, but the last one to return something different? – Oriol Jul 24 '15 at 23:01
  • @Oriol if possible, I'd like the last `Set` to be `void`, basically. – erip Jul 24 '15 at 23:02
  • 1
    You can't. And if you can find some hack to make this work, it's probably a bad idea! – Evert Jul 24 '15 at 23:03
  • @Evert OK, I wasn't sure if there was a good way to do this. I'll have to add documentation that tells the user to forget `.refresh()` at his own risk. – erip Jul 24 '15 at 23:07
  • Only way I can think of is using a timeout which you can clear and restart everytime the Set method is called. However,the loop won't wait for the timeout to be done. – Sebastian Nette Jul 24 '15 at 23:09
  • I pulled out my bag of tricks including `setTimeout(function{},0)` (which in another setting obviates the need for document/ready in frameworks: http://stackoverflow.com/q/1381481/34806) and `new Error().stack` (which however is not standard: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack) -- nothing worked – Dexygen Jul 25 '15 at 00:03

2 Answers2

0

In a comment to another answer you say you would like to keep the same signature, but you might be able to do this by allowing for an optional third argument which furthermore expresses the intent to refresh, rather than just a bare boolean true. The user still needs to indicate the refresh (and could accidentally call it somewhere other than the last Set), but it maintains the method chaining.

function Foo(a, b) {
   this.a = a;
   this.b = b;
   this.Set = function(attr, val) {
      this[attr] = val;

      var cfg = arguments[2] || {};
      if (cfg.refresh) {
         this.refresh();
      }

      return this;
   },
   this.refresh = function() {
      console.log('inside this.refresh');
   }
};

var foo = new Foo();
foo.Set("what","ever")
   .Set("Hello","World")
   .Set("United","States", {refresh: true});
Dexygen
  • 11,681
  • 11
  • 73
  • 144
-2

Maybe something like this:

function Foo(a, b) {
   this.a = a;
   this.b = b;
   this.Set = function(attr, val, isLast) {
      this[attr] = val;
      if(isLast) {
        this.refresh();
      }
   },
   this.refresh = function() {
      //do something
   }
};

foo.Set({})
foo.Set({})
foo.Set({}, true)
Mr. Smee
  • 968
  • 10
  • 28
  • I would like to maintain the function signature if possible. – erip Jul 24 '15 at 23:00
  • @erip I don't think it's possible, your program has no way of automatically knowing if your done calling the Set method. You've gotta create some sort of identifier for it... parameter, function call etc.. – Mr. Smee Jul 24 '15 at 23:05