MithrilJS / mithril.js

A JavaScript Framework for Building Brilliant Applications
https://mithril.js.org
MIT License
14.02k stars 925 forks source link

m.request with query params does not redraw with back-button #1733

Closed dealercontrolrachel closed 7 years ago

dealercontrolrachel commented 7 years ago

Hi, I am pretty new to Mithril but using it for work and really liking it so far. I am using ajax requests to perform paging and searching on a listing page and it is lightning fast, loving it. However, I am having one issue. I have a route defined like this:

 m.route(document.getElementById('dealerships-list'), "/", {
    "/": {
        onmatch: function(vnode) {
            m.request({
                url: '/dealerships/',
                data: Dealerships.dealerships_search_params,
                config: xhrConfig
            }).then(function(data) {
                // Initialize the dealerships
                Dealerships.init(data);
            });
            return Dealerships.DealershipsListView;
        },
        render: function(vnode) {
            return vnode;
        }
    }
});

Dealerships.dealerships_search_params is a javascript object that is set with a search form on the page. It has a lot of items.

The Dealerships.init function just maps the data from the ajax response to my objects to be displayed on the page.

This works excellently!! However, I wanted the query parameters for the search to be displayed in the url because it would be nice if users could send a link to specific search results or pages. To achieve this I have this code which is executed on form submit:

Dealerships.dealershipsSearch = function(e){
   e.preventDefault();
   // manipulate all search params to be sent as query params if they have a value
   var queryString = [];
   var params = Dealerships.dealerships_search_params;

   for (var key in params) {
       if (params.hasOwnProperty(key) && params[key]) {
           queryString[key] = params[key];
       }
   }

   // Change route with query string
   m.route.set('/', queryString);
};

This works AMAZINGLY. If I type in parameters or paste in a whole url, whatever. However, when I use the BACK BUTTONS on the browser, the URL is changed but the page does not redraw unless I refresh it.

Sorry for a long post, hopefully someone can explain to me how to make this happen!

dealercontrolrachel commented 7 years ago

Closed in error.

dead-claudia commented 7 years ago

@dealercontrolrachel Would you mind setting up a pen/fiddle/etc. with the example, so we can repro and play with it? Bonus points if you can whittle the size down some. 😄

orbitbot commented 7 years ago

Bear in mind that I've actually not personally implemented anything using the following, but is this issue to do with RouteResolvers creating diff rather than teardown semantics for a route endpoint? See http://mithril.js.org/route.html#wrapping-a-layout-component .

One way to confirm would be to add a onupdate lifecycle method in DealershipsListView, and check if that gets called when you press the back button. If so, you can probably populate the viewed data structure or set the active Dealership or whatnot in the new lifecycle method so the view gets the correct data.

If the above is the case, another solution may be to move the data updating request from the route definition to the view component (DealershipListView) itself, eg. have teardown semantics instead of diffing. From what I can tell of the original source, it seems that onmatch in the route resolver does not delay the route change, so it seems that DealershipListView can already handle empty data sources well enough.

dnErf commented 7 years ago
m.route(document.getElementById('dealerships-list'), "/", {
    "/":  {/* onmatch: function(args) { ... your code } */}
    "/:params" : {/* parse params if needed, render the component and spread its attrs */}
});
dealercontrolrachel commented 7 years ago

@dnErf This works! Seems like a slightly weird way to do things but I'll take it. Thank you!

I can also just use plain javascript to grab the parameters in the route and use the single route that I had initially!! I initially did not realize that the the onmatch was executing, it just wasn't passing my parameters (why would it?). Now that I realize that, I feel silly for not understanding before. :) :)