PrimeFaces p:blockUI blocking a certain component dynamically (on JSF EL condition)? - javascript

How do you make PrimeFaces p:blockUI block a certain component dynamically, that is on EL condition?
USE CASE:
The condition at hand is basically a mode that the user can be in on a page: if there are any exceeded collisions and they are currently shown at the users request then block the navigation tree (show exceeded collisions mode, nav tree blocked), otherwise we are in regular view and the navigation tree should be unblocked (show regular collisions mode, nav tree unblocked).
The problem currently is, that when in "exceeded mode", when a status change dialog changes the status to non-exceeded, the page renders/updates itself correctly back to regular mode, but the shadow on the navigation tree is still there. Since we're in regular mode now, I need some way to unblock if there are no more exceeded collisions.
Understood? :-)
OK, here's the bean property first:
/*
* The logic of this method ensures that after status update the
* mode is automatically put back into regular view if no followup
* date exceeded collisions exist.
*/
public boolean isFollowupExceededCollisionsShown()
{
return getFollowupExceededCount() > 0 ? this.followupExceededCollisionsShown : false;
}
1st try:
Looking at the VDL docs http://www.primefaces.org/docs/vdl/3.4/primefaces-p/blockUI.html revealed
some blocked attribute.
<p:blockUI widgetVar="explorerBlocker"
block=":explorer-form"
blocked="#{collisionManager.followupExceededCollisionsShown}" />
The above however does absolutely nothing.
2nd try:
Looking at the showcase http://www.primefaces.org/showcase/ui/blockUI.jsf, the client API seemed to be more adequate.
The idea was to call either show() or hide() depending on a ValueExpression when pressing OK on the status change dialog:
<p:dialog id="state-change-dialog"
widgetVar="stateChangeDialog"
modal="true"
appendToBody="true">
<h:form>
<ui:include src="/view/collisionmgmt/collisionStatusChangeGrid.xhtml" />
<h:panelGroup layout="block" styleClass="center-button-panel">
<p:commandButton id="save-button"
icon="ui-icon ui-icon-disk"
value="OK"
action="#{collisionManager.performStatusChange}"
process="#form"
update=":explorer-form:tree :collision-form:period-grid :collision-form:list :collision-form:growl"
oncomplete="stateChangeDialog.hide();" />
<p:commandButton icon="ui-icon ui-icon-close"
value="Cancel"
update=":collision-form:list"
onclick="stateChangeDialog.hide();"
immediate="true" />
</h:panelGroup>
</h:form>
</p:dialog>
The idea was to somehow extend the OK button's oncomplete="" with a call to explorerBlocker.show(); or explorerBlocker.hide(); depending on the new value of the EL #{collisionManager.followupExceededCollisionsShown}.
There are two basic variants I tried:
oncomplete="stateChangeDialog.hide(); #{collisionManager.followupExceededCollisionsShown ? 'explorerBlocker.show();' : 'explorerBlocker.hide();' }"
oncomplete="stateChangeDialog.hide(); if ( #{collisionManager.followupExceededCollisionsShown} ){ explorerBlocker.show(); } else { explorerBlocker.hide(); }"
The status change dialog is closed all the time, but the logic above isn't kicking in.
I must be making something inherently wrong here. I'm suspecting the OK button's oncomplete EL expression doesn't get re-evaluated when being clicked. Adding an #this to the update list doesn't change anything.
update="#this :explorer-form:tree :collision-form:period-grid :collision-form:list :collision-form:growl"
How is my problem best solved, where "best" is JSF-only first, then PrimeFaces-specific (and ideally, where this is documented!).
Is it possible to use the blockUI blocked="#{?}" attribute with EL?
Thanks

One way is to call hide() and show() method of BlockUI from Managed Bean.
You can do that by using RequestContext:
RequestContext.getCurrentInstance().execute("widgetVar.show()");
Another is you can pass the variable to JavaScript function and then let the Javascript function take care of that for you.
onClick="func(#{elvariable})"
<script type="text/javascript">
function func(value)
{
if(value==something){
widgetVar.show();
}else{
widgetVar.hide();
}
}
</script>

