1

I am trying to make a view which will have Partial Views being generated dynamically. Each Partial view is having a TextBox in which users put the name of a merchant.

Below is my view code:

@model BrightMediaTest.merchant

@{
ViewBag.Title = "AddMerchant";
Layout = "~/Views/Shared/_Layout.cshtml";
}

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")

<h2>AddMerchant</h2>

@Ajax.ActionLink("Add More", "AddMerchantPartial", "MerchantDB", new AjaxOptions { UpdateTargetId = "ajax-partial-view-box", InsertionMode = InsertionMode.InsertAfter })

@using (Html.BeginForm("AddMerchant", "MerchantDB", FormMethod.Post, new { }))
{
<ul style ="list-style: none;">
<li>
    @Html.LabelFor(m => m.merchantName)
</li>
<li>
    @Html.TextBoxFor(m => m.merchantName)
</li>
</ul>
<div id="ajax-partial-view-box">

</div>
<input type="submit" value="Add" />
}

As you can see there is a ActionLink in the Code "Add More", which actually adds partial view in the div "ajax-partial-view-box".

So, what I want to achieve is that when I submit the form. I want to take the text of all the TextBoxes which are added in the div "ajax-partial-view-box" dynamically by clicking the link "Add More".

There is no id associated with the TextBoxes in the partial views. Any help or suggestions is appreciated.

Here is my partial view code:

@model BrightMediaTest.merchant

<ul style="list-style: none;">
    <li>
        @Html.LabelFor(m => m.merchantName)
    </li>
    <li>
        @Html.TextBoxFor(m => m.merchantName)
    </li>
</ul>

So, I am trying to add all those merchants names in the database when the form is submitted. I have an Action "AddMerchant" which i will use to add all those merchant name in the database.

My merchant ViewModel is as follows:

namespace BrightMediaTest
{
using System;
using System.Collections.Generic;

public partial class merchant
{
    public merchant()
    {
        this.stores = new HashSet<store>();
    }

    public long merchantID { get; set; }
    public string merchantName { get; set; }

