getElementByID() for web user control not working? - javascript

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);

Related

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);
}

How to hide a row in a table when window.open() is called

I'm modifying an older application which has a table with many rows including the following row. (first question asked on StackOverflow, so hoping it's clear)
<tr id="volumeAvailable" class="standardRow">
<td align="right" colspan="2"><i>Volume Available for TPN/IL=</i></td>
<td>
<asp:TextBox ID="txtTotalFluidsLeftInfo" runat="server" style="border:None; text-align:left; color:blue; font-weight:bold;" Text="0" ReadOnly="true" CssClass="NumericTextbox" ToolTip="0" />
</td>
</tr>
The app has a click event which prints all controls/values on the aspx page by calling cmdPrint_Click below. A new window is opened which contains the print dialog and print preview.
protected void cmdPrint_Click(object sender, EventArgs e)
{
try
{
string _url = "'./TPNForm.aspx?ID=" + hOrderId.Value + "&type=print'";
string _target = "'_blank'";
string _script = "window.open(" + _url + "," + _target + "," + "'status=no, menubar=yes, toolbar=no,scrollbars=1, height=400px,width=600px');";
ClientScript.RegisterStartupScript(this.GetType(), "onclick", _script, true);
}
catch (Exception _ex)
{
throw new ApplicationException(_ex.Message);
}
}
My goal is to remove the above row from the table for the print view. I've tried adding volumeAvailable.Visible = false to the server side code to hide before displaying but the row still displays in the print window. I've also tried hiding the row by adding document.getElementById('volumeAvailable').style.display='none' but haven't been able to get this to work.
Can anybody assist on a possible way to exclude this row when calling window.open()? Thanks.
You can use CSS to hide an element only when printing. To hide just the one row, you can use an ID selector, like this:
#media print
{
#volumeAvailable
{
display: none !important;
}
}

Accessing dynamic created html checkboxes in code behind

I am dynamically creating my html checkboxes at runtime using javascript/jquery like this. It's using the Jquery bootgrid.
isAssigned: function (column, row) {
if (row.isAssigned == "True" || row.isAssigned == "true") {
return "<input id='chk" + row.id + "' type='checkbox' name='chkMarks' checked='" + row.isAssigned + "' />";
}
else {
return "<input id='chk" + row.id + "' type='checkbox' name='chkMarks'/>";
}
}
What I need to do is somehow get the values in my submit buttons click event in the aspx.cs code behind. I cannot runat="server" these controls because the id is not added until after the page has loaded. I need to get the id's and whether they are true or false.
I was wondering if anyone knew the best approach for me to get these values in something like this.
protected void myTester_Click(object sender, EventArgs e)
{
var values = Request["chkMarks"].ToString();
}
You almost had it! You need to use Request.Form instead of just Request. That should give you access to all your DOM elements.
var values = Request.Form["chkMarks"].ToString();
Here is some more information on it: https://msdn.microsoft.com/en-us/library/system.web.httprequest.form(v=vs.110).aspx
Request.Unvalidated["chkMarks"]
can also be useful depending on what the value of this field is.
I did it using JSON in the end using a postback so not asynchronous which is hopefully useful to someone.
function BuildJSON() {
var jsonObj = [];
jQuery("input[name='chkMarks']").each(function () {
item = {}
item["id"] = this.id;
item["isAssigned"] = this.checked;
jsonObj.push(item);
});
var json = JSON.stringify(jsonObj);
$('#<%= hiddenMarksID.ClientID %>').val(json);
}
In the code behind I attached an event to button and picked up the JSON like so...
protected void btnSubmitMarks_Click(object sender, EventArgs e)
{
string json = hiddenMarksID.Value;
List<ChkMark> chkMark = new JavaScriptSerializer().Deserialize<List<ChkMark>>(json);
Here is the POCO...
public class ChkMark
{
public string id { get; set; }
public bool isAssigned { get; set; }
}

Building a client side transfer method for p:pickList

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.

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/

Categories