0

I just started learning mithril, and I'm trying to write a simple frontend which interacts with a RESTful API. However, when I load this in the browser, the browser issues < 30 GET requests to '/posts' per second! I'm not sure if this is an error in my code or how mithril works... How can I make m.request issue a request once throughout the code, or update Post.list arbitrarily?

var Post = {
  model: function(data) {
    data = data || {};
    this.id = m.prop(data.id);
    this.text = m.prop(data.text);
    this.rating = m.prop(data.rating);
    this.created_at = m.prop(data.created_at);
    this.url = m.prop(data.url);
    this.title = m.prop(data.title);
    this.user_id = m.prop(data.user_id);
  },
  list: function() {
    return m.request({
      method: "GET",
      url: "/posts/",
      type: Post.model
    });
  }
}
var PostIndex = {
  controller: function() {
    this.posts = Post.list();
  },
  view: function(ctrl) {
    return [
      m("table.table", [ m("tbody", [
        ctrl.posts().map(function(post) {
          return m("tr", [
            m("td.heading", { onclick: m.route('/posts/' + post.id) }, [
              post.title,
              m("small", post.url)
            ]),
            m("td", [ m("small", post.user + ": " + post.created_at) ])
          ]);
        })
      ])])
    ];
  }
};
mt_caret
  • 1
  • 1
  • m.request is basic AJAX. A url of "/posts/" does not get you a list of posts. You need to "GET" a file, not a directory. Mithril hopes that file is a JSON file. That file can also be a script on your back end (php/python/perl/javascript) that returns JSON. If it doesn't return JSON there are ways to convert the response to JSON. Read up on AJAX first, then read the Mithril docs on m.request: http://lhorie.github.io/mithril/mithril.request.html – pelón Aug 29 '15 at 22:10
  • Yup, I understand; I'm also running a backend Web API in sinatra which serves an array of posts in JSON at '/posts/'. My problem isn't about parsing the JSON or showing the content of the posts, but is about why m.request is hitting the server < 30 times per second. – mt_caret Aug 30 '15 at 23:55
  • Sorry, it's hard to tell how informed a dev is with a snippet of code. I can't answer your question without seeing more code, but AFAIK the only thing in Mithril that could make so many calls is m.request used in a view that creates a list, and I don't see that mistake in the above code. Does sinatra serve a JSON "file", or an array, piece-by-piece? Are there multiple posts, each in JSON format, or one JSON array with all the posts -- as m.request expects? Is it possible that sinatra is spitting out the posts in a stream? I'm sorry I know very little about sinatra or the library you're using. – pelón Aug 31 '15 at 07:19

1 Answers1

2

Mithril uses something called a Virtual DOM internally. The way this works is that there is an in-memory model of the DOM that is updated every time something in one of your model objects changes or when the user interacts with the page. Basically, all the time. This all happens in memory, and is optimized to work very quickly. Each time the Virtual DOM is recreated, it then compares itself against the actual DOM to see if there are any differences. If something is different, only then does Mithril update the actual DOM. This is part of what makes Mithril so fast.

I'm not positive, but m.request might be being called here from the view function, which fires all the time (to update the virtual DOM).

ctrl.posts().map(function(post) {
      return m("tr", [.....

If this is the case, then what's happening here is the following: ctrl.posts() -> Post.list() -> m.request

A possible solution would be to store the results of m.request once it finishes executing, and then relate to the stored values everywhere else. You could change your controller code to the following:

controller: function() {
    this.posts = m.prop([])
    Post.list().then(this.posts, console.log)
},

This first initializes this.posts with a getter/setter function, which will return an empty list for now. It then calls the m.request function, which will send the GET request a single time. Once the request concludes, the results are passed to the "then" function. "then" takes functions as arguments: the first - what to do if the prior function succeeded, and the second - what to do if it failed. Either way, the function passed will automatically receive the results of the previous function, so this doesn't need to be specified explicitly. Since this.posts is now a function which is set by passing an argument, this will set this.posts with the results of the m.request call automatically.

After this change, when ctrl.posts is called in the view function it will be called on what is stored in the variable instead of spamming m.request each time.

I'm currently learning Mithril myself and having a great time with it. I'm not positive that what I described is what was happening here, but it sounds like something along those lines. If what I suggested doesn't help, it could be that your code is sending AJAX requests on every call to ctrl.posts() until the first request completes. So I would check if the requests stop after a little while, or continue all the time, as that might help in narrowing down what it might be.

YosefD
  • 21
  • 3