Building a client side transfer method for p:pickList - javascript

I'm working with Mojarra JSF 2.1.28 and Primefaces 3.5. I want to implement a client-side transferring input for the p:pickList component, where the user types something and the value is searched through the labels at the list of available elements, then it's transferred to the target list. That's how my code looks like:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head />
<h:body>
<script>
//Just for testing, transfer the second element
//to the target list when document is ready. Works well
$(document).ready(function(string) {
transfer("name2");
});
//Transfer function. It takes each list item from the source list and checks if it matches with the given pattern
//If it does, moves it to the target list
function transfer(string) {
$(".ui-picklist-source li").each(function() {
var re = new RegExp(string);
if ($(this).attr('data-item-label').match(re)) {
$(".ui-picklist-target").append($(this));
}
});
};
</script>
<h:form>
<p:inputText
onkeypress="if (event.keyCode == 13) { transfer(this.value); return false;}" />
</h:form>
<h:form id="form">
<p:pickList value="#{bean.elements}" var="element"
itemLabel="#{element.name}" itemValue="#{element.name}" id="picklist" />
<p:commandButton value="Send" action="#{bean.send}" />
</h:form>
</h:body>
</html>
#ManagedBean
#ViewScoped
public class Bean implements Serializable{
public class Element {
private String name;
public Element(String n) {
name = n;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "Element [name=" + name + "]";
}
}
private DualListModel<Element> elements;
public Bean() {
List<Element> source = new ArrayList<Bean.Element>();
List<Element> target = new ArrayList<Bean.Element>();
source.add(new Element("name1"));
source.add(new Element("name2"));
elements = new DualListModel<Bean.Element>(source, target);
}
public DualListModel<Element> getElements() {
return elements;
}
public void send() {
System.out.println("Available: " + elements.getSource() + " assigned: "
+ elements.getTarget());
}
public void setElements(DualListModel<Element> elements) {
this.elements = elements;
}
}
Well, in this test case, there are two items to play with, name1 and name2. When page loads, I use $(document).ready() to call my transfer(string) function in order to move name2 to the target list. Page gets properly loaded and if we click on Send button, we get the second element properly assigned.
Problem comes when calling the function using the p:inputText component. Here, we listen to the Enter key event to send the current given value and perform a transfer. At client side it works fair enough, it behaves as expected. However, when clicking on Send, model doesn't get properly updated at server side.
I infer this is caused by the view state kept by JSF, but how to deal with this? Is there a way to implement it or do I have to stick to Ajax requests?

The "right" way to achieve this is to use Primefaces's Javascript API of
PrimeFaces.widget.PickList
Assuming your widgetVar is pickListWV, here's how you would do:
function transfer(string) {
PF('pickListWV').sourceList.children().each(function() {
var re = new RegExp(string, "i");
if ($(this).attr('data-item-label').match(re)) {
PF('pickListWV').selectItem($(this));// select the item
PF('pickListWV').add();// add it to the target
}
});
}
EDIT:
Also you can make it more interesting, like live filtering..
<p:inputText
onkeypress="if (event.keyCode == 13) { transfer(this.value); return false;} else{PF('pickListWV').filter(this.value, PF('pickListWV').sourceList)}" />
EDIT 2:
As I can see that you have a case-sensitive match, so you have to declare your RegExp as case-insensitive
var re = new RegExp(string, "i");
Here's a small working example on github, and a working demo as requested :)
Hope this helps.

Related

two way communication between c# and JS using webview2

I'm using a webview2-control in a winforms application. I use messages to communicate between c# and Javascript
window.chrome.webview.addEventListener / window.chrome.webview.postMessage in Javascript
event .CoreWebView2.WebMessageReceived and method CoreWebView2.PostWebMessageAsString in C#
The communication works BUT only after the page in webview2 has been somehow refreshed. The first message sent by c# is always ignored/not received by JS. The messages after that are correcly received and processed.
My UI code:
public GUI()
{
InitializeComponent();
browser.Source = new Uri(System.IO.Path.GetFullPath("HTML/ui.html"));
InitializeAsync();
}
async void InitializeAsync()
{
await browser.EnsureCoreWebView2Async(null);
browser.CoreWebView2.WebMessageReceived += MessageReceived;
}
void MessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs args)
{
String content = args.TryGetWebMessageAsString();
if (content.StartsWith("getData"))
{
ReadDataFromCATIA();
var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
string jsonRootNode = JsonConvert.SerializeObject(this.RootNode, Formatting.Indented, serializerSettings); //here I've got the message I want to post
//String input = args.TryGetWebMessageAsString();
//MessageBox.Show("string from JS: " + input);
browser.CoreWebView2.PostWebMessageAsString(jsonRootNode);
}
else //object received
{
ProductNode received = JsonConvert.DeserializeObject<ProductNode>(content);
MessageBox.Show(received.PartNumber + " received");
}
}
and my JS in ui.html
window.chrome.webview.addEventListener('message', event => {
alert(event.data);
WriteDataFromCsharp(event.data);
});
function WriteDataFromCsharp(data) {
var target = document.getElementById('target');
if (target === null) { alert('target not found') };
//alert(target.id);
//target.textContent = event.data;
rootNode = JSON.parse(data);
target.innerHTML = addTable(rootNode); //addTable create an HTML table from the deserialized object rootNode
}
function RequestData() {
//function triggered by a button on the html page
//alert('post to c#');
window.chrome.webview.postMessage('getData');
}
So far, i've tried to:
ensure the javascript is as late as possible in the page (defer, at the end of body). No changes.
inject the javascript to the page after it has been loaded using .CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(jsCode). Same behavior.
inject the javascript after once the event NavigationCompleted has fired. same behavior.
What do I miss ?
Finally found the culprit: in my HTML-page, i've used a "submit" instead of "button". With
<input type="button" value="Load data from V5" onclick="RequestData()" />
The page behavior is as expected.

