How do I conditionally invoke JavaScript from a bean in JSF 2?
I have a class that uses PrimeFaces which uploads user files to a particular folder, but if the user attempts to upload a file with the same name as one that is already present, I want JavaScript in this case to open up a box on the screen asking the user if he wants to overwrite the old file, or cancel the operation. If no file with that name is present, then JavaScript is not called.
I know how to test that a folder has a particular file in it, it is just that I need to know how to invoke JavaScript conditionally.
I would most appreciate any advice on this.
I have looked at variopus resources online, but still cannot get the application to work correctly. basically, this is what I have done, in an included xhtml page I have the following code for the file upload:
<p:fileUpload id="fileUpload" fileUploadListener="#{filters.upload}"
allowTypes="#{filters.uploadTypes}" invalidFileMessage="#{filters.uploadBadType}"
sizeLimit="#{filters.uploadSize}" invalidSizeMessag="#{filters.uploadBadSize}"
update="fileUpload fileTable uploadMessage" description="Select Text File"
disabled="#{filters.disableFileUploadButton}"/>
<!--- Then further in the same file is this: -->
<p:remoteCommand name="remoteCommandOverwrite" actionListender="#{filters.execOverwrite}"/>
The parent xhtml page that includes the above I have the foolowing JavaScript:
<script type="text/javascript">
function popupConfirm() {
var overwrite = confirm('Warning: This will overwrite the existing file - Do you confirm this?');
if (overwrite) remoteCommandOverwrite([{name: overwrite, value: true}]);
}
</script>
In my bean I have the following code in three methods:
public void upload(FileUploadEvent event) {
FacesMessage msg = new FacesMessage("Success! ", event.getFile().getFileName() + " is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
overwrite = false;
// Do what you want with the file
try {
copyFile(event.getFile().getFileName(), event.getFile().getInputstream());
} catch (IOException e) {
e.printStackTrace();
}
}
public void copyFile(String fileName, InputStream in) {
// Initialization etc.
File file = new File(uploadFull + fileName);
if (file.exists()) {
RequestContext.getCurrentInstance().execute("popupConfirm()");
// Then test to see if overwrite is true or false, and act accordingly
}
// Then I am supposed to get the value of overwrite here:
public void execOverwrite() {
System.out.println("### execOverwrite() ###");
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> map = context.getExternalContext().getRequestParameterMap();
String soverwrite = (String) map.get("overwrite");
if (soverwrite.equals("true")) {
overwrite = true;
System.out.println("overwrite: true");
}
}
What I am trying to do is first to invoke conditionally the JavaScript function popupConfirm(). On clicking the "Upload" button that is invoked if the codition is true, which is what I want. This is then supposed to call
That works and brings up the confirm() box, but the is never called, so the method execOverwrite() in my bean is also never called, and I cannot pick up the return value and pass it to the code inside the method copyFile(). What is going wrong?
I put this problem on the back burner for about a week, and have just got back to it. I got it to work, and can pass a value back to the bean, but somehow I need to resume execution from the place where JavaScript is called.
To sumarize, my JavaScript contains the following code:
<script type="text/javascript">
function popupConfirm() {
var overwrite = confirm('Warning: This will overwrite the existing file - Do you confirm this?');
if (overwrite) remoteCommandOverwrite([{name: 'overwrite', value: 'true'}]);
}
</script>
And in the xhtml code I have:
<p:fileUpload id="fileUpload" fileUploadListener="#{filters.upload}" ...../>
<!-- Other code -->
<p:remoteCommand name="remoteCommandOverwrite" actionListener="#{filters.execOverwrite}"/>
Then on clicking the file upload button after clicking the choose file button, the code in the JavaScript, as listed above, is executed:
RequestContext.getCurrentInstance().execute("popupConfirm()");
Then on clicking "OK" in the dialog box, this method in the same bean is called:
public void execOverwrite() {
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> map = context.getExternalContext().getRequestParameterMap();
String soverwrite = map.get("overwrite");
if (soverwrite.equals("true")) {
overwrite = true; }
}
}
where the flag "overwrite" will eventually be tested to see if it is true.
Using various print statements I check that this works. However, the code does not resume executing after encountering the statement: RequestContext.getCurrentInstance().execute("popupConfirm()"); regardless of whether I enter "OK" or "Cancel" in the dialog, which is what i want it to do. It looks as if a callback of some type is required, and would most appreciate some ideas.
According to your tag, you are using PrimeFaces, so there is an easy way to invoke a javascript function from a server side event managed bean method when the browser has completed processing the server response. PrimeFaces gives you a utility class called RequestContext.
public void doActionListenerMethod() {
if (!isValid()) {
RequestContext.getCurrentInstance().execute("MyJSObject.doClientSideStuff()");
}
}
The following will execute the string argument as a Javascript when JSF has finished rendering on the client.
Related
In the system I'm working on, we have a page whose backing bean implements an API to fetch data from a database other than the system's main (user-related) one, and stores the data locally (in memory).
Part of the object that is stored is a link (and a Javascript function) that points to the secondary system's related page.
Here's my relevant code from the JSF page;
<p:dataTable id="dtDemonstrativoBoletos" value="#{bean.boletos}" rowKey="#{boleto.link}"
widgetVar="dtDemonstrativoBoletos" var="boleto"
selection="#{bean.boletoSelecionados}" rowIndexVar="#{boleto.link}">
<p:autoUpdate />
<p:column selectionMode="multiple" width="0" toggleable="false"/>
<!-- columns -->
<p:column width="24" style="padding-left:0;">
<p:link href="#{boleto.link}" target="_blank" >
<i class="fa fa-eye" style="color:darkslategrey;"/>
</p:link>
</p:column>
<p:ajax event="rowDblselect" listener="#{bean.redirecionarBoleto} update="dtDemonstrativoBoletos"/>
</p:dataTable>
Backing bean method;
public void redirecionarBoleto() throws IOException {
try {
if (!this.boletoSelecionados.isEmpty()) {
PrimeFaces.current().executeScript(this.boletoSelecionados.get(0).getLinkJs());
} else if (this.boletoSelecionado != null) {
PrimeFaces.current().executeScript(this.boletoSelecionado.getLinkJs());
}
this.getBoletoSelecionado().redirecionarJavascript();
/* the getBoletoSelecionado() method returns either the this.boletoSelecionado object
or attributes the first element in the this.boletoSelecionados list to it,
just to be clear */
this.boletoSelecionados = new ArrayList<Boleto>();
} catch (NullPointerException ex) {
} catch (Exception e) {
// TODO: handle exception
}
}
and object class.
public class Boleto {
private String situacao;
private String descricao;
private String codigoBoleto;
private String codigoEntidade;
private String link;
private String linkJs;
private Date dataVencimento;
/*getters and setters*/
public void redirecionarJavascript() {
PrimeFaces.current().executeScript(this.linkJs);
}
}
The API sets the link property as a parametrized .xhtml with the codigoBoleto and codigoEntidade values and the linkJs property with boleto.setLinkJs("window.open('" + boleto.getLink() + "')");.
Now into the issue: since the link is readily available on the DataTable, clicking on it redirects correctly to the page (as expected) within 1.12 and 1.48 seconds (going from localhost to production server), while the ajax event takes up to 50 seconds to reach the backing bean. I'm not even joking. Once it gets to the bean, it takes nil time to process the actual method (longest delay is between me seeing that it reached the breakpoint and pressing 'Skip') and the same amount of time - a second and a half - to reach the other page.
Any suggestions as to why my page straight up doesn't want to reach the code will be very warmly welcomed. Also, the linkJs property was implemented as a testing measure, to be immediately called, but the ajax can't see the boleto variable in the DataTable - I get a PropertyNotFoundException or some such. Also, I need it to work with rowDblselect due to system standardization.
Regarding the "PropertyNotFoundException" issue. It may be because the getter and setter methods are missing for that specific property or because you have not created the JSF 2.3 configuration class in the package where you have the backing bean.
import javax.enterprise.context.ApplicationScoped;
import javax.faces.annotation.FacesConfig;
import static javax.faces.annotation.FacesConfig.Version.JSF_2_3;
#FacesConfig(version=JSF_2_3)
#ApplicationScoped
public class ConfigurationJSF {
}
That is the configuration for JSF 2.3
I have an HTML page loaded in the webbrowser control. The HTML has a javascript function windows.print which tries to print from my browser. Please how can I pass the windows.print() function to print through Winforms C#. Or how can I pass the javascript object I want printed into the C# to be printed.
Please I am a beginner with C#, I would appreciate a detailed explanation. Thanks so much!
you can probably do something like this
namespace WindowsFormsApplication
{
// This first namespace is required for the ComVisible attribute used on the ScriptManager class.
using System.Runtime.InteropServices;
using System.Windows.Forms;
// This is your form.
public partial class Form1 : Form
{
// This nested class must be ComVisible for the JavaScript to be able to call it.
[ComVisible(true)]
public class ScriptManager
{
// Variable to store the form of type Form1.
private Form1 mForm;
// Constructor.
public ScriptManager(Form1 form)
{
// Save the form so it can be referenced later.
mForm = form;
}
// This method can be called from JavaScript.
public void MethodToCallFromScript()
{
// Call a method on the form.
mForm.DoSomething();
}
// This method can also be called from JavaScript.
public void AnotherMethod(string message)
{
MessageBox.Show(message);
}
}
// This method will be called by the other method (MethodToCallFromScript) that gets called by JavaScript.
public void DoSomething()
{
// Indicate success.
MessageBox.Show("It worked!");
}
// Constructor.
public Form1()
{
// Boilerplate code.
InitializeComponent();
// Set the WebBrowser to use an instance of the ScriptManager to handle method calls to C#.
webBrowser1.ObjectForScripting = new ScriptManager(this);
// Create the webpage.
webBrowser1.DocumentText = #"<html>
<head>
<title>Test</title>
</head>
<body>
<input type=""button"" value=""Go!"" onclick=""window.external.MethodToCallFromScript();"" />
<br />
<input type=""button"" value=""Go Again!"" onclick=""window.external.AnotherMethod('Hello');"" />
</body>
</html>";
}
}
}
I am developing a WinRT application and was wondering if i could link a Hyperlink tag to a JavaScript function or be able to call the view page with NavigateUri? Or if it is possible to use interaction triggers that call the JS by a command.
==Update==
I have been testing different xaml tags from hyperlink to hyperlinkbutton. HyperlinkButton seems to appear and is clickable. it is just the on click function that doesn't seem to get called...
<HyperlinkButton Grid.Row="1" Click="__Title__.OnBuildingClick" Content="Buildingoo"/>
<HyperlinkButton Grid.Row="1" Click="__Title__.OnBuildingClick" Content="Buildingoo" ?
<i:EventTrigger EventName="ClickHyperlink">
<behaviors:EventToScriptBehavior Command="__Title__.OnBuildingClick" />
</i:EventTrigger>
</i:Interaction.Triggers>
</HyperlinkButton>
these are the 2 approaches i have taken. The functino onBuildingClick is only an alert msg but it doesnt get called..
Assuming the webView has a predefined name "currentWebView":
<WebView x:Name="currentWebView" ... />
You can access the control from the codebehind using its name, and then tell the web view to search the DOM for a javascript function by name, and invoke it if the function exists.
To invoke a method by name you will use the C# Method "InvokeScript" and pass 2 parameters:
Param 1: Name of Javascript function
Param 2: Comma Separated List of Arguments in order of Javascript Signature
currentWebView.InvokeScript("nameOfFunction", "myFirstparam, secondParam, thridParam");
Additionally, in order to allow the XAML UI to respond to the Javascript event your Javascript code must call
nameOfFunction(param1, param2, param3){
window.external.notify("This content will be accessible in the next steps NotifyEventArgs e.Value property.")
};
and in the last piece of wiring up the ability to respond to Javascript from C#, you must apply an event handler in the C# codebehind to allow the XAML to be notified what is going on inside the WebControl
// In the Constructor / OnLoaded/ OnNaviagtedTo Event of the code behind
currentWebView.ScriptNotify += new NotifyEventHandler(currentWebView_ScriptNotify);
... then later in the class
private void currentWebView_ScriptNotify(object sender, NotifyEventArgs e)
{
//can access the value included in the notification
var notification = e.Value;
someTextblock.Text = notification;
}
Putting It Together:
<HyperlinkButton x:Name="btnExecuteJavaScript" Grid.Row="1" Click="btnExecuteJavaScript_Click" Content="Buildingoo"/>
public void btnExecuteJavaScript_Click(object sender, RoutedEventArgs e)
{
currentWebView.InvokeScript("OnBuildingClick", "arbitraryParameter, mayNotBeNeeded");
}
I am trying to figure out the correct javascript syntax for a DevExpress datagrid callback to pass data back to the client.
In the .aspx I installed an onclick event in the DataGrid row with a CustomCallback event using the js call: dg.PerformCallback(key); and in the aspx.cs file this function is correctly reached, however I cannot pass data back to the client:
protected void dg_CustomCallback(
object sender,
DevExpress.Web.ASPxGridView.ASPxGridViewCustomCallbackEventArgs e)
{
string key = e.Parameters; // works
e.Results = "something"; // .Results does not exist
return;
}
Then I switched from a CustomCallback to a DataCallback because the DevExpress.Web.ASPxGridView.ASPxGridViewCustomDataCallbackEventArgs does have a .Results property. However, I cannot figure out the corresponding javascript call. I tried in vain: dg.PerformCallback(key); , dg.PerformDataCallback(key); and dg.SendCallback(key);
Also I am wondering, when the above problem is fixed, which js function I need to program to receive the return data from the server after the callback.
What you are trying to achieve can be done using the JSProperties on callback and the OnEndCallback client side event of the ASPxGridview. This aspx tag can be placed exactly after the </Columns> closing tag of the ASPxGridview.
<ClientSideEvents EndCallback="function(s,e)
{
var errText = s.cpError;
if (errText != "")
{
alert(errText);
}
}" />
On the server side you set the JSProperties like this
gridOfApp.JSProperties["cpError"] = "The error was major!";
Important. Bear in mind that your JSProperties MUST ALWAYS start with the cp prefix.
I am trying to call a function from a Silverlight application. It should be a very simple task to do but so far I am not getting the result that I am looking for.
This is my Silverlight code:
private void button2_Click(object sender, RoutedEventArgs e)
{
HtmlPage.Window.Invoke("SayHello", new string[] { "Salut!" });
}
And this is the JavaScript code :
function SayHello(theid) {
alert(eval(theid));
var divStatusDiv = document.getElementById("divStatus");
divStatusDiv.style.backgroundColor = "Red";
}
The alert message always show "undefined" but when I press "OK" the colour of that DIV gets changed to Red as it should be.
Why am I getting "Undefined" all the time ?
You need to create the json that can be passed properly instead of just passing along an array like that. You can simply return "Salut!" instead of new string[] { "Salut!" } or you can create the json array for the string array you have.
I'm not familiar with Silverlight, but if theid has value "Salut!" inside of SayHello, then you cannot eval it, since it is a string of text, not code. You should change the line alert(eval(theid)); to just alert(theid);.
Use
alert(eval(theid.value));