How can I change the styleClass of a control while also doing a partial refresh on another component? - dojo

I have a straightforward XPage that lets a user answer a question with a simple Yes/No/NA response using radio buttons. I have restyled the radio buttons to look like a bootstrap button group to make it visually more interesting for the user. If the user chooses "Fail" then they are informed that they need to do something else - easily done with a simple partial refresh to a div further down the page.
This all works fine.
The problem I'm having is that I'd like it so that when the user selects an option, I would like to add a new class of "active" to the selected option so that it highlights in a pretty colour. But for the life of me I can't get this to work and though I'm sure it's a straight forward problem, I can no longer see the wood for the trees.
My current (abridged) iteration of the radio button code is this:
<xp:div styleClass="btn-group item-result" id="edit-result" loaded="${Question.open}">
<xp:radio text="${lbl.kwPass1}" id="itemPass"
styleClass="btn btn-pass #{(item.itemResult eq '0')?'active':''}" groupName="itemResult"
selectedValue="1">
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="actionAlert">
<xp:this.script><![CDATA[XSP.partialRefreshPost('#{id:edit-result}');]]></xp:this.script>
</xp:eventHandler>
</xp:radio>
<!-- other radio buttons -->
</xp:div>
<!-- other page compenents -->
<xp:panel id="actionAlert">
<!-- panel content and appropriate rendered value -->
</xp:panel>
This was attempting to do a chained partial refresh on the radio button container so that the EL would evaluate and apply/remove the 'active' style based on the document datasource ('item') value. I have also tried using dojo.addClass, dojo.removeClass, XSP.partialRefreshGet and other options. I don't mind what the solution is as long as it's efficient and works. I'd prefer not to move the actionAlert panel to within the same container as the radio buttons and I can't do a full page refresh because there are other fields which will lose their values.
Some notes:
I'm not using a RadioGroup control because it outputs a table and I haven't got around to writing my own renderer for it yet. Single Radio button controls work fine for what I need them to do.
I originally tried using the full Bootstrap solution of using "data-toggle='buttons'" (source) which sorts out applying the "active" style fine but then, inevitably, prevents the partial refresh from working.
the radio button styles are clearly not Bootstrap standard
Any assistance pointers or, preferably, working solutions would be appreciated.

You need to aim your partial refresh at the div containing all your radio buttons. Give it an id, so you can address it.
Partial refresh, as the name implies, refreshes one element and its content only. So you target the element that covers all of the items you need to recompute.

Stepping away from the problem, a couple of beers and an episode of iZombie later, I realized what I was doing wrong and sorted it out. So, for posterity, here is the simple solution that I swear I tried earlier but clearly works now:
<xp:div styleClass="btn-group item-result" id="edit-result" loaded="${Question.open}">
<xp:radio text="${lbl.kwPass1}" id="itemPass" value="#{item.ItemResult}"
styleClass="btn btn-pass" groupName="itemResult" selectedValue="1">
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="actionAlert">
<xp:this.script><![CDATA[dojo.query('.item-result > .btn').removeClass('active');
dojo.query('.btn-pass').addClass('active');]]></xp:this.script>
</xp:eventHandler>
</xp:radio>
<!-- et cetera -->
The many places I was going wrong:
In my code in the question, I was calling XSP.partialRefreshPost in the CSJS script of the radio button when it should have been in the onComplete of the eventHandler. It has to be chained to another partial refresh so that it runs after it, not at the same time. I did end up getting this right - but overlooked something I'll come to in point 3.
In my original attempt to use Dojo, my first mistake was to try and target the ID of the radio button, something like:
dojo.addClass(dojo.byId('#{id:radio2}'),'active');
This actually works as expected, so long as you remember that the ID of the radio button on the XPage refers to the actual radio button control and not the label wrapping; and the label is what I wanted to style. So the class "active" was being actually being added, just not to the element I thought it was. I should have spotted this in my browser code inspector except for the third thing I got wrong:
Ironically, I sorted out the first issue, remembering to put the XSP.partialRefreshPost into the onComplete - and then didn't remove it when trying to run the Dojo.addClass(). So I didn't notice the mistake with the addClass targeting the wrong element because after it ran, the partial refresh updated the container and removed the class I had just added which made me think that nothing was working.
So now I have some neatly styled radio buttons that don't look like radio buttons and it's all managed client side without any unnecessary partial refresh trips to the server barring the one where I actually need to look stuff up from the server. And the vital lesson is - sometimes you just need to step away from a problem and come back to it with fresh eyes later on.

Related

How to put one button inside another one in Vue?

I’m stuck with putting one button inside of another one: it’s prohibited, but I use bootstrap, that’s why I appoint class “btn …” to span and it looks like the button.
My button should look like this:
Filename.jpg <small delete button<
When you press on filename, file opens, when on small delete button - it sends request to API and deletes file
But now link is not working, but delete button does work. Putting and so one did not solve my problem
Code:
<span v-for=“link in links”
class= “btn btn-success”
v-bind:href=“<domain> + link.file”>
<button type=“button” class=“btn btn-danger” #click=“deleteFile(`$(link.file_id) `)”>-</button>
</span>
href only works on certain elements. Use <a> anchor instead of <span>.
Generally speaking it is not a good idea to wrap clickable elements inside other clickable elements. It's bad for accessibility and tab navigation and it can lead to easy missclicks from your users.
The right thing to do would be to put your two buttons next to each others to indicate that there is in fact two separate actions your users can take related to the file.