Adding Hyperlinks to ValidationSummary using programatically added validators

I'm using the javascript from the answer in this question in a project of mine:
Adding Hyperlinks to ValidationSummary
It works really great. I've added it to the bottom of my masterpage (for some reason, even though it is inside $(document).ready, Page_Validators is null if i place it in the head section)
Anyway! I'm also adding some custom validators programatically on postback using this code:
public static CustomValidator ReturnErrorMessage(string message, string validationGroup, string controlToValidate = "")
{
CustomValidator control = new CustomValidator();
control.ID = "cv" + controlToValidate;
control.IsValid = false;
control.Text = " ";
control.ValidationGroup = validationGroup;
control.ErrorMessage = message;
control.ControlToValidate = controlToValidate;
return control;
}
However whenever I add a CustomValidator like that, in a button event, page_load or whatever, Page_Validators will be overridden and the errormessage will revert to the message without a anchor.
What gives? Am I doing something wrong or can someone explain what is happening?
I've tried debugging it and it does set the values correctly, but then it just reset afterwards.
I've tried for the heck of it and in $(document).ready set all validators as isvalid = false, and that gets overwritten too.
Im using asp.net 4.5 unobtrusive validation, but it does not make a difference if I turn it off.
Adding the javascript in code using Page.ClientScript.RegisterStartupScript at some point after the validator has been created does not work either.
If I don't add any validators in code everything works as expected.
I'm aware I can just add the anchor tags manually, but this is a lot of work to update existing validators instead of just tossing in a small script, so I'm hoping to get this to work.
You can use this code to test this:
using System;
using System.Web.UI.WebControls;
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
CustomValidator control = new CustomValidator();
control.ID = "cv" + txtName.ClientID;
control.IsValid = false;
control.Text = " ";
control.ValidationGroup = "errorGroup";
control.ErrorMessage = "Error message";
control.ControlToValidate = txtName.ClientID;
Form.Controls.Add(control);
}
}
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="jquery-3.3.1.min.js"></script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtName" runat="server"></asp:TextBox>
<asp:ValidationSummary ID="vsSummary" runat="server" ValidationGroup="errorGroup" ForeColor="Red" HeaderText="Error!" />
</div>
</form>
<script>
$(document).ready(function() {
var validators = Page_Validators; // returns collection of validators on page
$(validators).each(function() {
//get target control and current error validation message from each validator
//[0] is needed when using aspnet 4.5 unobtrusive validation
var validator = $(this)[0];
var errorMsg = validator.errormessage;
var targetControl = validator.controltovalidate;
//make link only if theres a control to target
if (targetControl) {
var errorMsgWithLink = "<a href='#" + targetControl + "' style='color: #FF3232;'> " + errorMsg + "</a>";
//update error message with anchor tag
validator.errormessage = errorMsgWithLink;
}
});
});
</script>
</body>
</html>
If you want you can try implementing your own 'CustomValidationSummary' control by following the same design pattern as mentioned at Reference Source by Microsoft, and modify the render method to include anchor tag to wrap error text, before it is passed into the writer method at line number 462.
I ended up using a extension method, adding the anchor tag in the method
public static void AddValidator(this Page p, string message, string validationGroup, string controlToValidate = "", bool addAnchorTags = true)
{
CustomValidator control = new CustomValidator();
control.ID = "cv" + controlToValidate;
control.IsValid = false;
control.Text = " ";
control.ValidationGroup = validationGroup;
control.ControlToValidate = controlToValidate;
if (addAnchorTags && !string.IsNullOrEmpty(controlToValidate))
{
control.ErrorMessage = "<a href='#" + controlToValidate + "' style='color: #FF3232;'> " + message + "</a>";
}
else
{
control.ErrorMessage = message;
}
p.Validators.Add(control);
}

