views:

584

answers:

4

Hi,

I have the following requirement for creating a user profile in my application:

User should be able to enter multiple phone numbers/email addresses in his profile.
The screen looks somewhat like this:
- By default, on page load a single textbox for phone and email are shown.
- User can click a "+" button to add additional numbers/addresses.
- On clicking the "+" button we need to add another textbox just below the first one. User can add as many numbers/addresses as he wants. On submit, the server should collect all numbers/emails and save it in DB.

I tried using the Repeater control to do this. On page_load I bind the repeater to a "new arraylist" object of size 1. So, this renders fine - user sees a single textbox with no value in it.
When he clicks the "+" button, I ideally want to use javascript to create more textboxes with similar mark-up as the first.

My questions are these:

  1. Can I render the new textboxes anyway using js? I notice that the HTML rendered by the repeater control is somewhat complex (names/ids) etc. and it might not be possible to correctly create those controls on client-side.
  2. If there is a way to do #1, will the server understand that these additional inputs are items in the repeater control? Say, I want to get all the phone numbers that the user entered by iterating over Repeater.DataItems.
  3. Conceptually, is my approach correct or is it wrong to use the Repeater for this? Would you suggest any other approach that might handle this requirement?

Coming from a Struts/JSP background, I am still struggling to get a grip on the .NET way of doing things - so any help would be appreciated.

A: 

If you want to user the repeater, I think the easiest way is to put the repeater in a ASP.Net AJAX update panel, add the extra textbox on the sever side.

There are definitely other way to implement this without using repeater, and it maybe much easier to add the textbox using js.

Estelle
Thanks Estelle - repeater was not really necessary. It was just my first approach when looking for a way to implement repeating form elements.
redman12
A: 
  1. No, but you can create input elements similar to what TextBox controls would render.

  2. No. ASP.NET protects itself from phony data posted to the server. You can't make the server code think that it created a TextBox earlier by just adding data that it would return.

  3. The approach is wrong. You are trying to go a middle way that doesn't work. You have to go all the way in either direction. Either you make a postback and add the TextBox on the server side, or you do it completely on the client side and use the Request.Form collection to receive the data on the server side.

Guffa
Guffa, your answers help in my understanding. I am familiar with the Request.Form approach (which is how we basically do it in Struts) - just wanted to explore if ASP.Net had something better to offer.
redman12
@redman12: If you do a postback and create the inputs as TextBox controls using the repeater, then the controls will pick up the data so that you can read it from their Text properties, but if you create the inputs client side you have to use Request.Form.
Guffa
+1  A: 

Welcome to the hairball that is dynamically-added controls in ASP.NET. It's not pretty but it can be done.

You cannot add new fields dynamically using javascript because the new field would have no representation in the server-side controls collection of the page.

Given that the requirements are that there is no limit to the number of addresses a user can add to the page, your only option is to do "traditional" dynamic ASP.NET controls. This means that you must handle the adding of the control server-side by new-ing a new object to represent the control:

private ArrayList _dynamicControls = new ArrayList();
public void Page_Init()
{
    foreach (string c in _dynamicControls)
    {
        TextBox txtDynamicBox = new TextBox();
        txtDynamicBox.ID = c;
        Controls.Add(txtDynamicBox);
    }
}

public void AddNewTextBox()
{
    TextBox txtNewBox = new TextBox();
    txtNewBox.ID = [uniqueID] //  Give the textbox a unique name
    Controls.Add(txtNewBox);    
    _dynamicControls.Add([uniqueID]);
}

You can see here that the object that backs each dynamically-added field has to be added back to the Controls collection of the Page on each postback. If you don't do this, data POSTed back from the field has nowhere to go.

Dave Swersky
+3  A: 

The repeater control may be a bit of overkill for what you're trying to accomplish. It is mainly meant as a databound control for presenting rows of data.

What you can do is to dynamically create the boxes as part of the Page_Load event (C#):

TestInput.aspx :

<form id="form1" runat="server">
    <asp:HiddenField ID="hdnAddInput" runat="server" />
    <asp:Button ID="btnPlus" OnClientClick="setAdd()" Text="Plus" runat="server" />
    <asp:PlaceHolder ID="phInputs" runat="server" />

</form>
<script type="text/javascript">
    function setAdd() {
        var add = document.getElementById('<%=hdnAddInput.ClientID%>');
        add.value = '1';
        return true;
    }
</script>

TestInput.aspx.cs:

protected void Page_Load(object sender, EventArgs e)
{
    if (ViewState["inputs"] == null)
        ViewState["inputs"] = 1;

    if (hdnAddInput.Value == "1")
    {
        ViewState["inputs"] = int.Parse(ViewState["inputs"].ToString()) + 1;
        hdnAddInput.Value = "";
    }

    for (int loop = 0; loop < int.Parse(ViewState["inputs"].ToString()); loop++)
        phInputs.Controls.Add(new TextBox() { ID = "phone" + loop });
}

I ended up using a PlaceHolder to dynamically add the text boxes and a HiddenField to flag when another TextBox needed to be added. Since the IDs were matching, it maintains the ViewState of the controls during each postback.

CAbbott
+1 I like this solution- since each field is identical (except for ID) all you have to do is know how many have been added... bravo!
Dave Swersky
@Dave: Thanks - praise indeed. :)
CAbbott
Thanks Dave and CAbbott for pointing me in the right direction. I will try this approach.
redman12