Getting JSF-defined component with Javascript - javascript

I'm creating an interface using JSF, and I'd like the value of one text field to provide the default for a second if the second hasn't yet been set. The crucial code will look something like this:
<h:outputScript>
function suggestValue2() {
var value2 = document.getElementById('value2').value;
if (value2 == "") {
document.getElementById('value2').value = document.getElementById('value1').value;
}
}
</h:outputScript>
<h:inputText
id="value1"
onblur="suggestValue2();" />
<h:inputText
id="value2" />
The problem is this doesn't actually work. The actual IDs of those two input elements get prefixed with some JSF-generated values, which tanks the getElementById calls. What's the best way for me to accomplish what I'm trying to accomplish here?
Edit: I should note that this is going to appear inside a composite component, which could wind up appearing multiple times on a single page. JSF dynamically setting the actual ID represents desired behavior.

Bind the component to the view,
<h:inputText binding="#{input1}" ... />
so that you can just print its client ID elsewhere in the view by UIComponent#getClientId().
<h:outputScript>
var input1 = document.getElementById('#{input1.clientId}');
// ...
</h:outputScript>
As you mentioned that you're inside a composite component, it may be good to know that composite component's own client ID is already available via #{cc.clientId}. So the more recommended alternative would be:
<cc:implementation>
<h:outputScript>
var input1 = document.getElementById('#{cc.clientId}:input1');
// ...
</h:outputScript>
...
<h:inputText id="input1" ... />
...
</cc:implementation>
See also:
Integrate JavaScript in JSF composite component, the clean way

Jsf uses a concept "naming containers" which says the id need not be unique within a provided container. Provided the container has an Id. So if you are not giving an Id to the container jsf appends the unpredictable values before the element. With id for container it becomes containerid:elements id. And this can be used in JavaScript reliably

Related

Why would the value from a javascript selector be returned but undefined using jquery

I have a simple form called myForm I'm making as an exercise to get better at Javascript and jquery. Inside my form I have a set of radio buttons that act as boolean values to show or hide other content (in this case, 2 divs that contain a table).
I wanted to tidy up the code a bit using jquery selectors rather than the entire document.get javascript way. I'm doing this inside an ASP.NET Core 3.1 MVC application if that matters.
The following code correctly grabs the value of my radio buttons
My view
<div class="form-group">
<input type="radio" asp-for="AssignExtension" name="AssignExtension" value='true' onclick="assignExtensionSelect('true')" /> Yes
<input type="radio" asp-for="AssignExtension" name="AssignExtension" value='false' onclick="assignExtensionSelect('false')" /> No
</div>
site.js
<script>
function assignExtensionSelect(extRequired) {
let formInput = document.getElementById("myForm");
//let formInput = $("#myForm");
console.log(formInput.AssignExtension.value);
if (extRequired) {
$('#ifassignextension').show();
$('#ifnotassignextension').hide();
}
else {
$('#ifassignextension').hide();
$('#ifnotassignextension').show();
}
}
</script>
The commented line is the one that won't work correctly. Why does the code return undefined when I have formInput = $("#myForm"); but works as expected when I assign the variable using the document.getElementById syntax?
Note: I have jquery already included in the view and other functions already using it, so this is not a dependency issue or jquery not being loaded.
You're trying to use DOM properties with jQuery objects. If you're using jQuery, you have to use its selectors and methods.
let formInput = $("#myForm");
console.log(formInput.find("[name=AssignExtension]:checked").val());

Does primefaces fluidGrid provide a rowIndexVar or rowKeyVar attribute?

I am using a reponsive grid which is an extension of primefaces. However I can't seem to figure out how to get the index of items within the grid.
I tried rowIndexVar and rowKeyVar and they don't seem to work. I've included a snippet of my code below.
<pe:fluidGrid value="#{resultList}" var="showvar" rowKeyVar="rowKey" fitWidth="true" hasImages="true">
...
</pe:fluidGrid >
...
<h:link onclick="$(callme('#{showvar.showId}','#{rowKey}'));">
<h:graphicImage url="#{showvar.showImage}"/>
</h:link>
....
<h:outputScript>
function callme(id,row){
alert(id);
alert(row);
}
</h:outputScript>
I could just declare an unique index variable in my object and reference it like below, but I'd like to know if fluidGird offers a rowKeyVar attribute.
<ui:param name="itemIndex" value="#{showvar.showIndex}" />
I think what you are looking for is the varContainerId
varContainerId
Name of the request-scoped variable which contains the prefix of the client Id within pe:fluidGridItem. This property allows to get the whole clientId of a component within pe:dynaFormControl. The whole client Id is sometimes required for JavaScript or RequestContext.update(...).
See also: http://www.primefaces.org/showcase-ext/views/fluidGrid.jsf

In XForms, with Orbeon, how to update an XML instance through jQuery?