Update Text Unity 4.6 UI Element

I'm developing a small "Hello World" web app using Unity3D and I need to update a Text element (I'm using the brand new UI elements added in the latest unity release, 4.6).
I've been following several tutorials, including the Unity's official docs, and it doesn't work.
Here I post some code:
First of all my C# script, as you can see, i'm setting the initial value of the variable named "name" to "Unknown",
the goal is update it using the input name.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ChangeText : MonoBehaviour {
Text text;
string name;
// Use this for initialization
void Start () {
text = GetComponent<Text>();
name = "Unknown";
}
// Update is called once per frame
void Update () {
text.text ="Welcome " + name;
}
void updateLabelText(string inputName) {
Debug.Log ("input >> " + name);
name = inputName;
}
}
Here the website side javascript code:
function SaySomethingToUnity() {
input = document.getElementById('inputName').value;
u.getUnity().SendMessage("text", "updateLabelText", input);
//alert(input);
}
And finally the HTML to write user's name:
<div>
<input type="text" name="enter" class="enter" value="" id="inputName"/>
<input type="button" value="click" onclick="SaySomethingToUnity();"/>
</div>
If anyone can help me...
Thank all for wasting your time in my post!! :D

JavaScript: List Box not being updated after pop-up look up

I currently have a listbox (part of a search criteria) that represents a list of strings that are retrieved one at a time via a pop-up window. However when the pop-up window closes the selected String is never added to the listbox. (I have confirmed that the hidden variable is updated and if you navigate away from the page after a search is performed and navigate back, the list box correctly shows Strings I had selected from the pop up previously). Any help would be greatly appreciated!
<script type="text/javascript">
function selectBook(bookId, extendedBookName) {
var idInput = jQuery("#myForm\\:bookNames");
if (idInput.val() == "") {
idInput.val(extendedBookName);
} else {
idInput.val(idInput.val() + '###' + extendedBookName);
}
}
</script>
...
<h:form id="myForm">
...
<ui:define name="form-fields">
<h:selectOneListbox id="booksListBox" size="3" style="width:470px">
<s:selectItems var="_var" value="#{bean.searchCriteria.bookNames}" label="#{_var}" noSelectionLabel="" />
</h:selectOneListbox>
<h:outputLink onclick="return openNewWindow('#{bean.booksLookupUrl}?#{bean.bookLookupParameters}', 'book');"
target="_blank">
<h:inputHidden id="bookNames" value="#{bean.searchCriteria.bookNames}" converter="StringListConverter"/>
<h:outputText value="Add"/>
</h:outputLink>
</ui:define>
...
</h:form>
This is javaScript that belongs to the lookup window. This calls the selectBook function
function selectBook(bookId, extendedBookName) {
var extendedName = unescape(extendedBookName);
window.opener.selectBook(bookId, extendedName);
window.close();
}
And for my Java code...
public class BookSearchCriteria implements Serializable {
...
private List<String> bookNames = new ArrayList<String>();
public List<String> getBookNames() {
return bookNames;
}
public void setBookNames(List<String> bookNames) {
this.bookNames = bookNames;
}
The StringListConverter code...
#FacesConverter("myStringListConverter")
public class StringListConverter implements Converter {
// this is used as a regex, so choose other separator carefully
private static final String MY_SEPARATOR = "###";
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
if (value == null) {
return new ArrayList<String>();
}
return new ArrayList<String>(Arrays.asList(value.split(MY_SEPARATOR)));
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
if (value == null) {
return "";
}
return join((List<String>) value, MY_SEPARATOR);
}
/**
* Joins a String list, src: http://stackoverflow.com/q/1751844/149872
*
* #param list
* #param conjunction
* #return
*/
public static String join(List<String> list, String conjunction) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String item : list) {
if (first) {
first = false;
} else {
sb.append(conjunction);
}
sb.append(item);
}
return sb.toString();
}
}
I don't really understand why you want to populate your listBox using a popup.
But if you modify the underlying bean values, you need to rerender the <h:selectOneListbox/> to reflect these changes. If you are using (or can use) the richfaces framework it can be done quite easily through an ajax call.
The ajax4jsf component called <a4j:jsFunction/> can do the job. For example in your parent page add :
<a4j:jsFunction name="refreshListbox" reRender="booksListBox" limitToList="true"/>
and in your popup just call this js function when a new book value is selected :
<script type="text/javascript">
window.opener.refreshListbox();
</script>
You could also use a <rich:modalPanel> instead of a popup to stay in the same page and interact more easily with your jsf components.
The page in the popup window is running some server-side Java code that creates new options for the dropdown menu. This has no absolutely no effect on the parent page until the parent page is reloaded and regenerated by the server. To avoid reloading the page, you need to create and select the new option using client-side JavaScript/jQuery code.
Assuming that the HTML code for the resulting option should look something like <option value="bookId">extendedBookName</option>, you can use this:
<script>
function addBook(bookId, extendedBookName) {
var newOption = jQuery('<option>').val(bookId).text(extendedBookName);
var idInput = jQuery('#myForm\\:bookNames');
idInput.append(newOption).val(bookId);
}
</script>
Demo: http://jsfiddle.net/5Cwjk/
Or, if the selection from the popup window is not guaranteed to be new, then you'll want to dynamically determine whether creating an option is necessary before doing so to avoid duplicates:
<script>
function addBook(bookId, extendedBookName) {
var idInput = jQuery('#myForm\\:bookNames');
if (idInput.val(bookId).val() != bookId) {
var newOption = jQuery('<option>').val(bookId).text(extendedBookName);
idInput.append(newOption).val(bookId);
}
}
</script>
Demo: http://jsfiddle.net/nTtY9/1/