    public virtual ICollection<store> stores { get; set; }
}

This code was generated using Entity Framework.

Thanks

Aman Thakur
  • 159
  • 4
  • 14
  • 1
    Sounds like a reasonable plan. What exactly is the problem you are having? – Jasen May 21 '15 at 18:16
  • 2
    if all that's in the view is a text box / label, you should probably just do it in javascript. then you can wrap it up in a `$.ajax()` call to your controller – DLeh May 21 '15 at 19:49
  • 1
    You need to show your partial view. Does it also include `@Html.TextBoxFor(m => m.merchantName)`? (in which case you have invalid html because of the duplicate `id` attributes). You also need to show the method your posting back to. Is the signature `(string[] merchantName)`? And why do you have `@Scripts.Render("~/bundles/jqueryval")` since there is nothing to validate? –  May 22 '15 at 00:47
  • @Jasen: My problem is that i am not able to get the data from the text boxes which are there in the partial views. What I want to do is that when I submit the form, It should save all the names from all the partial views into the database. – Aman Thakur May 22 '15 at 13:13
  • @DLeh: how will i know which textbox value is picked up since there is no unique id of the textbox...:( – Aman Thakur May 22 '15 at 13:14
  • you don't need one. if you want to add many at once, they won't have IDs until you add them. – DLeh May 22 '15 at 13:15
  • @StephenMuecke: I have edited the post and added the my partial view code. It is a very basic code and thanks for notifying me I will remove the jqueryval, i placed it in there in hurry. I am trying to post the data to the Action AddMerchant but I am confused what parameter should I use in the Action method. For 1 textbox, i can use string as a parameter to the action method or may be a model class as well. But here i am a bit confused. Could you please guide me. Thanks – Aman Thakur May 22 '15 at 13:18
  • What does your ViewModel (`BrightMediaTest.merchant`) look like? Does it contain a `List` that would hold all of the merchant names? Are you posting a `List` to the Controller method? I'm thinking that you could maybe POST an array of merchant names, or join them into a single string on the Javascript side and send them back up this way. If you use `List` in your ViewModel, you can dynamically render a different form section the page for each merchant name in the ViewModel using Razor if you ever want to pull the form back up. – alex May 22 '15 at 13:33
  • @DLeh I am adding the partial view when the user clicks on the link "Add More"... – Aman Thakur May 22 '15 at 16:36
  • @alex I have added the viewmodel code...please have a look... – Aman Thakur May 22 '15 at 16:41
  • In both the main and partial view you need to use `@Html.TextBoxFor(m => m.merchantName, new { id = "" })` to avoid creating invalid html (although your label will not now work). The your POST method needs to be `public ActionResult AddMerchant(string[] merchantName)` - the parameter `merchantName` will contain an array of all the textbox values. –  May 23 '15 at 01:46
  • However you really should be using the `BeginCollectionItem` helper for this, or alternatively [this answer](http://stackoverflow.com/questions/29837547/set-class-validation-for-dynamic-textbox-in-a-table/29838689#29838689) gives a pure client side solution for dynamically adding collection items –  May 23 '15 at 01:46

2 Answers2

1

Ok, so I recreated a sample of your issue and it boils down to the fact that you can't post a list without specifying an index in the name attribute. See: MVC Form not able to post List of objects

At the end of the day, before the user clicks submit you will have n merchantName inputs. On your controller post action you will have something like this:

[HttpPost]      
public ActionResult AddMerchant(List<merchant> merchant)
{
    //post logic
}

In your current setup, the parameter will be null, BUT your form values in your HttpContext.Request will not be, they will look, something like this:

{merchantName=test1&merchantName=test2} 

You need to specify where those inputs are supposed to bind to, right now they are lost sheep in a huge pasture. That is done by specifying an index of the object that these inputs belong to.

Such as: merchant[i].merchantName where i is some index.

Once the partials are added they need to have an object class name and an index prepended to the merchantName value. Your html, before the user clicks add, would look something like this:

 <ul style="list-style: none;">
        <li>
            <label for="merchantName">merchantName</label>
        </li>
        <li>
            <input id="merchantName" name="merchant[0].merchantName" value="" type="text">
        </li>
    </ul>
    <div id="ajax-partial-view-box">


<ul style="list-style: none;">
    <li>
        <label for="merchantName">merchantName</label>
    </li>
    <li>
        <input id="merchantName" class="valid" aria-invalid="false" name="merchant[1].merchantName" value="" type="text">
    </li>
</ul>
<ul style="list-style: none;">
    <li>
        <label for="merchantName">merchantName</label>
    </li>
    <li>
        <input id="merchantName" class="valid" aria-invalid="false" name="merchant[2].merchantName" value="" type="text">
    </li>
</ul></div>
    <input value="Add" type="submit">

Then you form data will look something like:

{merchant%5b0%5d.merchantName=test1&merchant%5b1%5d.merchantName=test2&merchant%5b2%5d.merchantName=test3}  

And your action parameter will have 3 values in it with the correct data.

Now, how you do this is another matter. There are a number of ways to handle it through Razor or Javascript

Community
  • 1
  • 1
JasonWilczak
  • 2,303
  • 2
  • 21
  • 37
  • OP is only posting an array of value types not an array of complex objects, so this is all unnecessay - the POST method just needs to be `public ActionResult AddMerchant(List merchantName)` and it will be correctly bound –  May 23 '15 at 02:03
  • @StephenMuecke Yes, that is true. However, this setup allows OP to have a solution where they can introduce more properties for their merchant, if they desire. I would encourage you to post an answer with your solution as I think that is definitely viable. – JasonWilczak May 23 '15 at 10:12
  • @AmanThakur it stands for "Original Poster", in this case, the person who asked the question, which would be you. – JasonWilczak Jun 01 '15 at 10:15
0

What you want to do is lose the AjaxHelper. You'll need to take more control over your merchant parital insertion.

<a href="@Url.Action("AddMerchantPartial", "MerchantDB")" class="add-merchant">Add More</a>

Wire this up with jQuery. Use index to track your merchants added to the form so you can uniquely identity them later.

var index = 1;  // start with 1 because your form already has a static one

$(".add-merchant").on("click", function(event) {
    event.preventDefault();
    $.ajax({
        url: $(this).attr("href"),
        method: "GET",
        data: { index: index }
    })
    .done(function(partialViewResult) {
        $("#ajax-partial-view-box").append(partialViewResult);
    });
});

Modify your action

public ActionResult AddMerchantPartial(int index)
{
    ViewBag.Index = index;
    return View(new merchant());
}

Now in your partial you need to include the index and manually write the input elements.

@model merchant
@{
    var index = ViewBag.Index as int;
    var id = Model.merchantName + "_" + index + "_";     // merchantName_i_
    var name = Model.merchantName + "[" + index + "]";   // merchantName[i]
}

<ul>
    <li><label for="@id">@Model.merchantName</label></li>
    <li><input id="@id" name="@name" type="text" value="@Model.merchantName" /></li>
</ul>

Normally, I refrain from using ViewBag. You might consider adding index to the view model.

Your main form becomes

@using (Html.BeginForm("AddMerchant", "MerchantDB", FormMethod.Post, new { }))
{
    var id = Model.merchantName + "_0_";
    var name = Model.merchantName + "[0]";

    <ul style ="list-style: none;">
        <li>
            <label for="@id">@Model.merchantName</label>
        </li>
        <li>
            <input id="@id" name="@name" type="text" value="" />
        </li>
    </ul>
    <div id="ajax-partial-view-box"></div>
    <input type="submit" value="Add" />
}

And your post method would be

[HttpPost]
public ActionResult AddMerchant(List<merchant> merchants)
{
    ...
}
Jasen
  • 14,030
  • 3
  • 51
  • 68