Cross UserControl property setting and persisting values - javascript

I'm using a set of UserControls on a ASP.NET application which I'm maintaining.
I have a page which renders a set of custom UserControls. On one of these controls, lets say ucA, I may have a little Javascript Popup which render another UserControl and let's call this one ucB.
On ucA I've defined a public property which sets or gets values from a hiddenField defined in ucA:
<asp:HiddenField ID="hidWorkDirName" runat="server" />
and the property definition:
public string _hidWorkDirName
{
get { return hidWorkDirName.Value; }
set { hidWorkDirName.Value = value; }
}
My ucB only shows a Textbox which, upon submit, should set the value of the hidWorkDirName:
protected void btnSubmit_Click(object sender, EventArgs e)
{
ucA parent = (ucA)this.Parent; //this, being ucB
parent._hidWorkDirName = txtName.Text; //the TextBox value being set on ucA
}
While debugging I can see that the value is set correctly.
Now, ucA also has a Submit button (both submits are for different things) on which I want to read the value of the hidWorkDirName. But no matter what I try the value I get is always an empty string as if nothing had been set.
I've tried reading the value directly from the hiddenField and from the property itself (_hidWorkDirName) but I never get the value I've set previsouly.
Why is this happening?

This is because the Hiddenfield hidWorkDirName could get reset during the Page_Load. Try a different approach using ViewState.
Here's your property with ViewState
public string _hidWorkDirName
{
get
{
if (ViewState["WorkDirName"] != null)
{
return (string)ViewState["WorkDirName"];
}
return string.Empty;
}
set
{
ViewState["WorkDirName"] = value;
}
}

Related

struts2 optiontransferselect retrieve and display value from database