getElementByID() for web user control not working?

I have made a web user control (.ascx) which consists of the two html textbox and two input buttons, when I try to do document.getElementById('<%=ControlID.ClientID%>') it returns null. what could i have done wrong ?
source code:
<script language="javascript" type="text/javascript">
function intilize() {
var x = document.getElementById('<%=JSNumeric_Control.ClientID%>');
}
</script>
<table class="style1">
<tr>
<td >
<My:UserInfoBoxControl ID="JSNumeric_Control" runat="server" />
</td>
<td>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center">
<input id="Button1" type="button" value="button" onclick="intilize()" /><br />
</td>
</tr>
</table>
Here is the code for the Numbric.cs, the following are the property used and in page load i am using JavaScript to assign all the events for the HTML inputs:
public partial class NumricCounter : System.Web.UI.UserControl
{
public string defualtValue = "0";
public double defaultIncrementValue = 1;
public int defaultPrecsionValue = 0;
public string Button_Add_ClientID
{
get { return Button_Add.ClientID; }
}
public string Button_Subtract_ClientID
{
get { return Button_Subtract.ClientID; }
}
//Set and Get for all these properties. (Code Omitted)
public string Text;
public double IncrementValue;
public int PrecsionValue;
public void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Text_Output.Value = this.Text;
Button_Add.Attributes.Add("onclick", "Add(" + IncrementValue.ToString() + "," + PrecsionValue.ToString() + ");");
Button_Subtract.Attributes.Add("onclick", "Subtract(" + this.IncrementValue.ToString() + "," + this.PrecsionValue.ToString() + ")");
Text_Output.Attributes.Add("onkeyup", "check(" + this.PrecsionValue.ToString() + ");");
}
}
}
Try replacing your function:
function intilize() {
var x = document.getElementById('<%=JSNumeric_Control.ClientID%>');
}
with this:
function intilize() {
var x = document.getElementById('JSNumeric_Control');
}
The getElementById should read the ID of the element from the rendored html.
JSNumeric_Control.ClientID will return the ClientID of the control if it were rendered to the page. It's existance doesn't necessarily mean that there will be HTML on the final page that has that ID.
For example if you create a control that just outputs two buttons you will give each of those buttons different IDs that are not the same as the control that they live in. Often you might create a container div that you will put around all other content which you will give the ID of the Control to for easy finding but there is no reason for this to exist.
What you should do is either make sure that your control does create this HTML container with the ID of your control or you should refer specifically to the client ID of the items inside your control.
var button1 = document.getElementById('<%=JSNumeric_Control.Button1.ClientID%>');
The problem is that in the DOM there's no element with Id = ClientID of your UserInfoBoxControl. The controls inside your user control will have other Ids, like 'JSNumeric_Control_Button1', 'JSNumeric_Control_TextBox1' etc. If you need to get both of the input buttons, you can do one of the following:
Use jquery selector to find all inputs with type = button and id starting with <%=JSNumeric_Control.ClientID%>
Add two new properties to your control - FirstButtonClientID and SecondButtonClientID that will provide you with clientIDs of your buttons. Then you can use it in javascript.
Create custom javascript object which will represent your usercontrol and provide necessary functionality.
You are most likely trying to search the DOM before it's been loaded. Try putting your code inside an onload handler:
document.onload = function () {
alert(document.getElementById('<%=ControlID.ClientID%>'));
}
This makes sure that the code isn't executed before you actually have all of the DOM loaded.
//Button_Add is the control id inside the user control
var x = document.getElementByID(JSNumeric_Control.FindControl("Button_Add").ClientID);

Categories