Part 1 – Description of the problem
Part 2 – this article (simple history replacement)
Part 3 – additional history items
As we saw earlier in part 1, hitting the back button into a page with filled in fields will not restore (all) the values, but merely the ones that exist at the time of the page loading. In some cases, for example cascading dropdowns, will not be shown or exist at all at the time of the page load.
What we will accomplish in this part is to push into the query string (without reloading the page!) the selected make and the selected model (where applicable).
We are going to make use of
history.replaceState(...)
function. There is a W3C standard available for this function (and others). Note that this is supported on all browsers, but only the recent versions.
To do so we need a few things:
- A function to parse (a) query string; sadly JavaScript does not have anything built in, not even jQuery is of much help..
- Change the URL upon changing of selection
- Restore selection from the URL – including cascading
Great! Let’s start.
Upon a bit of searching around which turned into stackoverflowing, I reached the most voted question on this matter called ‘How can I get query string values in JavaScript?‘.
I really looked for a quick-simple-and-standards-compliant-and-so-on solution but the best I could scavenge was this:
function getParameterByName(name, url) { if (!url) url = window.location.href; name = name.replace(/[\[\]]/g, "\\$&"); var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, " ")); }
Ugly but it works.
We already have a bit of initialization ( quick tip/reminder : in jQuery you can write a function that will execute upon document ‘ready’ just like so: $(function() { /* … */ }); ) :
$(function () { $("#SelectedMake").change(makeChanged); makeChanged(); // <- this wasn't really necessary });
Let’s add an event handler for the change of the selected model in order to change the URL from there, too:
$(function () { $("#SelectedMake").change(makeChanged); $("#SelectedModel").change(modelChanged); }); // ... function modelChanged() { var makeId = getParameterByName("mkId", document.location.href); var modelId = $("#SelectedModel").val(); if (!modelId) history.replaceState(null, null, "/Home/Selector?mkId=" + makeId); else history.replaceState(null, null, "/Home/Selector?mkId=" + makeId + "&mdId=" + modelId); }
We also need to change the makeChanged logic in order to simplify the function and allow future reuse of the ajax populating call (changed lines are highlighted) :
function loadModels(make, pushState, onSuccess) { $.ajax({ cache: true, type: 'GET', url: '@Url.Action("GetModelsByMake")', data: { 'makeId': make }, success: function (data) { if (pushState) history.replaceState(null, null, "/Home/Selector?mkId=" + make); var models = $("#SelectedModel"); models.empty(); models.append($("<option></option>").attr("value", "").text(" -- please select a model -- ")); $.each(data, function (key, val) { models.append($("<option></option>").attr("value", val.Id).text(val.Text)); }); $('#divModel').show(); if (onSuccess) onSuccess(); }, error: function (xhr, ajaxOptions, error) { alert(error); $('#divModel').hide(); } }); } function makeChanged() { var selectedMake = $("#SelectedMake").val(); if (selectedMake === "") { $('#divModel').hide(); } else { loadModels(selectedMake, true); } }
We refactored out the ajax call into the loadModels function and added two parameters:
- pushState – this would control whether after successfully loading the models the URL should be changed or not
- onSuccess – an option callback to be executed after successfully loading the models
At this point, if we run the sample, we should have URL changing functionality fully implemented but if we submit into the next page and go back to the selector page only the make dropdown will have its selection restored and that isn’t our work but the browser’s (this worked from the beginning).
Finally, in order to restore selection from the URL, a bit of initialization is necessary:
$(function () { init(); $("#SelectedMake").change(makeChanged); $("#SelectedModel").change(modelChanged); }); // ... function init() { var makeId = getParameterByName("mkId", document.location.href); if (!makeId) return; var modelId = getParameterByName("mdId", document.location.href); loadModels(makeId, false, function () { $("#SelectedModel").val(modelId); }); }
.. and now all should work smooth! 🙂
You can check out the code on Github. For this particular step of development here’s the link to the HTML code.
3 Comments.