Quasar QSelect popup and input text persistent on click

I am trying to set up a QSelect driven by user input in order to achieve an "autocomplete" behavior. There are a few examples on the official documentation and they make use of the #filter callback.
I have currently two problems:
Whenever I click outside of the input field the input text is lost and the popup disappears.
If I click on the input the current text remains, but the pop is hidden until I click on it again.
For the latter issue, one workaround is to force the popup to show upon click:
<q-select
ref="input"
...
#click.native.prevent="onClick"
>
...
onClick(){
if( this.searchFilter.length > 0){
this.$refs.input.showPopup()
}
}
However, the inconvenience is that the popup still shortly disappears for a short while before showing again. I've also tried using #click.native.stop instead of #click.native.prevent to no avail.
As for issue number 1 I haven't even found a workaround yet.
Here is a related issue, though the popup disappearing was a wanted behavior in his case.
I set up a basic Pen to illustrate the issue. Try clicking on the input or outside the input at the same height.
The trick was to use #click.capture.native and then conditionally stop the propagation inside the callback function via event.stopImmediatePropagation() See it working here

Materialcss modal not working with datatables.js

I am trying to build an admin dashboard using material design framework. As a part of it, I am trying to add modal-trigger element inside a <td></td> tag of a table that uses datatable.js library. But when I click on the trigger no modal is appearing. Did anyone face similar issue before?
I think that what's happening is that your trigger isn't in the DOM when you draw the table, but without seeing your code I can't be sure. Generally, it will trigger a modal when it is clicked or something? You might want to change the actual triggering to clicking on a td with a given class contained within the table so something like this:
$(".modal-trigger").click(function(){//Open Modal});
This would work on the first page but not after the first draw of the table as the event had been registered before the elements were within the DOM. Rather, you'd need to listen to the click within the table like this:
$("#table-id").on("click", ".modal-trigger", function(){//Open Modal});
I hope that makes sense and that it helps, if not perhaps work up a JSFiddle illustrating your issue?

xpages tab container partial refresh

I make a partialRefresh on a tab container with 4 tabs:
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="djContentPane1">
<xp:this.action><![CDATA[#{javascript:viewScope.put("hideRelatedDocuments", true);}]]></xp:this.action>
</xp:eventHandler>
The viewScope is then used to some fields which I want to hide/show. Those fields lay on the tab container.
And after this, the tab container looks strange:
What might be the problem? Thanks for your time.
I tried adding a mainPanel, and on this panel I added the TabContainer. Then, I updated the panel but sttill, no work. The result:
You can refresh the fields to show/hide them depending on current value in combobox field if you
put the fields into panels which have an id and always gets rendered
place the panels into the Tab Panels
refresh all panels on onchange event of your combobox with XSP.partialRefreshPosts
Look for the sample code here.
It's because you're refreshing the content pane. I've not pinned down the cause, but refresh either an area outside the Dojo Tab Container or a panel/div that's inside the Tab Container Pane you want to refresh.
I suspect it's something to do with Dojo then processing the response HTML. As a client-side framework, I don't think Dojo is really designed to handle replacing HTML by injection into an existing page.
Tommy Valand added a snippet to handle this problem: http://dontpanic82.blogspot.co.uk/2013/03/fix-for-partial-refresh-on-dojo-tab.html

Dojox.grid.DataGrid - in a widget - only renders on visible tab

I am using a Widget that contains a DataGrid object. The Widget works fine when included in the first tab (this is the visible tab), but not when I use the same code on a second tab.
The code is the same I have done several checks to make sure there are no other problems - and non Grid code is rendering fine - only the grid that has a problem. I have tried setting the height and width manually and this just results in a large grey rectangle on the second tab.
Do I need to tell the Grid to refresh in some way - or is it a property for the TabContainer?
Help - this is driving me mad!
Yeah, that's a big problem with the grid. If you use it declaritively in a tab container, it won't render properly on the non-visible tabs. It needs to calculate height/width (even though you specify them)...as you have seen.
The way I got around it was to create the grids programatically on tab select. I posted about my solution on the dojo forums. My code sample is over on github. It's a bit too large to post here methinks. Let me know if you want it, and i'll edit my answer.
There's also a discussion on nabble with a different solution.
"resize" works like a charm! Been looking for this for a long time (didn't know what I had to search for), thanks.
I use this routine to dynamically determine if the tab has more than one datagrid, as I may not know the ID of one single grid, maybe someone else might use that, too:
dojo.query('div#container div[id^="gridNode_"]').forEach(function(node, index, arr) {
dijit.byId(node.id).resize();
});
This will check the div with id="container" (skip that part if you want to search the whole DOM) for divs with an id starting with "gridNode_" and apply "resize" to those widgets.
An alternate approach is to resize the grid upon tab element selection. Sample code
dojo.connect(dijit.byId('my_tab_container'), "selectChild", function(child){
// if second tab (could be any...) selected
if(child.id == 'mySecondTabId'){
var myGrid = dijit.byId('myGridInsideTabId');
if(myGrid != null) myGrid.resize();
}
});