HTML View loses state during search (ajax) update - javascript

I have been searching around trying to find a solution to this with no success :(
I have a page that lists products. The page has two layouts (list and grid) with a toggle to switch between the views (basically changing the CSS behind the view) this is all working as expected !
I am using full text search on the page containing of a search box (text input) that filters the products based on the entered search string etc... this is also working as expected.
I have a keyup event listener on the text input so that the data is filtered on each key press using an ajax get response. The data is filtered correctly however the view state / switch is not considered.
E.g. the default view is a list. If I do not change the view and search, everything is updated correctly: the correct results renders as a list. However, if I change to the grid view, and then search, although the results are correct the view is switched back to the original (list) view.
The switch toggle also stops working after a search has passed.
I am not sure, but it seems like the $.get methods response reloads the original file not containing any added classes (in relation to the view change) that have been added. It also appears that all JS added on document load is lost.
This is "basically" what I have:
CSS
.hideSection {
display: none;
}
HTML
<form accept-charset="UTF-8" action="/employees" id="employee_search" method="get">
<input autocomplete="off" id="query" name="query" type="text">
</form>
<div class="btn-group">
<button id="listViewButton" type="button" class="btn btn-default btn-sm">LIST</button>
<button id="gridViewButton" type="button" class="btn btn-default btn-sm">GRID</button>
</div>
HTML
<div id="listView" class="list-view">
...
</div>
<div id="gridView" class="grid-view hide-view" >
...
</div>
JS (CoffeScript)
$("#gridViewButton").click ->
$(".gridView").removeClass "hideSection"
$(".listView").addClass "hideSection"
$("#listViewButton").click ->
$(".listView").removeClass "hideSection"
$(".gridView").addClass "hideSection"
$("#employee_search input").keyup ->
$.get $("#employee_search").attr("action"), $("#employee_search").serialize(), null, "script"
false
As said, all searching works as expected, the problem is that the view looses its state (List or Grid) when searching, and additionally all JS appears not to work after the search - im not sure if this is something to do with the response from the $.get method as looking at it in the inspector, it contains the HTML from the original file not the modified file after changing the view etc..
Any help on this in relation to keeping the view state on searching would be greatly appreciated !
Thanks,
Jon.

listView and gridView are id not class and use toggleClass() like,
$("#gridViewButton").click ->
$("#gridView,#listView").toggleClass "hideSection"
$("#listViewButton").click ->
$("#listView,#gridView").toggleClass "hideSection"

The only way I managed to achieve my required behavior was to modify the Ajax GET method, by adding a success handler and modifying DOM data within this handler:
Within my keyup handler, before the GET, I save the current view state (styles added to the view containers) in variables accordingly. Within the GET, in the success handler I modify the result setting the view (styles) to the the new (changed) values (form the set variables) after the DOM was loaded:
$("#employee_search input").keyup (event) ->
alteredListViewHtml = $(".listViewEmployeeContainer").attr("class")
alteredGridViewHtml = $(".gridViewEmployeeContainer").attr("class")
$.get $("#employee_search").attr("action"), $("#employee_search").serialize(), ((result) ->
$(".listViewEmployeeContainer").attr('class', alteredListViewHtml)
$(".gridViewEmployeeContainer").attr('class', alteredGridViewHtml)
viewToggleClickHandlers()
return ), "script"
false
I hope this makes sense and can help someone out !
Cheers,
Jon.

Related

Stimulus: how to handle repeating items with the same target name

I have a list of items, and each one has a link to click to edit it. I am using stimulus to make the edit "modal" form visible when they click that edit link. The id of what is going to be edited is present as an id= on the corresponding link tag of the list
So, the edit link looks like this:
<td>
<a data-action="click->content#edit"
data-target="content.editBtn"
id="<%= url_for(content) %>")>
Edit
</a>
</td>
And the idea is that the content#edit action in the stimulus controller examines the and locates the id of it and uses that to edit the right row.
However the problem I am having is, I think, that as a result all the rows of this list have a data-target with the same name and the wrong one (the first one?) gets bound to the target..
However if I wanted to make each data-target different by e.g. appending the id to it, now I have a long list of targets in the controller.js so that doesn't make sense.
What's the right way to handle?
If you're using Rails as the backend like your other questions seem to indicate, there may be a simpler, non-Stimulus solution. To use Stimulus, you'd need to fetch the data for the item from the server or from the DOM, display it in a form, then submit the correct form with the correct ID to the server through JavaScript. Why not just have a remote link_to button to the edit action for each item? Rails gets a JS request to the edit controller action, and you can load the modal form with the data that you have from your Ruby object.
If you use Stimulus for anything on the form, I'd use this opportunity to craft a Stimulus controller that listens to the ajax->send/error/complete events and automatically disables/enables buttons, sets loading spinners on buttons, and closes the modal. Those would be good areas to sprinkle in some functionality that Stimulus makes very simple.
This is actually a good use of Stimulus since it is modular. You add a controller for each row instead of having the controller around the page or table.
<tr data-controller="content">
<td>
<a data-action="click->content#edit" data-target="content.editBtn" id="<%= url_for(content) %>")>
Edit
</a>
</td>
</tr>
I was just having a similar problem.
This helped:
https://codepen.io/smashingmag/pen/ExPprPG
Basically you can loop over the targets like:
for(let tgt of this.mySameNameTargets) {
tgt.innerHTML = "Some Value"
}
This assuming you have this in the controller:
state targets = ["mySameName"]
You can also use "Action Parameters" to put the id in each row:
https://stimulus.hotwired.dev/reference/actions#action-parameters
From those docs it looks like this:
<div data-controller="item spinner">
<button data-action="item#upvote spinner#start"
data-item-id-param="12345"
data-item-url-param="/votes"
data-item-payload-param='{"value":"1234567"}'
data-item-active-param="true">…</button>
</div>
// ItemController
upvote(event) {
// { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
console.log(event.params)
}
You can set the id of the item in that row in a param, and then when they click on that row you can dig it out of the params.

