Remember last Friday I blogged on Deeper into CustomValidator Part 2, I actually mentioned about RegisterStartupScript in the Conclusion. If you have not read about this, check this out here http://community.sgdotnet.org/blogs/chuawenching/archive/2007/04/06/ASP.NET-2.0-Deeper-into-CustomValidator-Part-2.aspx
I am not sure whether most of you are aware of this RegisterStartupScript. I believe some of you had coded it, heard of it or probably new to it.
Since I am not a theory guy, basically you can use RegisterStartupScript to inject JavaScript markup into your web pages. Then think of it, why should do this if you can manually place the JavaScript code block in your html during coding instead of runtime.
I will provide a simple scenario, so you can visualize on what you need this for?
Scenario
Below is the web project hierarchy
Master Page
è frmA
o uclA
è frmB
o uclB
You have a standard and reusable control called uclBizLogic which is part of both uclA and uclB.
In this user control, you have a lot of textboxes, 2 checkbox lists, 1 button and validation controls. When you click on the button in frmA, it will redirect to frmB.
Let’s drill down on the CheckBoxLists validation logic.
Check the tables diagram below:
Parent Item | : | [CheckBoxListParent] [CustomValidator here] [ListItem index=”0” value=”Yes”] |
Child Item | : | [CheckBoxListChild] [ListItem index=”0” value=”MIND”] [ListItem index=”1” value=”INETA APAC”] |
Okay you will have only one item in the Parent CheckBoxList. It is either you checked it or you don’t. In Child CheckBoxList, you have 2 items (MIND and INETA APAC).
You have logic here. If you checked the Parent CheckBoxList’s ListItem, you must check either one of the ListItem in Child’s CheckBoxList or you can check both of them. But you cannot leave both of them unchecked or else you will be prompt an error “Please checked either one in Child”.
Now looking at this scenario, you will be wondering why can’t I code JavaScript and place this in the MasterPage head element.
<script language="javascript">
function ParentValidation(sender, args)
{
if (document.all('_ctl0_ContentPlaceHolder1_uclA_ uclBizLogic _cblParent_0').checked == true)
{
if (document.all('_ctl0_ContentPlaceHolder1_uclA_uclBizLogic_cblChild_0').checked != true
&& document.all('_ctl0_ContentPlaceHolder1_ uclA_uclBizLogic_cblChild _1').checked != true)
{
args.IsValid = false;
}
else
{
args.IsValid = true;
}
}
else
{
args.IsValid = true;
}
</script>
If you do notice one thing here, I am actually hardcoding the ClientID of all the controls here. But remember this; you actually have uclBizLogic in both frmA and frmB. If you only have this user control in 1 form, you can just follow the code above. What do I mean by this?
In frmA, you will have ID like this:
_ctl0_ContentPlaceHolder1_uclA_uclBizLogic_cblChild_0
In frmB, you will have ID like this:
_ctl0_ContentPlaceHolder1_uclB_uclBizLogic_cblChild_0
So now what? How can I dynamically pass in the ClientID of the controls into my JavaScript and still using ClientSideValidation of a CustomValidator?
This is where RegisterStartupScript comes in handy.
Just take note one more thing, on the syntax changes
Previously:
this.Page.RegisterStartupScript(jsFunctionName, jsBuilder.ToString());
Now:
this.Page.ClientScript.RegisterStartupScript(typeof(Page), jsFunctionName, jsBuilder.ToString());
The 1st code is already deprecated in .NET 2.0. Well you can still use it but you will face some unknown problems when you try to register some JavaScripts. I had faced some problems using the 1st code in ASP.NET 2.0, but I was not ready to blog on the exact problems.
In order to prevent this to happen, use the 2nd one instead. It is also recommended by MSDN to use the 2nd one.
Now RegisterStartupScript is part of ClientScript and you need to specify the Type as the 1st argument. Based on my experiences, if you are using this either in a Page or a User Control, by specifying typeof(Page) should be fine.
Below is the CustomValidator code. It works the same way like you will have a JavaScript code in the MasterPage.
<asp:CustomValidator ID="csvParent" runat="server" Display="Dynamic" ErrorMessage="Please checked either one in Child"
ClientValidationFunction="ParentValidation"> Please checked either one in Child </asp:CustomValidator>
Take note, you do not specify the ControlToValidate attribute here. There is a reason here. Basically CheckBoxList does not support validators. You will get an error for that when you run it.
Now you will create a private method inside your uclBizLogic called ParentValidation.
private void PublishInternetValidation()
{
string jsFunctionName = "ParentValidation";
StringBuilder jsBuilder = new StringBuilder();
jsBuilder.Append("<script language=\"javascript\">\n");
jsBuilder.Append("function " + jsFunctionName + "(sender, args)\n");
jsBuilder.Append("{\n");
jsBuilder.Append("\tif (document.all('" + cblParent.ClientID + "_0').checked == true)");
jsBuilder.Append("{\n");
jsBuilder.Append("\tif (document.all('" + cblChild.ClientID + "_0').checked != true && document.all('" + cblChild.ClientID + "_1').checked != true)");
jsBuilder.Append("{\n");
jsBuilder.Append("\t\targs.IsValid = false;");
jsBuilder.Append("}\n");
jsBuilder.Append("\telse");
jsBuilder.Append("{\n");
jsBuilder.Append("\t\targs.IsValid = true;");
jsBuilder.Append("}\n");
jsBuilder.Append("}\n");
jsBuilder.Append("\telse");
jsBuilder.Append("{\n");
jsBuilder.Append("\t\targs.IsValid = true;");
jsBuilder.Append("}\n");
jsBuilder.Append("}\n");
jsBuilder.Append("</script>");
this.Page.ClientScript.RegisterStartupScript(typeof(Page), jsFunctionName, jsBuilder.ToString());
}
Then you need to call this method inside your Page_Load event. Remember do not put this method calling inside your !Page.IsPostBack conditional checking. I will explain why below?
protected void Page_Load(object sender, EventArgs e)
{
ParentValidation();
}
Whenever you perform a postback, it will register that JavaScript to your page. So it also means that you click on a button to insert, update or delete you still want the validation logic to work here. So let’s assume this if we place this ParentValidation inside the condition checking like below:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ParentValidation();
}
}
So what will happen here? Let’s visualize a bit here (I did not follow the exact Page Life Cycle here or else it will be too long)
Click on a link frmA à (go into Page_Load event of uclBizLogic) àregister JavaScript à Display frmA à Insert your data inside uclBizLogic à will validate if there is an issue à click on Save button à redirects to frmB à (go into Page_Load event of uclBizLogic) à register JavaScript à Display frmB
Cool it works fine. What if you want to save again which will cause another postback?
This time it will not register the JavaScript code. You will not have the validation to work anymore as it is not executing your ParentValidation method. Obvious right J
You need to place this method in the right place at the right time.
Conclusion:
I really hope you see a value in why you need this RegisterStartupScript for? Second, there is another thing that you need to take note here. Even I use the code above, I still hardcode certain values. If you do notice, here is the code:
jsBuilder.Append("\tif (document.all('" + cblParent.ClientID + "_0').checked == true)");
jsBuilder.Append("{\n");
jsBuilder.Append("\tif (document.all('" + cblChild.ClientID + "_0').checked != true && document.all('" + cblChild.ClientID + "_1').checked != true)");
As you see, I was not able to figure out how to get the ClientID of the ListItem part of both cblParent and cblChild? So after analyzing the View Source of the web forms, I manually placed the “_0” or “_1” into this. I believe this is not a good practice. I promise I will look further into this so I can work out a better approach to handle this situation.
If you know a better way, do let me know. I hope you find this useful J
Thanks.