Problem/Motivation
When an embedded view display (such as a block) has exposed filters, if the view also contains a display with a path (page, export, etc) then the exposed form action will be set to the other display's path. This results in Reset buttons redirecting to other displays for example.
Proposed resolution
This is a tricky one, this could be as involved as rewriting the Drupal\views\Plugin\views\display\DisplayPluginBase::getLinkDisplay
logic.
Remaining tasks
Write a failing test
Find a solution
Original report
When a view is setup to use the "Embed" display in a view, using AJAX in the form in any way will cause the action attribute of the HTML form object to be rewritten to submit to '/views/ajax' instead of the current page. This issue is harmless unless the filter in your embedded view uses the Reset button to clear the form and reload the page. When the Reset button is pressed, it'll direct you to /views/ajax instead of your current page.
A variation of this bug is if you have both a Page display setup and an Embed display setup in the view. If both of these displays exist in the same view, then the form's action attribute for the filter in the Embed display will be rewritten to submit to whatever path you setup for the Page display. I'll use this more elaborate scenario in my steps below.
To reproduce this bug:
- Create a new view named "Ajax Test"
- Change the format to Table. In the settings make the title field sortable
- Edit the Title field to give it a label of "Title"
- Add the Title field to the Filter Criteria with the filter configured to use the Contains operator. Check the "Expose this filter to visitors" box.
- Under Advanced Options, enable AJAX for this view
- In the Exposed Form options, make sure Basic is chosen as the type and in the settings check off the box that inserts a Reset button.
- Add a "Page" display to this view. Keep all the same settings. Only edit the path for the Page display. Use "/wrong-endpoint" as the path
- Save the view
Now we need to embed this view into a page. I'm going to use the Page Example module from the Drupal Examples for Developers set of modules for the dummy page needed to create an embedded view display, but it can be embedded in other ways and would still work the same.
- Install and enable the Page Example module to create a sample page we can embed into.
- Open 'Drupal\page_example\Controller\PageExampleController'
- Edit the description method and append the following before the "return $build" line:
$build['test'] = views_embed_view('ajax_test', 'embed_1');
Go to the example page at '/examples/page-example'. Your embedded view should be there with the exposed filter. Inspect the HTML form object. The action attribute at this time should be '/examples/page-example'.
Now use the exposed filter to filter the table results. The results will narrow to match your search terms and the "Reset" button will appear. Inspect the form object again. The form will now submit to '/wrong-endpont'. The AJAX parts of the view will continue to work. You can use the table header sort. You can use new search terms and your results will filter as expected, but if you press the Reset button, you'll be redirected to the '/wrong-endpoint' page that was created as a Page type of display for this view.
If you skipped that step of creating the Page type display, and your AJAX Test view only has an Embed display, you will instead get redirected to /views/ajax. Fixed by #2820347: Exposed filter reset redirects user to 404 page on AJAX view when placed as a block so it goes to the current page instead.
This issue does not occur if the view in question is not an embedded view. If you try to repeat this behavior on the Views Page that we created at /wrong-endpoint, you will get the correct behavior. The form's action attribute will not get changed to direct to the wrong page when AJAX rebuilds the form.
Comment | File | Size | Author |
---|---|---|---|
#15 | 2844823-15.patch | 1000 bytes | bobbygryzynger |
Comments
Comment #2
hyperlinked CreditAttribution: hyperlinked as a volunteer commentedComment #3
hyperlinked CreditAttribution: hyperlinked as a volunteer commentedComment #4
hyperlinked CreditAttribution: hyperlinked as a volunteer commentedThere's an easy workaround for this. Since the embed display will adopt the path specified by a Page display if contained in the same View, you just duplicate your Embed display as a Page and make the path for the page the same as the one the Embed display will be embbed at. This is assuming that the Embed display will always be at a predictable path.
So for example, if your Embed display is going to be embedded on all paths that follow a pattern of "events/[entity_id]", you simply duplicate the embed as a Page display with the path "events/%".
Alternately, don't even create the Embed display. Just create a page display and embed that instead.
Comment #5
LendudeMoving to the right queue. Didn't attempt to reproduce.
Comment #6
xjmComment #7
hyperlinked CreditAttribution: hyperlinked as a volunteer commentedWell, it appears that the workaround that I mentioned above doesn't work or at least it won't work under most circumstances.
Comment #9
hyperlinked CreditAttribution: hyperlinked as a volunteer commentedI opened this against the 8.2 branch initially and I just tested this against a fresh install of the 8.3 branch. This issue is still active in the lates 8.3-dev release on February 11, 2017.
Comment #13
maacl CreditAttribution: maacl commentedComment #14
bobbygryzyngerI'm still seeing this issue in
8.7.x
. A somewhat similar issue is mentioned in #8 from #2820347: Exposed filter reset redirects user to 404 page on AJAX view when placed as a block, but was not addressed by the patch there.Comment #15
bobbygryzyngerFollowing-up on #8 from #2820347: Exposed filter reset redirects user to 404 page on AJAX view when placed as a block:
The attached patch avoids selecting a routed display for the form action when the display is not a routed display. Let's see what the testbot has to say about it.
Comment #17
bobbygryzyngerIt appears that this logic is relied upon in some areas. Until a stable fix can be achieved, the form's action can be overridden to the correct path if it is known.
For instance:
Comment #18
mbovan CreditAttribution: mbovan at MD Systems GmbH commentedUpdated the issue summary with new behavior after #2820347: Exposed filter reset redirects user to 404 page on AJAX view when placed as a block.
Comment #19
mbovan CreditAttribution: mbovan at MD Systems GmbH commentedThe problem from #15:
To summarize, the current issue title #2844823: Views exposed form action incorrect for embedded views' displays with other displays with paths is a duplicate of #2820347: Exposed filter reset redirects user to 404 page on AJAX view when placed as a block. However, the issue went into another direction (starting from #14).
@bobbygryzynger, would you like to use this issue and argue #2820347-126? If so, could you please update the issue title/summary to match the new goal?
Comment #21
Ludo.RThis happens as well in D7
Comment #23
acbramley CreditAttribution: acbramley at PreviousNext for Service NSW commentedThis still happens and is easy to reproduce, I just bumped into this issue while using views_data_export which caused the Reset button to trigger the download of the export, quite a confusing bug!
The issue is the same though, the action is set to the export path rather than the current page.
The bug comes all the way from
Drupal\views\Plugin\views\display\DisplayPluginBase::getLinkDisplay
where it simply chooses the first display that has a path. The flow is:1)
ViewsExposedForm::buildForm
calls$view->hasUrl
2)
ViewExecutable::hasUrl
callsDisplayPluginBase::getRoutedDisplay
3)
getRoutedDisplay
callsgetLinkDisplay
which checks the link_display option (this is used by views displays with the "View more" logic and probably other things). However, if this is empty it just finds the first display with a path and returns thatComment #24
acbramley CreditAttribution: acbramley at PreviousNext for Service NSW commentedComment #26
staceroni CreditAttribution: staceroni as a volunteer commentedI tried #15 and it has fixed the form action on a view block with exposed filters + AJAX enabled.
Update: I only noticed comment #17 afterward, so I will override the
<form action>
in a hook instead.Comment #27
staceroni CreditAttribution: staceroni as a volunteer commentedUsing a hook as suggested in #17 does fix the action on the initial page load.
<form action="/current-page">
However, after using a filter (and ajax enabled), the exposed form is updated via javascript and then the action is changed to /views/ajax.
Using the reset button on the form is then broken.
<form action="/views/ajax">
Comment #30
inversed CreditAttribution: inversed commentedFor anyone still running into this problem, my workaround was to duplicate the page display that was being unintentionally used to set the path for the embedded Views displays. Then, I disabled the original page display and my embedded displays started setting the form action correctly (with or without AJAX).
Comment #31
seeduardo CreditAttribution: seeduardo at Cyber-Duck commentedAs I think it is related to this issue, I wanted to note that I have posted a potential solution (more of a workaround, really) in a different issue thread: https://www.drupal.org/project/drupal/issues/1109980#comment-14414629
To save you clicking through if not wanted/needed, the gist of my comments are below.
On our project using Drupal Core: 9.3.5, on an Export CSV view attached to a Block view the latter of which is using Better Exposed Filters, the 'Download CSV' button was working fine, but the 'Reset' button also triggered the CSV download. This was the code that "solved" the problem for me:
The compromise is that the page reloads rather than just resetting the filters, but this is certainly better than the faulty behaviour of the 'Reset' button beforehand. I still feel a better solution needs to be incorporated into Core at some point.
Comment #32
dpi#1874838: Allow exposed blocks to use 'Link display' settings seems to be a good solution to this issue in some circumstances.
For example I am rendering a view with a Block display which also has a Views Bulk Export display.
The Block display has filters and Views Bulk Edit on it.
The
<form action>
attribute will automatically get the Views Bulk Export route on it, because the block is not routable.If I use the patch from #1874838: Allow exposed blocks to use 'Link display' settings, I'm able to set the path of the real route embedding this page, so it does not pick up the export path. The
<form action>
attribute will have the value of the custom path.Comment #35
Dubs CreditAttribution: Dubs commentedI am embedding a views block in paragraphs, and this code works for me (using hook_form_FORM_ID_alter() ).
Comment #36
alisonI found this issue right after submitting a new one, darn.
#3346820: On views with AJAX enabled, exposed filter "reset" causes page load
I'm not closing my new one quite yet, though, for a few reasons (in no particular order):