How to apply Primefaces effect when component is rendered - jsf

i have drag panel and drop panel, and when component is dropped in the drop panel i show a new panel and hide the old panel based on render boolean attribute as follows:
1- xhtml:
<p:outputPanel id="oldPanel" rendered=#{myBean.old_panel_rendered}> .... </p:outputPanel>
<p:outputPanel id="newPanel" rendered=#{myBean.new_panel_rendered}> .... </p:outputPanel>
2- bean:
old_panel_rendered=true;
new_panel_rendered=false;
public void onComponentDrop(DragDropEvent ddEvent) {
old_panel_rendered=false;
new_panel_rendered=true;
}
how to execute an effect for newPanel when it gets rendered and execute an effect for oldPanel when it gets unrendered.
please advise, thanks.

Call js function which will apply the effects when a new item droppped:
<p:ajax listener="#{bean.onDrop}" onstart="applyEffects();" update="newPanel" />
Function is:
function applyEffects() {
var oldPanel = $(document.getElementById('oldPanel'));
var newPanel = $(document.getElementById('newPanel'));
oldPanel.css({"display":"none"});//or oldPanel.fadeOut(500) which looks fancy
newPanel.css({"display":"inline"});
newPanel.effect("highlight",
{color:"#87FF7A"}, 1500);
}
Don't forget to give exact client id of components when calling document.getElementById. You can detect it via your browser's developer settings. If there will be a problem, you can drop update="newPanel" or maybe you can try update="oldpanel newpanel".
To be able to apply it for specific panel:
public void onComponentDrop(DragDropEvent ddEvent) {
int id = event.getData();//or sth.similar to getId
RequestContext.getCurrentInstance().addCallbackParam("index", id);
}
Upper code adds a parameter to ajax response it can be retrieved by:
function applyEffects(xhr,status,args) {
var oldPanel = $(document.getElementById('oldPanel'));
var newPanel = $(document.getElementById('newPanel'));
if(args.id=='oldPanel') {//oldPanel or whatever which equals to eventID
oldPanel.css({"display":"none"});//or oldPanel.fadeOut(500) which looks fancy
}
newPanel.css({"display":"inline"});
newPanel.effect("highlight",
{color:"#87FF7A"}, 1500);
}
You should call this from p:ajax oncomplete="applyEffects(xhr,status,args);". I am coding directly here, therefore can be few mistakes which can be seen on IDE easily.

Related

Reload page from backing bean

In my code, I'm expanding treeNode in left frame that is selected through navigation links present in right frame. It works but everytime I click the link on right frame, I have to refresh the right frame manually. I tried to reload the page from backing bean using javascript code but it's not working. Can anyone please help me to figure out why it's not getting executed.
Thanks in advance for helping me out.
Below is the code I'm using.
public void expandTreeView( TreeNode selectedNode )
{
if ( selectedNode != null )
{
selectedNode.getParent().setExpanded( true );
}
RequestContext rc = RequestContext.getCurrentInstance();
rc.execute("window.location.reload(true)");
}
You need to combine a JS Function with remoteCommand, it will look like this :
myHTML.xhtml
<p:commandLink id="commandLink" onclick="myFunction(nodeSelected)" >
...
</p:commandLink>
Also add a JS function
<script type="text/javascript">
function myFunction(x) {
...
}
</script>
and finally combine it with a p:remoteCommand it allows you to call a managedBean method from your JS function
You can see Primefaces remoteCommand Example or simply look to this SO post Invoking a p:remoteCommand via a JavaScript function passing a message local to that function to another function through the “oncomplete” handler
Hope that helped you.

autoComplete - action & onClick