In the jsp page, I have a <s:optiontransferselect> for swap values between left side and right side and a submit button to save.
<s:optiontransferselect
allowUpDownOnLeft="false"
allowUpDownOnRight="false"
allowSelectAll="false"
allowAddAllToLeft="false"
allowAddAllToRight="false"
addToRightLabel="Go to right"
addToLeftLabel="Go to left"
leftTitle="Left side values"
headerKey="0"
name="option"
list= "optionList"
rightTitle="Right side values"
doubleHeaderKey="0"
doubleList="selectedOptionList"
doubleName="selectOption"
doubleId="selectedValues"
>
</s:optiontransferselect>
<s:submit />
I run the program, it actually can save the value from the right side. However it does not show the saved values there.
I am thinking about using javascript and use onchange event in <s:optiontransferselect> to achieve this
<script>
function show(){
const list = document.getElementById("selectedValues");
for (var i = 0; i < list.options.length; i++) {
//seems something not correct in this part but I am not sure how solve in this way
list.options[i].selected = true;
}
return true;
}
</script>
<s:optiontransferselect
allowUpDownOnLeft="false"
allowUpDownOnRight="false"
allowSelectAll="false"
allowAddAllToLeft="false"
allowAddAllToRight="false"
addToRightLabel="Go to right"
addToLeftLabel="Go to left"
leftTitle="Left side values"
headerKey="0"
name="option"
list= "optionList"
rightTitle="Right side values"
doubleHeaderKey="0"
doubleList="selectedOptionList"
doubleName="selectOption"
doubleId="selectedValues"
onchange ="show()" <!-- onchange seems not work here -->
>
</s:optiontransferselect>
When I run the program, the right side still cannot show the saved value.
More information about the optiontransferselect in case it is useful.
I have an action class for it.
public class OptionTransferSelectAction extends ActionSupport {
private List<String> optionList;
private List<String> selectedOptionList;
//private String option;
private List<String> option;
private List<String> selectOption;
public OptionTransferSelectAction (){
optionList=new ArrayList<String>();
//display on the left side for selection
optionList.add("Section A");
optionList.add("Section B");
optionList.add("Section C");
optionList.add("Section D");
optionList.add("Section E");
optionList.add("Section F");
optionList.add("Section G");
optionList.add("Section H");
selectedOptionList=new ArrayList<String>();
//display on the right side
//pretend it does not have any values in the first time (does not
//trigger the save yet)
}
public List<String> getOptionList() {
return optionList;
}
public void setOptionList(List<String> optionList) {
this.optionList = optionList;
}
public List<String> getSelectedOptionList() {
return selectedOptionList;
}
public void setSelectedOptionList(List<String> selectedOptionList) {
this.selectedOptionList = selectedOptionList;
}
/*
public String getOption() {
return option;
}
public void setOption(String option) {
this.option = option;
}
*/
public List<String> getOption() {
return option;
}
public void setOption(List<String> option) {
this.option = option;
}
public List<String> getSelectOption() {
return selectOption;
}
public void setSelectOption(List<String> selectOption) {
this.selectOption = selectOption;
}
}
update
I change option from String to List<String> with the proper getter and setter. I run the code, the optiontransferselect still cannot show the saved values.
Which part I did wrong? Would someone let me know please? Thank you.
update
I created a new jsp for example, success.jsp. If I selected some values to selectOption and click the submit button. the jsp file can display the values that I just submitted. I can see the saved values in the database. However the optiontransferselect still cannot show the saved values.
The success.jsp has one code which can display the value I have just submitted.
Selected Value(s) : <s:property value="selectOption"/>
In the database, I can see the saved values, so I would like to know how to let the optiontransferselect show the saved values?
another update
I try to use a button to call javascript function to show selectedValues before submit.
<script>
function testshow() {
var value = document.getElementById("selectedValues");
for(var i=0;i<value.options.length; i++) {
alert(value.options[i].innerHTML);
}
return true;
}
</script>
//button to call the function
<s:submit onclick="return testshow()"/>
I run the program, when I click the button, it can show the selected values before submit, so I still don't understand why the optiontransferselect cannot get saved selected values.
I think there are two things you need to know in order to solve your issue:
The lifetime of a Struts action.
How the optiontransferselect actually works.
Lifetime of a Struts Action
I start with that, because that might already solve your issue.
👉 A new instance of a Struts action is created for each request.
That means for you when connect the attributes list (left side) and doubleList (right side) to fields in a class derived from ActionSupport - like in the example OptionTransferSelectAction above - you must ensure, that you initialize it with meaningful values, I mean values that reflect the current state you want to show to the user. In the worst case you have to fetch the values from the database with each request.
Alternatively you can inject a bean into the action with a dedicated lifetime (e.g. session scoped). I might line that out in a separate post.
(BTW I would be thankful if someone else could provide further information, if the lifetime of a Struts action can be changed directly).
How optiontransferselect works
This tag wraps up two select tags (or more specifically updownselect tags). I will line out the function of it first, because it makes us better understand optiontransferselect. (Sidenote: Contrary to JSF, Struts clearly distinguishes between attributes for get and set operations.)
To connect the select tag with the underlying data, these are the three relevant attributes:
list - This is the list of all available values (=get).
value - This is the list of values initially selected (=get).
name - The name of the property (field) of the action where the selected values will be supplied on form submit (=set).
So the only property your action will recieve is the one connected with name. The select tag will NOT...
...post back the complete list of values (the value of list) to the action.
...change the values of list and value in the underlying Java class / action.
And the same is true for optiontransferselect. Which makes the semantics quite weird, because some changes (namely which values show up left and which show up right) will never be post back to the Struts action, you only can get hold of the selected values of both sides.
The major difference is that you have those three attributes now twice:
list --> list for the left and doubleList for the right side.
value --> value for the left and doubleValue for the right side.
name --> name for the left and doubleName for the right side.
The question now is how to come by that you will only get the selected values? I would suggest to automatically select them right before submission:
<s:submit value="Save" onclick="selectAllOptions(document.getElementById('idLeft'));selectAllOptions(document.getElementById('idRight'));"/>
Where idLeft corresponds to the value of id in the optiontransferselect and idRight corresponds to the value of doubleId. With that change you would get the complete list of left and right values in name and doubleName respectively.
The code is exactly the same as for the button you would get with allowSelectAll="true" and the JavaScript method selectAllOptions is provided by Struts via the optiontransferselect.js, regardless of allowSelectAll was true or false.

Custom ASP.Net Server Controls: How to call javascript function in parent page