I want to perform some logic in JavaScript when users click on the span element and update current XML instance from jQuery:
(I have seen 2 similar questions online but they never got answered!)
XForms:
<xhtml:span class="buttonPlusOne" id="plusVat">+</xhtml:span>
<xf:output ref="instance('submitted_instance')/TotVATAmnt"></xf:output>
<xf:input id="changeVatTotal" ref="instance('submitted_instance')/TotVATAmnt"></xf:input>
JavaScript:
$('span.buttonPlusOne').on('click', function () {
// do some logic and increment value for 0.01
orbeonElId = $(this).siblings('#changeVatTotal').children('input').attr('id');
// alert(orbeonElId) produces right id (changeVatTotal$xforms-input-1)
// alert(newValue) produces 0.02 (for example)
ORBEON.xforms.Document.setValue(orbeonElId, newValue);
});
I can see Orbeon posting data (Firebug), but the XML Instance does not get updated (input does not update the output - even though they share same "ref" attribute).
I suppose that you're mixing up the html element's id with the XForms id (id attribute) of the xf:input element.
The Orbeon documentation shows an example: the id to use in the ORBEON.xforms.Document.setValue() function call is the id of the (server-side) XForms element. So in your example it's changeVatTotal, and not the id of the (client-side) html input element changeVatTotal$xforms-input-1. This is why there's a request showing up in firebug with no effect on the XForms instance: the server-side XForms engine doesn't find a xf:input element with the id changeVatTotal$xforms-input-1, so it doesn't know what to do with that request.
This means, too, that you (usually) don't need to compute the id of the target element, instead you can just use the "plain" XForms id attribute value of the xf:input.
Alternatively:
Is it possible to handle the "plus" button completely in XForms? You could use a xforms:trigger control and include a xforms:setvalue action on the DOMActivate event:
<xf:trigger>
<xf:label>+</xf:label>
<xf:setvalue
ev:event="DOMActivate"
ref="instance('submitted_instance')/TotVATAmnt"
value="0.01 + instance('submitted_instance')/TotVATAmnt" />
<.... more actions ...>
</xf:trigger>
I think this solution would be more stable than doing some of the work client-side and some server-side.

Javascript fails to access a JSF component by calling through its id

I am trying out the following piece of code to get access a JSF component in Javascript by referring to its Id. But this fails.
JSF component:
<p:commandLink onclick="calculatePosition(this.id)" >
<h:graphicImage url="2.png"/>
</p:commandLink>
Javascript code:
<script type="text/javascript">
function calculatePosition(idOfClicked){
alert(idOfClicked);
var $element = jQuery('#'+idOfClicked);
var offset = $element.offset();
alert(offset.top);
}
</script>
1st alert works dislaying correct id of the element thereby proving that the JS function is called & correct id has been passed but it fails to display the 2nd alert. This happens only when the id of a JSF component is passed to this JavaScript function but works fine with non JSF components.
How can I make it work correctly ?
JSF prepends IDs of parent NamingContainer components (such as <h:form>) in generated client id with : as default separator character. So for example
<h:form id="foo">
<p:commandButton id="bar" />
...
will end up in generated HTML as
<form id="foo" name="foo">
<input type="submit" id="foo:bar" name="foo:bar" />
(rightclick page in webbrowser and choose View Source to see it yourself)
The : is however illegal in CSS identifiers. This was chosen so that the enduser won't accidently use it in component IDs which would only result in inaccessible parameters and components in the JSF side. To select an element with a : in the ID using CSS selectors in jQuery, you need to either escape it by backslash or to use the [id=...] attribute selector.
var $element1 = jQuery('#' + id.replace(':', '\\:'));
// or
var $element2 = jQuery('[id="' + id + '"]');
Alternatively, since JSF 2.0 you can override the ID separator character by a javax.faces.SEPARATOR_CHAR context parameter in web.xml with a different but CSS-valid value such as -. However, you need to be careful that you don't use this character yourself in any JSF component IDs.
As a completely different alternative, you can also just pass the whole HTML DOM element itself.
<p:commandButton onclick="calculatePosition(this)" />
so that you can do
function calculatePosition(element) {
var $element = jQuery(element);
// ...
}
without fiddling with IDs.

How to relate data (id's) to links for JavaScript?

I'm trying to write a fairly complex dynamic web page, using JQuery AJAX, and I am struggling with how to relate my links (<a ...>) with the the data their tied to, such as action names, and data element ids. I have pondered several different schemes, but I'm not sure I like any of them.
Building it into onclick, which means I have to configure it in the link generation.
<a onlick="func('abc', 123)">...</a>
Inserting it into the id of the link, which means parsing it out in JavaScript.
<a id="link_abc_123">...</a>
Putting the link in a div with hidden input elements...
<div>
<a>...</a>
<input type="hidden" name="action" value="abc"/>
<input type="hidden" name="id" value="123"/>
</div>
Is there a best practice or a commonly accepted way of structuring this data?
Best practice should always be, to strictly separate Code.
That means, you shouldn't include any Javascript into your backend-source code. So personally I'm a big fan of either putting the necesarry data into the elements (your last example) when using a template-engine, or sending just the necesarry data on a separate request (JSON for instance) to the client.
Using jQuery, it's a very convinient way to create data- attributes, where you can store any information, while jQuery will translate the values from those attributes into the data expandos. For instance:
<div id="test" data-foo='bar' data-test='{"cool": "stuff"}'>Just a div</div>
When selecting that element with jQuery var $test = $('#test'), you can access:
$test.data('foo') // === 'bar'
$test.data('test').cool // === 'stuff'
Read more: http://api.jquery.com/data/
With HTML5, you have the luxury of using data-* attributes - for example:
...
Which jQuery actually has support for - calls to $('a').data() will include the data-* values in it.
For simple things, I use a function like:
function getIdStr(i, sDelim) {
if (typeof i != "string") {
var i = $(i).attr("id");
}
var arr = i.split(sDelim || /_|-/);
if (arr.length > 1) {
return arr[arr.length-1];
} else {
return "";
}
}
// usage
$(function(){
$(".data .action").click(function(){
doSomething(getIdStr(this)); // -> 123
});
});
For something heavier, you might try to attach a data to the topmost container.
i would go with the new Custom Data Attributes HTML5 brings along.
Just use this <a data-action="foo" data-id="bar">...</a>
Also, jQuery already has support to get these data-attributes
You can add a custom property to the input and access it in javascript.
eg
<input type="hidden" name="action" value="abc" yourproperty='<%= Eval("YourDataID") %>'/>

Categories

Resources