I have below code, There can be a case when user types something there is no auto suggestion available, but still i want the text entered by the user.
My onClick function does gets called, but i want to get the value the user has types.
I have tried below approaches, but none seems to be working.
AutoComplete tag
<p:autoComplete id="cityInput"
value="#{myBean.text}"
style="margin-right:15px;"
completeMethod="#{myBean.fetchData}"
global="false"
forceSelection="true"
maxResults="5">
</p:autoComplete>
Button
<p:commandButton id="searchCity"
value="Search"
icon="ui-icon-search"
onclick="captureSearchText();"
action="#{myBean.search}"/>
Script
<script type="text/javascript">
function captureSearchText(){
alert("88");
//var searchVal = myAutoComplete.input.val();
//var searchVal = document.getElementById("cityInput");
//alert(searchVal.value);
//return true;
}
</script>
Edit Section
When I press ENTER key, I get the searched string in my fetchData, maybe because it is p:commandButton. But on click of button also I want to get the searched text. I hope it is clear now. What can I do to get search text on mouse click of search button. tried invoking JavaScript function.
In the completeMethod #{myBean.fetchData} just return the search string as entered by the user if the real 'searching' does not yield any results.
public List<String> fetchData(String query) {
List<String> results = searchService.search(query);
if (results.isEmpty() {
results.add(query);
}
return results;
}
There is in fact nothing 'jsf' to this, plain java creativity.
But the user still has to select it then unless the autocomplete has an autoselect option. Not sure about this. But you can also store the query string in a variable server side and use that if the user does not select anything (setting the field to required is better then imo)
public List<String> fetchData(String query) {
List<String> results = searchService.search(query);
this.queryString = "";
if (results.isEmpty() {
results.add(query);
this.queryString = query
}
return results;
}
Or move it outside the ifEmpty if you always want to store it
forceSelection="true" was an issue actually, it was forcing a selection.
I have removed it and it ran smoothly.
Answer by #Kukeltje is also good, but that is something a workaround, and forceSelection="true" was actually the root cause.

How to count the number of times a h:outputLink was clicked?

I have a PrimeFaces page with following code:
<pm:content id="content">
<p:dataList value="#{likeditems.likedItems}" var="item" pt:data-inset="true" paginator="true" rows="5">
<f:facet name="header">
Products you liked in the past
</f:facet>
<h:outputLink value="#{item.url}" target="_new">
<p:graphicImage name="http://example.com/my-product-mobile/f/op/img/underConstructionImage.jpg" />
<h2>#{item.title}</h2>
<p>Approx. #{item.price} (for most up-to-date price, click on this row and view the vendor's page)</p>
</h:outputLink>
<f:facet name="footer">
Products you liked in the past
</f:facet>
</p:dataList>
</pm:content>
When the user clicks on the h:outputLink, I want 2 things to happen:
A new page with URL item.url is opened in the browser.
Method likeditems.itemLinkClicked(item) is invoked (in that method I update the number of times a particular link was clicked).
First thing is already working (target="_new").
How can I implement the second one (method call for updating the number of times the link was clicked) without the first ceasing to work?
First thing is already working (target="_new").
The target should actually be _blank.
How can I implement the second one (method call for updating the number of times the link was clicked) without the first ceasing to work?
The simplest (naive) JSF-ish way would be triggering a <p:remoteCommand> on click.
<h:outputLink value="#{item.url}" target="_blank" onclick="count_#{item.id}()">
...
</h:outputLink>
<p:remoteCommand name="count_#{item.id}" action="#{likeditems.itemLinkClicked(item)}" />
But this generates lot of duplicate JS code which is not very effective. You could put it outside the data list and fiddle with function arguments. But this still won't work when the enduser rightclicks and chooses a context menu item (open in new tab, new window, new incognito window, save as, copy address, etc). This also won't work when the enduser middleclicks (default browser behavior of middleclick is "open in a new window").
At ZEEF we're using a script which changes the <a href> on click, middleclick or rightclick to an URL which invokes a servlet which updates the count and then does a window.open() on the given URL.
Given a
<h:outputLink value="#{item.url}" styleClass="tracked" target="_blank">
the relevant script should basically look like this:
// Normal click.
$(document).on("click", "a.tracked", function(event) {
var $link = $(this);
updateTrackedLink($link);
var trackingURL = $link.attr("href");
$link.attr("href", $link.data("href"));
$link.removeData("href");
window.open(trackingURL);
event.preventDefault();
});
// Middle click.
$(document).on("mouseup", "a.tracked", function(event) {
if (event.which == 2) {
updateTrackedLink($(this));
}
});
// Right click.
$(document).on("contextmenu", "a.tracked", function(event) {
updateTrackedLink($(this));
});
// Update link href to one of click count servlet, if necessary.
function updateTrackedLink($link) {
if ($link.data("href") == null) {
var url = $link.attr("href");
$link.data("href", url);
$link.attr("href", "/click?url=" + encodeURIComponent(url));
}
}
and the click servlet should look like this (request parameter validation omitted for brevity):
#WebServlet("/click")
public class ClickServlet extends HttpServlet {
#EJB
private ClickService clickService;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String url = request.getParameter("url");
clickService.incrementClickCount(url);
response.sendRedirect(url);
}
}
Note that this way the target="_blank" isn't necessary. It would only be used by users who have JavaScript disabled. But then many more other things on the website wouldn't work anyway, including the above JS tracking. It this is also really your concern, then you'd better just put that click servlet URL directly in <h:outputLink value>.

Navigate after successfull action on commandButton

I am building a JSF application using Primefaces mobile (0.9.3 with Primefaces 3.5).
I hava a login page, on this login page there is a login button defined as followed:
<p:commandButton value="#{msg['label.login']}" action="#{loginBean.login}" update="usernameMsg" />
The loginBean.login method is defined as followed:
public String login() {
try {
//do login
return "#loggedInTarget?transition=fade";
} catch (UserNotValidException e) {
//User not valid, display exception
FacesContext
.getCurrentInstance()
.addMessage(
"loginForm:username",
new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"",
messageService
.getMessage("error.message.loginNotSuccessfull")));
}
return null;
}
In my main xhtml (the same in which the button is defined), I have defined the following addtional views:
<pm:view id="registerTarget">
<ui:include
src="/WEB-INF/c52bc19d58a64ae4bd12dec187a2d4b7/register.xhtml" />
</pm:view>
<pm:view id="loggedInTarget">
TEST
</pm:view>
I do it that way because I want to use transitions within Primefaces mobile. To get to the register page I use a p:button which works fine:
<p:button value="#{msg['label.register']}"
href="#registerTarget?transition=fade" />
Why is my login button not working? I also tried to put in "#loggedInTarget?transition=fade" directly as action, but this didn't work either :(
I need the action, because I have to check if the user credentials are valid. If they are valid I want to display my loggedInTarget.
How can I achieve it?
Thanks in advance!
EDIT:
The button is inside a form.
The navigation case outcome value must represent a view ID, not an URL (for sure not a hash fragment). Try with <p:button outcome="...">, and you'll see that it fails over there as well.
Your closest bet is sending a redirect.
public void login() throws IOException {
// ...
externalContext.redirect("#loggedInTarget?transition=fade");
// ...
}
This is also exactly what the <p:button href="..."> does under the covers: causing a GET request on the given URL.