How to capture the value for one of the custom ticket fields in zendesk on the new request page?

In Zendesk's help center, I have a new request page set up which allows the end-user to submit a request.
For one of the questions, there is a dropdown which asks the client to establish whether they are using the basic or enterprise version.
If basic, I would like to display some text, perhaps a modal that has messaging around the prioritization for our customers.
However looking at the code, it isn't apparent how to capture the value from the dropdown to display the messaging accordingly.
So far I didn't see a clear way to accomplish this, I've been looking at the documentation here https://developer.zendesk.com/apps/docs/help-center-templates/new_request_page#content
This is the code that's set up on the New Request Page template.
{{breadcrumbs}}
<div class="clearfix">
<section class="main-column">
<h1>{{t 'submit_a_request'}}{{#if parent}}
<span class="follow-up-hint">
{{follow_up}}
</span>
{{/if}}</h1>
<div class="form">
{{request_form}}
</div>
</section>
{{chat}}
You will want to grab the selected value with something like this (using jQuery for simplicity):
$('#request_custom_fields_' + custom_field_id).attr('value');
You can inspect the DOM to see the id. The resulting value will be the tag name you have assigned to the selected value of the custom field.
You can grab it on change and react to it with something like:
$('#request_custom_fields_' + custom_field_id).change(function(){
if ($(this).attr('value') == 'my_cool_tag_value') {
//Do your stuff
}
});

Autocomplete with restriction

I am trying to do, using bootstrap, an autocomplete function which works over an inputText having to show the options list only when the related inputText contains more than 4 characters.
This is my xhtml:
<script>
$(function(){
jQuery.noConflict();
var autocompPerson = $('.autocomp');
autocompPerson.typeahead({
source: [#{operationsFormBean.allPersonFilteredList}],
});
});
</script>
<div class="col-md-8 pb0 pl0">
<h:inputText id="auPerson"
value="#{backingBeanRef['selectedPersonName']}"
styleClass="form-control autocomp wo-borders bg-transparent">
<a4j:ajax
event="keyup" listener="#{backingBeanRef['personFilteredList']}"
render="operations_form_panel"/>
</h:inputText>
</div>
The method personFilteredList() is in my java Bean and it is the one who controls if the input has or not more than 4 characters.
If I use the event "keyup", whenever I write a character in my inputText, I lose the focus over it and the list disappears immediatly. I know it is working since it shows the list only when I write more than 4 characters. The problem is : when the method is called I have to render the inputText in order to show the list and it makes me lose the focus over which makes the list disappears.
Do you know if I can use a js function like "updater" which updates my selectedPersonName every single time I write something without using the event keyup?
Thanks in advance.
Use the AJAX oncomplete JS callback hook to update your source for typeahead and re-render just the inputText. See this post for resetting the source in bootstrap typeahead.

Cannot select table row id using jquery after ajax update

I have a html table containing data rows. I am building add/edit/delete functionality to the table using AJAX.
The format of my table row is as follows:
<tr id="281"><td class="todo-task">fdgdg</td><td>some more data</td><td>EDIT BUTTON</td></tr>
At the moment I have a working AJAX function to both add a new row and edit a row via a prepend().
In order to edit a table row, you click the edit button which pulls the values into the form and submits ... simples so far. Here's the code for the edit click and the form markup. Note the id from the edit click goes into a hidden field in the form.
$("button.edit-todo").bind("click", function(){
var task = $(this).closest('tr').children('td.todo-task').text();
var todoID = $(this).closest('tr').attr("id");
$("div#todo-form input.todo-task").val(task);
$("div#todo-form input.key").val(todoID);
});
...
<form id="mgnt-edit-todo-form" class="record-edit-form">
<input type="text" class="todo-task input" name="task">
<input class="key" name="key" type="hidden" value="279">
<button class="update uk-button uk-modal-close" type="submit">Save</button>
</form>
... and the ajax bit which binds the hidden field for the update.
$("button.update").bind("click", function(e){
var key = $(this).closest('.key').attr('value');
$.ajax({
type: "POST",
url: "update-record.php",
//etc etc etc
The problem
The first update works fine, it will prepend a table row including the id. The issue occurs when I then wish to make a further update to that same "prepended" row. Whilst the DOM shows the markup as correct the function on button.edit-todo will no longer get the id. When looking at the form markup in the DOM, the value of the hidden field is blank.
I've looked at the source in Chrome and I can see that it does not match the view in the browser/DOM. For example, the markup is all the same, but the rows have been put into a different order - it looks as though by date. (There is a date field but I have cut down the fields for this example..) I think this is causing the issue ... question is why? and how do i fix it?
Thanks
Update
Here is my markup in the DOM, looks fine to me.
To be absolutely clear the issue is occuring when clicking the edit button.
It calls the following function (now using 'on':
$("button.edit-todo").on("click", function(){
//load the data from this row
var task = $(this).closest('tr').children('td.todo-task').text();
var client = $(this).closest('tr').children('td.todo-client').text();
var due = $(this).closest('tr').children('td.todo-due').text();
var todoid = $(this).closest('tr').attr('id');
$("div#todo-form input.todo-task").val(task);
$("div#todo-form select").val(client);
$("div#todo-form input.todo-due").val(due);
$("div#todo-form input.key").val(todoid);
});
Clicking row 286 works as intended (it was present on the page load) in that the tr id is added to the hidden field in the form. Clicking row 297 (as far as I'm aware in the DOM?) does not pass the id to the hidden field...
Please help!!! Doing my head in...!
Instead of .bind use .on:
$("button.edit-todo").on("click", function(){
(...)
and
$("button.update").on("click", function(e){
(...)
Using ID for loaded data is not very comfortable way anymore. Use class instead of ID.
Also jQuery has changed "bind" to "on" method. I recommend to use "on".
For example:
$(document).on("click", ".edit-todo", function(){
var task = $(this).closest('tr').children('.todo-task').text();
var todoID = $(this).closest('tr').attr("id");
$(".todo-form .todo-task").val(task);
$(".todo-form .key").val(todoID);
});
Also, source shows the code when the document loads, not the dynamic DOM.

$().buttonset not working on dialog modal box

I've done a dialog box that contains a form inside it, and I would like to add some fancy items to it. I've been trying with $().buttonset() as I've done with most of my radio buttons in the application, in order to get a coherent UI for my application. The thing is that, even if following the rules specified, the buttons remain as a normal radio button, and not with the fancy interface. Do you know what could be the problem?
This is the part of the form where I want the fancy radio buttons:
<div id ="Replace">
<input type="radio" name="Replace" value = "true" id = "ReplaceYes"
onclick = "setReplace(this)" />
<label for="ReplaceYes">Yes</label>
<input type="radio" name="Replace" value = "false" checked="checked"
id = "ReplaceNo" onclick = "setReplace(this)" />
<label for="ReplaceNo">No</label>
</div>
And then, as the previous part of code is in a partial view, invoked when showing the modal box, this is how I try to convert the buttons appearance:
$("#Replace").buttonset();
The thing is that, debugging it I've seen that it goes through that part of the code, but it doesn't do what it's meant to do. Any clue?
I had the same problem has your, after an intensive reflexion I 've found that :
When you declare your Dialog box, you can specify a function called when it opens
$( ".selector" ).dialog({
open: function() {
$( '#buttonset' ).buttonset();
}
});
Updated: I had never used .buttonset() before, in any case it works against your markup, you can see a demo here. This demo uses the same code as the question:
$("#Replace").buttonset();
Make sure you're including the jQuery UI CSS correctly, and that your IDs are unique, if they are not you'll get some real funny behavior, and should switch to a class. Also, ensure that this part of your view is in the DOM when it runs, e.g. is it inside a document.ready event handler like this?
$(function() {
$("#Replace").buttonset();
});

Categories

Resources