I have built a number of custom server controls and routinely call javascript functions that are embedded into my controls. But I haven't been able to figure out how to create a property that the user of the control can add their own function to and their javascript function in the page containing the control will be called. I need to do something like OnClientClick for an asp:Button control.
Edit 1:
Thanks to the answer by #h2015 I have made progress on this.
In my control I create a public property for the user's script,
public string EditAddressScript
{
set
{
string scriptName = "EditAddressScript";
Type csType = this.GetType();
ClientScriptManager csm = Page.ClientScript;
string scriptText = string.Format("function EditAddress() {{{0}}}", value);
csm.RegisterClientScriptBlock(csType, scriptName, scriptText, true);
}
}
Then in the RenderContents section of my control I check whether a value has been specified for EditAddressScript and create a button if it has.
output.Write("<input id='btnEdit' type='button' value='Edit Address' style='font-size: 6pt;' onclick='EditAddress();' />");
The page that uses this control creates it dynamically so I do something like this,
ECFControls.DefendantAddressReview defendantAddress = new ECFControls.DefendantAddressReview();
divControls.Controls.Add(defendantAddress);
defendantAddress.ClientEditAddressScript = "alert('Hello, World');";
This works, but is far from optimal.
I would like to define the script in the aspx page and then just give my control the name of the script, rather than setting the value of EditAddressScript to the actual javascript code.
In the RenderContents section of the control I have the name of the script hardcoded I would like that to be dynamic.
Edit 2:
Okay, it looks like I was making this far more complicated than necessary. All that is needed is to create a script in the parent aspx page and set the event in the control to that script.
So this is what I have now
Add script to aspx page.
function EditAddress() {
alert("Hello, World");
}
Create a property in the control to hold the value (_EditAddressScript is a private string variable in the control)
public string EditAddressScript
{
set
{
_EditAddressScript = value;
}
}
Then in the RenderContents section of the control I check whether a value has been specified for EditAddressScript and create a button if it has.
if(_EditAddressScript.Trim() != "")
output.Write(string.Format("<input id='btnEdit' type='button' value='Edit' style='font-size: 6pt;' onclick='{0}' />", _EditAddressScript));
Create the control like this,
ECFControls.DefendantAddressReview defendantAddress = new ECFControls.DefendantAddressReview();
divControls.Controls.Add(defendantAddress);
defendantAddress.EditAddressScript = "EditAddress()";
If the control were created declaratively I could do this,
<ECF:DefendantAddressReview ID="defendantAddressReview" runat="server" EditAddressScript="EditAddress()" />
Is there a better approach?
Thanks.
this.Page.ClientScript.Register...
https://msdn.microsoft.com/en-us/library/ms178207.aspx
I explored two possible solutions in the edits to my original question. The second edit achieves the desired result. I wanted to have an optional property for a custom ASP.Net server control that would specify the name of a javascript function that the user of the control could place in the same page as the control. Here's how I did it.
Add a public string property to the server control that defines the name of your javascript function. (_CustomJavascript is a private string variable in the control)
public string CustomJavascript
{
set
{
_CustomJavascript = value;
}
}
Then in the RenderContents section of the control check whether a value has been specified for _CustomJavascript and add the code to call that javascript if it has been. In my case I was creating a composite control and wanted an edit button to display that would call the user's javascript function.
if(_CustomJavascript.Trim() != "")
output.Write(string.Format("<input id='btnEdit' type='button' value='Edit' style='font-size: 6pt;' onclick='{0}' />", _CustomJavascript));
All the user has to do is write their function.
function EditAddress() {
alert("Hello, World");
}
And give the name of their function to the CustomJavascript property when declaring the control.
<cc:AddressReview ID="addressReview" runat="server" CustomJavascript="EditAddress()" />

How to change value in array from asp.net to javascript

I am loading the value of string myvalue (global string) in array arr in javascript by
function hello()
{
alert("hi");
var arr=[<% myvalue %>];
alert(arr);
}
protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
myvalue="1234";
Page.ClientScript.RegisterStartupScript(GetType(), "whatiskey", "hello();", true);
}
and updating myvalue on listbox1.item select and calling method which updates value of arr, but javascript arr does not load the new value
you have to put double quotes and write it like:
var arr=["<%=myvalue %>"];
or more better way :
var arr= new Array();
arr.push("<%=myvalue %>");
Register ListBox1 as partial post back element could also be one of the reason.
are you able debug, that ListBox1_SelectedIndexChanged is being called.
You need to something like below to stop this from multiple time registered, this can one of the reason not to call hello(). Use F12 to investigate the rendered HTML.
// Check to see if the client script is already registered.
if (!Page.ClientScript.IsClientScriptBlockRegistered(cstype, csname2))
{
Page.ClientScript.RegisterStartupScript(GetType(), "aNewKey", "hello();", true);
}

How to update value of registered script variable on post back?