Related

Why isn't NVDA reading my custom error alert consistently?

I've set up a custom html page template for a client's azure b2c login page and the inline errors are getting read back as-expected (can provide additional details about those upon request if it'd be beneficial), but I'm a bit stumped as to why the page-level errors aren't getting read back as well.
The following are the relevant snippets of html from the template that get rendered during the initial page load:
<!-- B2C-Generated Form Element (all relevant html changes happen within this element) -->
<form id="localAccountForm" action="JavaScript:void(0);" class="localAccount" aria-label="Sign in to Your Account">
<!--B2C-Generated Error Container (prior to error / this html is generated/injected by Azure B2C when the page is loaded) -->
<div class="error pageLevel" aria-hidden="true" role="alert" style="display: none;">
<p class="remove"></p>
</div>
<!-- Custom Error Container (prior to error / this html gets added to the template by jQuery once the window is ready) -->
<div role="alert" style="" class="errorContainer no-error" aria-hidden="false">
<p id="pageError" role="" aria-hidden="true" style="" class="remove"></p>
</div>
After the initial content loads (both the content from Azure B2C, as well as the modifications from jQuery), the following logic gets run to ensure all of the error elements on the page are set up properly (or at least that's the intent) & eliminate some differences that may otherwise cause some problems:
initializeAlerts([
'#pageError',
'label[for="signInName"] + .error > p',
'.password-label + .error > p'
]);
// Below functions are loaded & run from a separate .js file:
function initializeAlerts(errorSelectors) {
errorSelectors.forEach((s) => {
// Store the current error & whether or not inline styles were attempting to hide it
var errorMsg = $(s).html();
var errorStyle = `${$(s).attr('style')} ${$(s).parent().attr('style')}`;
// Ensure the parent element has the role="alert" attribute (rather than the error itself)
$(s).attr('role', '');
$(s).parent().attr('role', 'alert');
// Default both the error element & it's parent to be visible
$(s).removeClass('remove').attr('aria-hidden', 'false').attr('style','');
$(s).parent().addClass('errorContainer').addClass('no-error').removeClass('remove').attr('aria-hidden', 'false').attr('style','');
// If an error message is NOT present, add a class to the parent for styling purposes
if (errorMsg) {
$(s).parent().removeClass('no-error');
}
// If the error was supposed to be hidden dynamically via Azure B2C (i.e. it's a standard error that's prepopulated & simply gets shown/hidden), ensure the error itself is hidden (NOT the parent)
if (errorStyle.indexOf('none') > -1) {
$(s).addClass('remove').attr('aria-hidden', 'true');
$(s).parent().addClass('no-error');
}
// If/when the error gets updated, ensure it gets displayed/read
callbackOnDOMUpdate([s], function() {
var currentError = $(s).html();
if (currentError) {
$(s).removeClass('remove').attr('aria-hidden', 'false').attr('style','');
$(s).parent().removeClass('no-error');
} else {
$(s).addClass('remove').attr('aria-hidden', 'true').attr('style','');
$(s).parent().addClass('no-error');
}
});
});
}
function callbackOnDOMUpdate(selectors, callback) {
selectors.forEach(selector => {
$(function() {
var target = document.querySelector(selector);
var observer = new MutationObserver(function(mutations, observer) {
callback();
});
observer.observe(target, {
subtree: true,
childList : true,
characterData : true
});
});
});
}
After all of that runs, if the user enters an incorrect user/pass combination, an error is returned to the page (via the "B2C-Generated Error Container") which looks something like this (depending on the specific error returned):
<!--B2C-Generated Error Container within form (after receiving error) -->
<div class="error pageLevel" aria-hidden="false" role="alert" style="display: block;">
<p class="remove">Unable to validate the information provided.</p>
</div>
Though, the client wants some verbiage/styling changes made, so rather than showing that message as-is, a "remove" class is added to it (which is associated with a display: none !important css rule) and my custom error container is updated to show something similar to the message below (again, actual message may vary depending on the message returned from b2c):
<!-- Custom Error Container (after receiving error) -->
<div role="alert" style="display: block">
<p id="pageError">
<strong>We're sorry, the information entered does not match what we have on file.</strong>
Please try re-entering the information below or you can recover your login info.
</p>
</div>
Unfortunately, while this DOES appear to get read as expected every once in a while, most of the time, the only message I hear read is "Sign in to Your Account form continue button" (which seems to be a combination of the aria label for the form element the changes are nested within, the form element name itself & the name of the last button the user clicks prior to seeing the page update).
I've tried to ensure that the error itself is nested as a child element within a parent element that:
Is always visible (from both a css & aria perspective)
Has role='alert'
(and simply show/hide the error itself via the addition/removal of the "remove" class)
... but I must be missing something somewhere, so any help anyone can offer would be appreciated.
So... as it turns out, all of the above code was fine as-is (with the exception of the fact that #1 above wasn't entirely true). The issue turned out to be related to the following...
When the user clicks the "sign in" button, I used $('body').removeClass('loaded'); which in turn would cause:
A loading animation to be displayed
The element containing all page content to be set to display: none;
When an error was detected, I would then similarly fire off the following commands (in this order):
$('body').addClass('loaded'); (thus making everything visible again)
the logic to read the system error that was returned & populate the custom error area of the code accordingly
... so, I tried removing all jquery related to the loading animation to see if perhaps the problem was related to that & sure enough, the error was read as expected.
That being said, I believe the real issue at play here was that the error/alert update was getting completed before all visibility-related changes had kicked through from the css side & therefore NVDA wasn't reading the alert because, from NVDA's perspective, the error was still hidden.
... hopefully the documentation of this experience helps someone else down the road!

How to dynamically update google recaptcha sitekey?

I have a plain js + jQuery app with a button protected by google recaptcha, everything works as expected, but I'm failing to update the sitekey on the fly. The reason I want this is that I have a couple of environments (staging, test, production etc.) and I'd like to have a separate sitekey for specific envs (in order to separate the test stats from data from real users).
I'm able to change the attribute on my recaptcha element, but it looks like the attributes are taken by the script on initialising the whole thing, how can I refresh/reset the widget to accept the new sitekey?
I've been experimenting with reset and render methods, but to no effect so far.
<div
id="google-recaptcha"
class="g-recaptcha"
data-sitekey="this-will-be-replaced-anyways"
data-callback="onSubmit"
data-size="invisible"
></div>
if (grecaptcha) {
$('#google-recaptcha').attr({
'data-sitekey': 'my-real-sitekey'
});
}
I think you are looking for Explicitly render the reCAPTCHA widget
1.Specify your onload callback function. This function will be called by JavaScript resource(see the next step)
<script>
function onloadCallback() {
grecaptcha.render('myCaptcha1', {
'sitekey': 'sitekey value', // required
'theme': 'light', // optional
'callback': 'onloadCallback' // optional
});
}
</script>
2.Insert the JavaScript resource, setting the onload parameter to the name of your onload callback function and the render parameter to explicit.
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"></script>
3.Define the target HTML control that is supposed to be a captcha on your page.
<div id="myCaptcha1"></div>
I hope it helps you as helped me, good luck

Use inputs in AngularJS Material bottom-sheet

The problem
I'm designing a Cordova hybrid app using the (very) new AngularJS Material Design dependency.
I have a log in form in a bottom sheet called via $mdBottomSheet service. Example below:
$scope.showLogin = function ($event) {
$mdBottomSheet.show({
templateUrl: 'views/login/login.html',
controller: 'loginCtrl'
})
};
The contents of views/login/login.html are:
<md-bottom-sheet ng-controller="loginCtrl" layout="column">
<form name="signInForm" ng-submit="submitPassword()">
<md-list>
<md-item>
<md-progress-circular md-mode="indeterminate" ng-show="loading"></md-progress-circular>
</md-item>
<md-item>
<md-text-float label="Email address" ng-model="username" required>
</md-text-float>
</md-item>
<md-item>
<md-text-float type="password" label="Password" ng-model="password" required>
</md-text-float>
</md-item>
<md-item>
<md-button class="md-primary md-raised submit" type="submit">Sign in</md-button>
</md-item>
</md-list>
</form>
</md-bottom-sheet>
Everything runs and displays just fine.
HOWEVER! when I go to click on the input, the focus is never given to the input, and instead the md-bottom-sheet element is dragged back and forth. Clicking on the button (when not disabled) fires just fine, but the click on the input to give it focus is never recognized.
Things I've tried
Adding ng-click="return false", 'ng-click="$event.preventDefault()"`
Adding a -webkit-transform: translate3d(0, 80px, 0) !important; rule to the CSS, as that's the default state of the property which is being altered on drag.
Adding angular.element('md-bottom-sheet').on('click', function() { return false; }); to an ng-init block.
Doing the same as above (using jQuery to try to ham-hand this) on the dragstart event.
The question
How can I use an input in a bottom sheet with material design, given I've tried every workaround that I know to get this to work for touch?
Caveats
1. I would love to offer a live example for this, but I can't find a CDN source for angular/material
2. This only happens on a mobile device, no idea if it will happen on a mobile website as I've only tested it in a Cordova hybrid app
3. There are no examples of this I've found via search, so I can't even point you to a resource that might emulate the problem.
4. Basically, this is going to be very hard to reproduce.
Update
One possible fix I've identified:
The following block in the function BottomSheet(element) "class" for the provider has the following:
function onTouchStart(e) {
e.preventDefault();
/* do the rest of the code */
}
Changing that e.preventDefault() to the following does allow normal input behavior, but requires that I fork their repo.
if (e.target.tagName.toLowerCase() !== 'input')
e.preventDefault()
Is there a solution that doesn't require me to fork this for such a minor change?
You were near the solution. You had to use stopPropagation() instead of preventDefault(). Here is the simple approach:
Directive (.coffee)
angular.module('Expedite').directive 'stopEvent', ->
restrict: 'A'
link: (scope, element, attr) ->
element.bind attr.stopEvent, (e) ->
e.stopPropagation()
View (.slim)
input ng-model="user.email" stop-event='touchstart'
This way you will not have to fork material repo.
So I'm running into exactly the same issue. The code above works for pre 0.6.1 - though doesn't work so well afterwards.
I have however found that moving that e.preventDefault() (minus the if() logic) down to the onTouchMove(e) function, seems to fix all scroll glitches (atleast that I can see) - and still allows inputs to function normally.
Agree, it would be super if the ng-material guys could incorporate something like this into the repo to make $mdBottomSheet implementation more flexible.

DataTables: Confirm user wishes to page

We have a number of jQuery DataTables that all use server side processing. We have paging and sorting set up, and all is working well. In these tables there is at least one column of checkboxes to allow selecting of rows to do some kind of processing on. We want to confirm that the user wishes to page or sort if there are any checkboxes checked.
What I thought I could do (and can't)
"fnPreDrawCallback" : function(table) {
if (CullAddress.AddressIsChecked()) {
var $warningDiv = $('div#pageWarning');
var warningText = "One or more Addresses are selected for Excluding or Tagging. Are you sure you wish to nvaigate away?";
$warningDiv.find("div#pageWarningText").html(warningText);
$warningDiv.dialog({
resizable: false,
height: "auto",
width: "auto",
modal: true,
buttons: {
"Leave Page": function () {
CullAddress.resetWarningText();
$warningDiv.dialog('close');
},
"Stay On Page": function () {
CullAddress.resetWarningText();
$warningDiv.dialog('close');
return false;
}
},
});
}
},
Initially I thought this would be simple, but now, it is getting a bit hefty, and I am not sure of what the right way is. I am trying to use the fnPreDrawCallback, and initially I intended to create and display a jQueryUI Dialog, and have the buttons determine whether or not to return false; out of the callback thus staying on the page, or allowing the page/sort to go through.
I now understand that javaScript does not work that way. I suspect I will have to do the following, but before I go through that trouble I want to ask if there is a more concise (and reusable) way of doing this.
In fnPreDrawCallback, get properties to describe the intended set page/sort (e.g. offset, pageSize, sSortDir, iSortCol, etc).
Determine via dialog whether to continue or stay on page
Use aforementioned properties to construct the GET for the datatable to bypass the fnPreDrawCallback
Am I making this more difficult that it needs to be? Surely I am not the first person to want to do this, but for the life of me, I can find an example, or I cant figure out the keywords I should be searching for...
Any helps?
Link to working example: http://jsfiddle.net/6frQZ/3/
As already discussed in the comments to the question, I tried to circumvent the default behaviour of DataTables to fit your needs and created an example on jsFiddle to show, including numbered-pagination and sorting.
Basically, you'll need to unbind the event-handlers, that the DataTables - plugin binds to it's components, like so:
$('.dataTables_paginate a').unbind();
$('.dataTables_wrapper thead th').unbind();
Using .unbind without a parameter will unbind any event-listener on the element, so be careful when using this.
Gladly, the DataTables - API provides functions that let you call the internal paging and sorting-methods yourself, named fnSort (API-Link) and fnPageChange (API-Link).
To keep it simple, i just used a confirm - Box to ask for the user-interaction:
var userInteraction = confirm("Do you really want to change the page?");
if(userInteraction){
oTable.fnPageChange(dir);
$('.dataTables_paginate span a').unbind();
}
but all you'd need to do is call the DataTables-functions inside of your "Leave Page" - callback you already provided in the code.
Note: When it comes to the numbered buttons of the paging: It seems like DataTables regenerates those everytime the paging is changed, thus I need to unbind the event-Handlers again after every page-change.
The rest is simple yet not very elegant code, in which I just look for certain classes to know, what button was clicked or which state the sorting-header is in.
Excerpt:
var dir = "",
$this = $(this);
if($this.hasClass('previous')){
dir = "previous";
}else if ($this.hasClass('next')){
dir = "next";
}else if($this.hasClass('first')){
dir = "first";
}else if($this.hasClass('last')){
dir = "last";
}else{
dir = parseInt($this.text(),10)-1;
}

Ember Collapsible Container

I'm using Ember.js with handlebars and I need to make a div within my page collapse/expand when clicked. I know how to do this in jQuery, but I can't use any jQuery. Does anyone know how to accomplish this? Also I don't want to just toggle a hide attribute, I need the full sliding up and down feature for collapsing. If anyone has any ideas, I'd really appreciate it.
Thanks
Clicking on your view will cause a click event to be triggered. You can code your animation in any manner you want inside a click event handler in your view:
CollapsableView = Ember.View.extend({
click : function(event) {
this.$().toggle('fast');
}
})
The proper way of doing this in Ember is via the awesome Liquid Fire addon.
The outline:
Install Liquid Fire into your project.
Define a transition like this:
this.transition(
this.hasClass('transition-spoiler'),
this.toValue(true),
this.use('toDown'),
this.reverse('toUp')
);
In your controller/component, create a property spoilerIsVisible and a toggleSpoiler property:
spoilerIsVisible: false,
actions: {
toggleSpoiler: function() {
this.toggleProperty('spoilerIsVisible');
}
}
In your page/component template, create a button and a spoiler wrapper like this:
<button {{action 'toggleSpoiler'}}>
{{if spoilerIsVisible 'Show spoiler' 'Hide spoiler'}}
</button>
{{#liquid-if spoilerIsVisible class="transition-spoiler"}}
<p>Dumbledore dies</p>
{{/liquid-if}}
Note that you can wrap steps 3-4 into an x-spoiler component or something.
I do something similar, but with a tree-structure. I have written a blog post about this previously here: http://haagen-software.no/blog/post/2012-05-05-Ember_tree
It has the features you need in it, in that it adds and removed elements from the DOM when the nodes are clicked on.
A working example can be seen in an app I am currently building here: https://github.com/joachimhs/EurekaJ/tree/netty-ember/EurekaJ.View/src/main/webapp

Categories

Resources