Problem/Motivation
Currently we don't track the state of AJAX views. Users trigger reloads, but no state information is logged. Specifically, it is not possible for users to return to a particular state, short of repeating the steps to get there (e.g., load page, click a particular pager link).
Steps to reproduce
Enable AJAX on a view.
Browse to another page: the URL is not updated.
Proposed resolution
We could introduce a new SetBrowserUrl AJAX command that uses the browser history API.
Views AJAX response can then return this command to update the browser URL.
Possibly, in a follow-up issue, mark Views Ajax History module incompatible with next release of Drupal core or perhaps check core versions, to prevent 2 changes to history for each Views AJAX..
Remaining tasks
User interface changes
The URL for Ajax views with exposed forms will now be updated as you select new options.
Introduced terminology
N/a
API changes
New AJAX command in views to set the page URL.
Data model changes
None
Release notes snippet
N/a
| Comment | File | Size | Author |
|---|---|---|---|
| #122 | views-backportToD1129-setBrowserUrl-AjaxCommand-343535-122.diff | 16.85 KB | cleverhoods |
Issue fork drupal-343535
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
dawehnerthe problem is that the browser NEVER sends the fragment to the server
Comment #2
nedjoWe would need to parse and send the fragment via javascript on page load.
A good place to look for ideas is the new Asynchronous module, http://drupal.org/project/asynchronous, which used a jQuery history plugin.
Comment #3
Dave.Ingram commentedThe asynchronous module states:
Both Asynchronous and Page renderer are dev versions, so this probably isn't a very stable thing to be trying to build into Views any time soon. I think it's great that people are making strides to make Ajax calls bookmarkable... but if it's not stable anywhere else yet, then there's no way it's going to get into Views.
Comment #4
Dave.Ingram commentedOops, didn't mean to change the title. Changed it back.
Comment #5
merlinofchaos commentedI don't think we need to use any kind of a plugin for this. All we need to do is have the browser check the fragment and make an ajax call if it exists; and to have the browser UPDATE the fragment on subsequent ajax calls.
Perhaps 'all' is understating how much work this actually is.
Comment #6
momper commentedsubscribe
Comment #7
peterjmag commentedI realize that this issue is quite old now; is there another issue where this functionality is being discussed or worked on? If so, I'd be happy to test patches.
Comment #8
merlinofchaos commentedTo my knowledge nobody is actually working on this. This would be a useful feature, though, and I'd support work on it.
Comment #9
mohammed j. razemsubscribing!
Comment #10
merlinofchaos commentedWow, critical? Why does everyone think their needs are critical?
Comment #11
mohammed j. razemJust to get attention for the importance of it! It might not be critical! But still it's important!
Comment #12
mohammed j. razemAnd the issue is alive again since November 2009! That's the effect of Critical issues!
Comment #13
merlinofchaos commentedIt's only alive if someone agrees to work on it. Maybe you should lobby dereine =)
Comment #14
thaddeusmt commentedit would be REALLY super if the AJAXed View remembered what view-page it was on when a link was clicked, and returned to that view-page again after the whole website-page reloaded.
it's a pain right now: if the user pages over six times in a sidebar view and clicks on a link, when the page reloads they have to page over six times again to click on the next link.
I can't find any views jQuery pagers/carousels that quite do what I want, either...
Drupal is so great most of the time I'm like "w00t! someone already did this I don't have to code it!" but then when I need to change/extend some of the functionality (like this) the awful reality of "someone else coded this now I have to unravel their strange, mysterious code before I have the faintest idea where to BEGIN to fix my problem" hits... :P
Comment #15
benone commentedSUBSCRIBE
url should be ALWAYS different for every different part of Drupal website.
Comment #16
merlinofchaos commentedWith AJAX this just isn't always possible, so this is a basically useless statement to make.
Comment #17
benone commentedBut without dynamic urls Ajax option for Views will be useless very soon also.
Golden rule is that every single click on the website should have unique url.
Or I read wrong tech blogs ?
Comment #18
merlinofchaos commentedNo offense, but if it's that important to you, please help with the feature request. Your posts are coming off like orders.
Comment #19
benone commentedDon't read me in a wrong way, I have only good intentions. I am views fan and views are fun for me. :)
Just read some articles and this one often appears - unique urls.
I never carried about that before when I was using ajax in views but now I just start understand how important it is.
So I wrote about that.
If I knew more about ajax I would upload some example patch here for sure.
Also would do this for jCarouselLite module which works crossly-browser perfect as views blocks. But the same - URL always the same.
I wrote about it here: http://drupal.org/node/762956
Believe me I thought its not a big change, just something in javascript which changes current url in a browser after each click.
Now I realize its not so easy to achieve that.
Comment #20
merlinofchaos commentedMy experience is that with AJAX, it varies depending on how you are using it. Traditionally most AJAX in Drupal has been for form manipulation, and you don't want unique URLs for that. When using AJAX on pagers, indeed unique URLs are something you want. However, it's not as easy as all that because it means that you need to interpret the fragments as soon as the page loads and perform the proper ajax. That makes it hard to just add generically. I'm not saying it's impossible, but it's not a simple problem to solve, which is why no one has yet taken the time to solve it for Views.
Comment #21
benone commentedI thought adding onClick='javascript:change url...' would be enough.
But you say it must be done after page load. So I believe you are right and have nothing more to add. :)
Comment #22
merlinofchaos commentedYou are not allowed to change the actual URL with javascript, only the fragment. That's why it's so hard.
Comment #23
benone commentedWhich fragment ?
And is possible not to change , just add '?p=2' in the end ?
Comment #24
merlinofchaos commentedNo, only the fragment, which is the part after the #
Comment #25
scott859 commentedsubscribing
Comment #26
jordanmagnuson commentedSubscribing. Would it be possible, as an in-between step, to allow ajax views results to be bookmarked via clicking on a link, ala the "[link]" feature at http://drupalmodules.com/ ?
Comment #27
merlinofchaos commentedYes, this should certainly be possible; the pagers are able to retain exposed filters, and that makes total sense as an option to AJAX exposed filters. Patches will definitely be considered.
Comment #28
iamjon commentedI'm marked this as an unassigned task. If anyone would like to role up their sleeves and take it upon themselves to write a patch it would be awesome
Comment #29
denny84 commentedCan anyone think about "%menu_tail" ?
Comment #30
tim.plunkettI've used the jQuery BBQ hashchange plugin for things similar to this, and that plugin is now included in D7, but it's used for the Overlay, so I'm not sure that we can reuse it for Views.
Comment #31
merlinofchaos commentedWe possibly can but it's going to create some ugly URLs. The real issue is going to arise that you can have repeated ajax operations that move you quite a ways through a set of views. Plus, there is a LOT of data that goes into ajaxing a view and it all needs to be somewhere on the page. One of the reasons I've never tackled this is that I'm afraid of how much the data can balloon leading to enormous URLs.
Comment #32
brunorios1 commentedsubscribing
Comment #33
nod_Hi guys, I have a sandbox project that deals with this. I'm not making a patch here because it needs an external lib. and I'm not sure how much it'll be possible to integrate. It's missing ui options and all that for now.
I haven't put it through rigorous testing yet, still, it works for basic exposed filters and pager. Here is the tentative module name : views_ajax_history.
The amount of data is actually not that bad (or I missed some pretty huge stuff) as far as state saving goes, it's all done by the browser and the length of URL is not bigger than a view without ajax enabled.
Comment #34
idflood commented@nod_: nice :) I haven't tested it but if I'm right the external library you are mentioning is history.js. I believe you can use the integrated jQuery BBQ instead. It would remove the external library issue.
Comment #35
nod_Might not be the place to discuss this, but history.js is actually much better statechange > hashchange. You don't need the # anymore on proper browsers.
A lot of time passed since #22 :)
Comment #36
tim.plunkett@nod_ yes, but bbq is included in core, which makes it that much easier to use, and even adds a possibility of it being included in Views...
Comment #37
nod_hash urls are fine for admin pages, I'd be very sad to see that on views pages. Using a hash has his own sets of issues too, the intelligent state handling page from history.js has a pretty good writeup which I share.
As usual IE is the problem, everything is fine for other browsers. If you absolutely need history I'd say just don't use ajax views. If this goes in views somehow I really don't think it should be on by default either (except for a clean implementation using statechange).
In any case I really don't want to contribute to views by introducing the crazy url hash hack just because it's used by core.
Comment #38
nod_Who cares about my opinion anyway, it's important and I just have to make the thing pluggable to allow for a different library to be used by contrib. If someone can walk me trough the best way to do that in views (a new class? just some JS wizardry?) that'd be nice.
If someone wants to help the js part is here : views_ajax_history.js. There is no config form for this either, *wink* *wink*.
Anyway I'll get working on a patch using bbq to include that in views.
Comment #39
nod_Sorry, no patch yet, just adding a reference to #1341226: Discuss the use of the History API in the overlay module.
Comment #40
geerlingguy commentedSee also: #1084336: Use HTML5 History API for AJAX (so URL preserves state of exposed filters) - perhaps that issue should be marked duplicate?
Comment #41
mkadin commentedIn the interim is it at all possible to allow for the ajax to still function even if there is a &page=X in the query string? For example, I've got some links to specific pages of a view that uses ajax...but if I go to myview&page=3, the ajax pager no longer functions. Throbber shows up, goes away, page stays the same. It seems as though we could still get that to work, even without fancy javascript stuff. And that would get us part of the way towards bookmarking specific pages in an ajax-paged view.
Is this a separate bug that deserves its own issue?
I'd be happy to work on it if someone can point me in the right direction...I'm having trouble finding the right place in the code for this...
Comment #42
matt2000 commentedI can't believe I missed this thread while searching for existing issues. I've implemented a solution in a patch to Views 3.5 here:
#1786904: Deep link filter settings & Reload results from AJAX submit when using the browser's back button
Comment #43
attila.fekete commentedI would suggest https://drupal.org/project/views_ajax_history by nod_. It's not a sandbox project anymore, and works great.
Comment #44
herd45 commentedThis module allows users to save ajax views. Each saved view is an entity.
https://drupal.org/project/views_save
Comment #45
aaronbaumanAny chance of getting this into views core?
This is standard / expected behavior for modern web apps.
Comment #46
candelas commentedI don't know enough about Views coding, but maybe you are interested in this solution that I found
http://www.accessadvertising.co.uk/blog/2013/03/html5-history-api-ajax-u...
http://www.accessadvertising.co.uk/blog/2013/04/html5-history-api-making... (here the solution)
Thanks for so much shared code :)
Comment #47
aaronmchaleI came accross this today when experimenting with using AJAX option for Views, I was hoping that the URL would be updated when filters and pagination changes (like it does without AJAX enabled), maybe this issue could be restarted?
Comment #48
marcvangendRe #47: Did you try the module mentioned in comment #43? If so, I'd like to know if it worked for you.
Comment #49
aaronmchaleRe #48 thanks for linking that, haven't tried that yet
Comment #51
pivica commented> Re #47: Did you try the module mentioned in comment #43? If so, I'd like to know if it worked for you.
For anybody interested for this I can verify that https://drupal.org/project/views_ajax_history works fine for both filters and pager on Drupal 8 site. Will report if i find any problems, but for now it seems this module is a good solution until core supports this in the future.
Comment #58
claudiu.cristeaComment #59
claudiu.cristeaFor sure needs test coverage.
Comment #60
claudiu.cristeaTests provided.
Comment #61
claudiu.cristeaI'm not sure we have to add also the pager info in URL
Comment #62
claudiu.cristeaComment #63
claudiu.cristeaComment #64
claudiu.cristeaThis looks like a Feature request and belongs more to Views rather than Views UI.
I see the pager is added correctly. Other question is whether we want to clean the URL for filters that are showing default values, making the whole URL (which, being displayed in the browser's address bar, is somehow part of the UI) much more cleaner.
Comment #65
matthiasm11 commentedThe patch from #59 will change the url in the address bar incorrectly when the view is a block instead of a page.
When the view is a block, the
view_base_pathis empty.Example: a node
/node/1with alias/categories-overviewcontaining a views block "categories" with filter "color". After changing the color filter, the url would bemysite.com/?color=redinstead ofmysite.com/categories-overview?color=red.The attached patch fixes this, so the url will be
mysite.com/categories-overview?color=red.Comment #69
prudloff commentedHere is a reroll for Drupal 10.0.
Comment #70
ameymudras commentedComment #71
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #72
pooja saraah commentedFixed failed commands on #70
Attached patch against Drupal 10.1.x
Comment #75
webdrips commentedRe-rolling #65 for 9.5.9
Comment #76
kachinsky commentedHi @webdrips
Your patch doesn't work, the view returns the error after applying:
Error: Class "Drupal\views\Ajax\SetBrowserUrl" not found in Drupal\views\Controller\ViewAjaxController->ajaxView() (line 219 of /code/web/core/modules/views/src/Controller/ViewAjaxController.php)Comment #77
shabana.navas commentedIs there a quick way we can clean up the URL that is built? Right now, I only have a couple of exposed filters and the URL already looks like this:
?date%5Bmin%5D%5Bdate%5D=2023-05-29&date%5Bmax%5D%5Bdate%5D=2023-06-16&promotion_type%5B6%5D=6&promotion_type%5B1%5D=1Comment #78
prudloff commented#72 does not apply to 10.1.0. Here is a reroll.
(I tried to generate an interdiff but it fails for some reason.)
Comment #79
elamanThe most recent patch causes a bug if user visits bookmarked url and tries to uncheck the existing filter. The main reason is that queryString is preserved when performing request to the ajax callback page:
So you can't really reset bookmarked filters.
Also it is better to push new filter state into history, so you can use browser navigation to restore previous filter state.
Comment #80
elamanComment #81
elamanComment #82
elamanInstead of removing queryString that caused issues, now remove only params that represent exposed filters.
Comment #83
prudloff commented#82 causes a problem with pagination, for example :
Remove the page param from the query string seems to fix it.
Comment #84
elamanComment #85
prudloff commentedHere is a reroll of #83 for Drupal 10.2.
Comment #88
sakthi_dev commentedRe rolled with 11.x.
Comment #91
gaëlgI made a fix so that browser URL is not updated if the view is in a dialog/modal, because in that case the browser URL is not the URL containing the view parameters.
It's like there's an URL for the page which is behind the modal (the one shown in the browser URL field), and one other URL ("hidden") for the "page" shown in the modal.
Without this fix, we got a strange behavior when using media library search in modal: it works the first time, but as soon as params are added to the browser URL, subsequent search is buggy until the whole page is reloaded.
I guess a test should be added for the modal case? Like just test that browser URL is not updated when an AJAX exposed form is used inside a modal.
And automated tests do not pass, I guess it's just some test code that needs to be updated for compatibility with current 11.x.
Then, I believe the merge request will be ready for review again.
Comment #95
prudloff commentedI rebased and fixed tests but I can't remove the draft status on the MR.
I had to change the order of GET params in some related tests, I'm not sure why.
I suppose that's because the code generating the SetBrowserUrl command does not process them in the same order as the code generating the pager links.
I'm not sure it is a problem as long as the GET params themselves are still the same.
Comment #96
smustgrave commentedHave not reviewed as the issue summary needs some attention please.
Comment #97
prudloff commentedI updated the summary but now the MR has a conflict.
Comment #98
prudloff commentedI fixed the conflict (it was caused by #3323574: Pager not working correctly in AJAX view with exposed filters).
I also noticed we were mixing two ways to remove parameters from the query (a regex and parsing it as an array). I moved everything to the new way.
Comment #99
greenskin commentedRerolled patch from #85 for Drupal 10.4.6.
Comment #100
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #101
gaëlgComment #102
gaëlgComment #103
gaëlgComment #104
smustgrave commentedleft some comments on the MR.
Comment #105
sander wemagine commentedAfter some searching I also found the contrib module Views AJAX History. Is there any reason why not to use that module and continue working on this issue?
Comment #106
prudloff commentedRemoved patches to focus on the MR.
Comment #107
prudloff commentedI think comments have been addressed.
Comment #108
smustgrave commentedFeedback appears to be addressed for this one.
Comment #109
nod_Now that we have htmx in core it could be a bit easier to manage that. We don't use htmx for exposed forms but that should be something to look into
Comment #111
alexpottI pushed a couple of commits to use constructor property promotion in SetBrowserUrl and remove the unnecessary docs from the controller constructor as well as merging in latest 11.x
Comment #112
alexpottCreated a CR for the new AJAX command.
Discussed the issue with @catch and @nod_ - we talked about whether the new AJAX command should be in core or views. We agreed this is temporary because when we migrate to HTMX there is already a command for this. Also views ajax is likely to be replaced by HTMX - see #3548100: [PoC] Use HTMX for (some?) ajax views. We agreed to get this in, even if it is deprecated "soon" it's still a win in the meantime.
Comment #113
alexpottCommitted 9c8eb29 and pushed to 11.x. Thanks!
Comment #116
scott_euser commentedThanks for the work on this!
Sounds like Views Ajax History module probably needs a follow-up issue to mark itself incompatible with next release of core or perhaps check core versions otherwise sites might get 2 changes to history for each views ajax. Have I understood this right? Happy to work on contributing such a fix to there if so.
Comment #117
anybodyThank you all! I just came across this wonderful improvement 🎉and had to remember this still existing and slightly related Views Core "Reset" button bug with AJAX views: #3346820: On views with AJAX enabled, exposed filter "reset" causes (non-ajax) page load
Maybe this can now be solved better based on this improvement? If anyone has a good idea for a resolution, that would wonderful.
Comment #118
ressaVery cool, great to see AJAX improvements! @scott_euser: Handling Views Ajax History in a follow-up issue sounds like a good idea, and I have mentioned it in the Issue Summary.
Thanks for making me aware of the AJAX Reset issue @anybody. I in fact tried "Views AJAX History" just the other day, exactly to prevent page load after Reset or navigation back, with no luck. So since it's related, I am adding it.
Comment #119
mortona2k commentedI added a note to an issue in Views Ajax History: https://www.drupal.org/project/views_ajax_history/issues/3386120#comment...
Comment #121
berdirThis is triggering a weird race condition in our tests, easy to fix: #3560093: $target_url in ViewAjaxController::ajaxView can be NULL
Comment #122
cleverhoods commentedBackport patch to D11.2.9 for composer.
Comment #123
aporieIt is a great improvement for institutional websites, though, in some other cases, such as stores or website where the UX takes over SEO purpose (for example when using views_infinite_scroll), I think one might want to restore back to previous behavior.
I've let a workaround solution in #2870341: Change URL in browser with page parameter.
Comment #124
pookmish commentedSo after updating from 11.2 to 11.3 this issue has caused a great deal of issues with query parameters. When an exposed filter (taxonomy reference field) allows multiple selections, new filter query parameters are always appended after every form submission. Also after de-selecting an option and submitting the form, the option is still selected.
I can reproduce this using a standard profile install. Creating a simple ajax view block with an exposed filter with multiple selection on the "Tags" field for the "Article" content type shows the issue. The resulting url after form submission looks like "/home?field_tags_target_id[0]=1&field_tags_target_id[1]=1&field_tags_target_id[2]=1". The "/home" prefix also gets injected when the block is on the home page and depends on the node alias.
Does anyone know why this might be? Why would this merge cause so much complications? Were multiple selections tested?
Comment #125
berdirI believe there are some existing open issues about problematic behavior with multi-value selection fields, we also had issues with them before combined with the ajax_history contrib module and other cases. Found #3273334: Unable to unselect a multi select exposed filter in ajax view and #3121172: Exposed filters get values from url when ajax is on.
Comment #126
pookmish commentedAlthough those two issues are related, the addition of this "feature" is probably the most disruptive I've encountered with Drupal core. To add that there is no way to opt in to/out of the feature is more frustrating. I would've expected more thorough review and testing from an issue that took 18 years to "resolve".