1

I'm using m.request in a project, and since I have a request that can be potentially long running, I want to run it with background:true. However, it seems like the value never gets set to the generated m.prop.

I've made a jsFiddle with an example based on this Stack Overflow answer: http://jsfiddle.net/u5wuyokz/9/

What I expect to happen is that the second call to the view should have the response value in ctrl.test.data(), but it seems to still have undefined. At Point A in the code, it logs the correct value. However, at Point B, it logs false, undefined and then true, undefined.

I'm not sure if I'm doing something incorrectly, or if this the expected behavior.

Code from the jsFiddle:

var requestWithFeedback = function(args) {
  var completed = m.prop(false)
  var complete = function(value) {
    completed(true)
    return value
  }
  args.background = true
  return {
    data: m.request(args).then(complete, complete).then(function(value) {
        //Point A
        console.log(value);
        m.redraw()
        return value
    }),
    ready: completed
  }
};

var mod = {
        controller : function() {
            this.test = requestWithFeedback({
                method : "POST",
                url : "/echo/json/",
                serialize: serialize, 
                config: asFormUrlEncoded,
                data : {
                    json : "{\"name\" : \"testing\"}"
                }
            });
        },
        view : function(ctrl) {
            //Point B
            console.log(ctrl.test.ready(), ctrl.test.data());
            return m("div", ctrl.test.ready() ? 'loaded' : 'loading');
        }
    };
Community
  • 1
  • 1

2 Answers2

2

Edit: The problem is that m.redraw is called before the data is assigned. Instead you could create a m.prop for data and let the ajax request assign that value when completed. requestWithFeedback will then look like this:

var requestWithFeedback = function(args) {
  var data = m.prop()
  args.background = true
  m.request(args).then(data).then(function() { m.redraw() })

  return {
    data: data,
    ready: function() {return !!data()}
  }
};

Here's a modified version of your fiddle using this code: http://jsfiddle.net/u5wuyokz/11/

ciscoheat
  • 3,719
  • 1
  • 35
  • 52
  • Thanks, this works perfectly! Do you know if anything has changed since when the SO question I referenced was posted? The answer came from Leo Horie himself, so I assumed that it would work, but as written, it was giving me issues. – Artem Kochnev Feb 08 '15 at 01:04
  • 1
    You're right, it looks like it should work since a Mithril promise returns a GetterSetter. I'll tinker with it some more, if I cannot find a good reason I'll file an issue on github. – ciscoheat Feb 08 '15 at 11:25
1

When using background: true, Mithril's components branch or any other system that executes controllers in the same 'tick' as views, m.requests made in the controller will not have resolved by the time they are invoked by their respective views.

It is therefore recommended to always use the initialValue property of m.request if you're using background: true. The canonical example is to have an initial value of [] when you make a request for a list of entries:

var projectsModule = {
  controller(){
    this.projects = m.request( {
      background   : true,
      initialValue : [],
      url          : '/projects.json'
    } );
  },
  view( ctrl ){
    return m( 'ul', ctrl.projects.map( 
      project => m( 'li', project.name )
    ) )
  }
}

This solves the practical problems of views breaking when they expect to be able to work with m.request return values in a generic way, but doesn't address the more complex case in your example, where a ready flag is desirable to indicate a 'loading' state. I have a generic API that consumes a model getter function and an optional initial value. It has next, then, hasResolved, isPending, get and set methods: this allows views to be more flexible in the way they query the state of asynchronous models. hasResolved indicates whether the method has ever resolved, and next returns a promise that will resolve either with the current request, or when the next request is resolved if isPending is false.

Barney
  • 16,181
  • 5
  • 62
  • 76