how to disable multiple h:inputtext fields in JSF

In my home page(.xhtml),I'm having 4 h:inputText box and one serach button(h:commandButton)in JSF page
<h:inputText id="stlID" value="#{searchSettlementTransRequest.stlmtTransId}" name='stlmt'>
<h:inputText id="pmtID" value="#{searchSettlementTransRequest.pmtTransId}" name='pmt'>
<h:inputText id="AgentID" value="#{searchSettlementTransRequest.AgentId}" name='agnt'>
<h:inputText id="AgencyID" value="#{searchSettlementTransRequest.AgencyId}" name='agncy'>
<h:commandButton id="tranSearchBtn" styleClass="valign first button-search sSearch" action="stlmtSubmit"/>
My requirement is:
on entered data in 1st input field the other input fields should be disabled(ie. 2,3,4).
on entered data in 2nd input field the other input fields should be disabled(ie. 1,3,4). and so on..
Note: at the sametime if user entered data in 1st or 2nd or 3rd or 4th (user dynamic)field and delete the same before leaving out the input field in such case all field should be enabled.
When i get response back(empty or non-empty data response) after clicking "search" button now all the input fields should be enabled for another search(now user may input data in any of the 4 fields)
If you want the inputs to be enabled after each page load you can have a flag representing the page is loaded and set that flag to false after each ajax request through an listener. And to ensure that the flag is set to true after complete page loads you can use preRenderView event. You just need a method to be executed for preRenderView event which set the flag to true if the page is loaded through complete request rather than an ajax request. If the page is loaded with ajax request, faces-request parameter will be set to "partial/ajax".
public static final String INITIAL_VALUE = ""; // I assume your fields are integer and they are initialized to INITIAL_VALUE in the first place
public Boolean pageIsLoadedWithFullRequest = true;
public void preRenderView()
{
//check whether the request is an ajax request or not
Map<String, String> requestHeaderMap = FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderMap();
if(!"partial/ajax".equals(requestHeaderMap.get("faces-request")))
{
pageIsLoadedWithFullRequest = true;
}
And if you want to disable other inputs whenever the user writes into one of the fields, you can make an ajax call after a blur event to change the state of other input fields and set the pageIsLoadedWithFullRequest flag to false in a listener.
public void madeAjaxRequest()
{
pageIsLoadedWithFullRequest = false;
}
public Boolean getStlIDEnabled()
{
return pageIsLoadedWithFullRequest || !stlmtTransId.equals(INITIAL_VALUE) && pmtTransId.equals(INITIAL_VALUE) && AgentId.equals(INITIAL_VALUE) && AgencyId.equals(INITIAL_VALUE);
}
public Boolean getPmtTransIdEnabled()
{
return pageIsLoadedWithFullRequest || stlmtTransId.equals(INITIAL_VALUE) && !pmtTransId.equals(INITIAL_VALUE) && AgentId.equals(INITIAL_VALUE) && AgencyId.equals(INITIAL_VALUE);
}
public Boolean getAgentIdEnabled()
{
return pageIsLoadedWithFullRequest || stlmtTransId.equals(INITIAL_VALUE) && pmtTransId.equals(INITIAL_VALUE) && !AgentId.equals(INITIAL_VALUE) && AgencyId.equals(INITIAL_VALUE);
}
public Boolean getAgencyIdEnabled()
{
return pageIsLoadedWithFullRequest || stlmtTransId.equals(INITIAL_VALUE) && pmtTransId.equals(INITIAL_VALUE) && AgentId.equals(INITIAL_VALUE) && !AgencyId.equals(INITIAL_VALUE);
}
<f:event type="preRenderView" listener="#{searchSettlementTransRequest.preRenderView()}"/> <!--This will ensure that preRenderView method will be called right before rendering of the view ends -->
<h:inputText id="stlID" value="#{searchSettlementTransRequest.stlmtTransId}" name='stlmt' disabled="#{not searchSettlementTransRequest.stlIDEnabled}"/>
<h:inputText id="pmtID" value="#{searchSettlementTransRequest.pmtTransId}" name='pmt' disabled="#{not searchSettlementTransRequest.pmtTransIdEnabled}"/>
<h:inputText id="AgentID" value="#{searchSettlementTransRequest.AgentId}" name='agnt' disabled="#{not searchSettlementTransRequest.AgentIdEnabled}"/>
<h:inputText id="AgencyID" value="#{searchSettlementTransRequest.AgencyId}" name='agncy' disabled="#{not searchSettlementTransRequest.AgencyIdEnabled}"/>
If you want to disable them through ajax you should add the f:ajax command and render all four of these inputTexts. Example:
<h:inputText id="stlID" value="#{searchSettlementTransRequest.stlmtTransId}" name='stlmt' disabled="#{not searchSettlementTransRequest.stlIDEnabled}">
<f:ajax event="blur"
listener=#{searchSettlementTransRequest.madeAjaxRequest()}
execute="stlID pmtID AgentID AgencyID"
execute="stlID pmtID AgentID AgencyID"/>
</h:inputText>
Unrelated It is not a good practice to capitalize the first letters of variables such as AgentId, AgencyID, it should be better if you change them to agentId and agencyID.
Cheers
If you want to do this only in jsf just have to add some AJAX calls. Some suggestions below:
Add to all your <h:inputText attribute disabled with value of #{searchSettlementTransRequest.getDisabled('this_component_id')". So the whole thing will look like this:
<h:inputText id="someId1" value="#{searchSettlementTransRequest.someValue}" name="stlmt" disabled="#{searchSettlementTransRequest.getDisabled('someId1')}"/>
Next in your bean processing this request change settter for `stlmTransId, pmtTransId, AgentId, AgencyId' that their will mark that this value is being set:
public void setXXX(XXX xxx) {
// your actual code comes here and at the end set that this component was changed
actuallyChanged = id_of_input_text_component_corresponding_to_this_value_as_string;
}
Of course you have to add field String actuallyChanged to your bean.
Method getDisabled(String compId) will know looks:
public String getDisabled(String compId) {
if (actuallyChanged.equals(compId)) {
return "";
else {
return "disabled";
}
}
For all your components you have also add AJAX call like this:
<f:ajax event="change" execute="#this" render="stlID pmtID AgentID AgencyID"/>
So know when value of one component will change all other will be disabled. But there is immposible to make them enabled after call is make because without cliend side scripting you cannot easly make them available on select or focus gained or anything.
If you like to disable components temporary, for example when user type in one component other all disabled, but when he select some of them then this one selected is enabled and all other, included this in which he firstly write text, will be disable. In that case better aproach is to use client side scripting for this, JavaScript for example.
Update to exactly match your problem in JavaScript (jQuery)
add to all components an event like this:
onkeyup="$('#thisComponentId').attr('disabled', false); $('#otherComponentId').attr('disabled', true); $('#otherComponentId').attr('disabled', true); "
After your comment you can change it to:
onkeyup="if ($('#thisComponentId').val() && $('#thisComponentId').val() == '') { // here set enable for all components } else { // here set disable for all other components }
This isn't very sophisticated way but it should work. Know when you start typing in one component other will be disabled but when you send request all will be enabled again.

Resources