I have a field agt_count that stores a number of agents. The default value is set as 1 and is increased by one for each new agent that is loaded.
Apparently I cannot re-register a script variable once it has been set so I need to know a way that I can update the script variables value if the page load is registered as a postback.
protected void Page_Load()
{
if (!IsPostBack)
{
Page.ClientScript.RegisterScriptVariable("agtCount", app.GetFieldValue("agt_count"));
}
else
{
Page.ClientScript.RegisterScriptVariable("agtCount", app.GetFieldValue("agt_count"));
{
}

C# variable value only update once when called in Javascript

I've look an found only a similar post and didn't really answer my question or maybe they did but I didn't understand. The post I read was this one: Why does this javascript variable I'm creating via C# only get updated once?
I'm using a paged gridview and every time it's object data source runs the SelectCountMethod, I use the returning value on javascript. But, I've noticed that even thought that same returned value changes, on the server side. On javascript this value doesn't update.
My program is quite long but I'll use a basic example and hopefully some of you will understand it.
Client side:
function SomeFuntion()
{
alert("<%=num%>");
}
Server side:
//Global variable
Public Static int num = 0;
Public int SelectCountMethod()
{
var num = SomeMethod(); //Returns int
return num;
}
For example, on the server side num returns 60 and then this value updates to 7. On the server side num equals 7 but on the client side it's still 60. Is there a way to update the client sides value?
I apologies for my poor typing skills, English is not my native language.
Some examples might be helpful and thanks in advance.
I noticed that it doesn't mater where I update this variable(on selectCount method or any other method), on the client side doesn't update.
Taking a look at your client-side code, the "<%=num%>" is actually run on the server. If you examined the source in your browser, what you'll see is:
function SomeFuntion()
{
alert("60");
}
As you can see--there is no variable to update. In order to see that "60" change to "7", you'd have to refresh the client to pick up the new value that the server has for "num".
You could modify your JS method like this
var myMsg = <%=num%>;
function SomeFuntion()
{
alert(myMsg);
}
and in the codebehind
public int num = 60;
public int SelectCountMethod()
{
num = SomeMethod(); //Returns int
ScriptManager.RegisterStartupScript(this,
this.GetType(),
"Funct",
"myMsg = " + num + ";",
true);
return num;
}
So every time your method SelectCountMethod() is called, your JS variable myMsg get a new value because of the line ScriptManager.RegisterStartupScript
Without your server side code, we won't be able to find the ideal solution. However, you could potentially do the following:
Use a Hidden Field to store the value.
Use a global variable for the page, to pass the value to global variable in your JavaScript.
The easiest would be the Hidden Field, the reason is you can easily modify the data on both Client and Server without any real issues. You'll want to ensure that you do not modify the state to often.
<input type="hidden" id="hdTimer" runat="server" />
Then you can do your JavaScript, such as:
$('#Example').on('change', function () {
$('#hdTimer').val('60');
});
Now throughout the Client code you'll be able to modify the field with no issues, but when you need to submit a form, for a PostBack. You can use the field server side:
var content = hdTimer.value;
As I noted though, excessive cross manipulation may cause an issue at some point. Depending on the complexity. Your other approach would be the Global.
// Server Side
var example = 60;
protected void Page_Load(object sender, EventArgs e)
{
}
So this Global will hold the value, but when you reinitialize the value at Page Load it will hold be able to push an updated value to your JavaScript:
//Client Side:
var example = '<%= example %>';
The key though, will be to ensure you properly reinitialize the value.
When you do a PostBack your page is reinitialized, which can modify values on you if you aren't aware. This is incredibly important.
Use an HttpHandler and jQuery. Google is your friend, there are several examples on SO as well.
I found a possible solution, inefficient, but it will have to do. I have a search textbox that every time the search button is clicked, updates the grid view with the retrieved data from a data base. When the Onclick is called it binds the data source with the gridView. What I did was call the SelectCountMethod again right below the binding and used the same parameters I had stored on the Object data source as paramaters for the selectCountMethod. Then the amount returned by the selectCount I stored it on a hiddenField and that's it.
//Global variables
string _param1 = string.Empty,
_param2 = string.Empty;
//On click method for search btn
protected void OnSearch(object sender, EventArgs e)
{
gv.DataBind();
someHiddenField = SelectCountMethod(param1, param2);
}
protected void OnSelecting(object sender, ObjectDataSourceSelectingEventArgs e)
{
try
{
e.InputParameters["Param1"] = param1;
_param1 = param1
e.InputParameters["Param2"] = param2;
_param2 = param2;
}
catch (Exception ex)
{
cvServerError.IsValid = false;
cvServerError.ErrorMessage = ex.Message;
}
}

Categories