1

I am just getting started with ASP.net mvc 5 and got stuck into one problem that I have explained below:

I have User and History models. And user can have more then one history.

User model:

public class User
{
    public int ID { get; set; }
    public string TicketType { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Gender { get; set; }

    public virtual ICollection<History> Histories { get; set; }
}

History model:

public class History
{
    public int ID { get; set; }
    public int Year { get; set; }
    public int Month { get; set; }
    public int Day { get; set; }

    public virtual User User { get; set; }
}

UserController:

public class UsersController : Controller
{
    private MyDbContext db = new MyDbContext();

    public ActionResult Create()
    {
        var user = new User();
        user.Histories = new List<History>();
        user.Histories.Add(new History { });
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "ID,TicketType,FirstName,LastName")] User user)
    {
        if (ModelState.IsValid)
        {
            db.Users.Add(user);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(user);
    }
}

Here is my form:

Create.cshtml

@model MyApp.Models.User

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    @Html.Partial("_Form")

    <input type="submit" value="Create" class="btn btn-lg btn-success" />
}

_Form.cshtml: Just to remove duplication in both Create and Edit forms

        @model MyApp.Models.User
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        <div class="form-group">
            @Html.LabelFor(model => model.TicketType, htmlAttributes: new { @class = "control-label" })
            @Html.DropDownListFor(model => model.TicketType, new List<SelectListItem>
                    {
                        new SelectListItem() {Text = "OPD", Value="opd"},
                        new SelectListItem() {Text = "Emergency", Value="emergency"},
                    }, htmlAttributes: new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.TicketType, "", new { @class = "text-danger" })
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label" })
            @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label" })
            @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
        </div>


        <!-- TODO: fix these form elements which are 
         related to History not User. I am not sure how to do that. 
         Also NOTE that, user fields are saving properly and 
         update is also working. -->


        <div class="form-group">
            <label class="control-label">Year</label>
            <input type="text" value="" class="form-control" />
        </div>

        <div class="form-group">
            <label class="control-label">Month</label>
            <input type="text" value="" class="form-control" />
        </div>

        <div class="form-group">
            <label class="control-label">Day</label>
            <input type="text" value="" class="form-control" />
        </div>
przbadu
  • 5,769
  • 5
  • 42
  • 67
  • 1
    Refer [this answer](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) for some options, and [this one](http://stackoverflow.com/questions/40539321/partial-view-passing-a-collection-using-the-html-begincollectionitem-helper/40541892#40541892) for a detailed example using `BeginCollectionItem()` –  Jul 25 '17 at 11:38
  • @StephenMuecke: Yes I had looked into that solution and for me, I won't be having multiple nested form so, if I could solve it without using `BeginCollectionItem` helper that would be great? – przbadu Jul 25 '17 at 11:43
  • What do you mean multiple nested forms (neither link has that - especially since it would not work). Not sure why you would not want to use the correct tool fr the job, but look at the 2nd option in the first link –  Jul 25 '17 at 11:46
  • @StephenMuecke: Thank you for your help, giving `BeginCollectionItem()` helper a try. :) . After reading first link answer, you also provided a manual solution, thanks for that too. – przbadu Jul 25 '17 at 11:51
  • @StephenMuecke: My final problem after using `BeginCollectionItem()` helper is; how to permit `Year`, `Month`, `Day` in `Create` action i.e: `[Bind(Include = "ID,TicketType,FirstName,LastName")]`? Please help – przbadu Jul 27 '17 at 12:43
  • FYI, `Create(User user)` without `[Bind(Include = ....)]` is working and is creating both user and their history. For security reason, I want to allow My history attributes there. – przbadu Jul 27 '17 at 12:45
  • This is how Year field name is generated in HTML, `Histories[c7215c89-3621-4feb-baa1-0da9920bf772].Year` using `BeginCollectionItem` – przbadu Jul 27 '17 at 12:47
  • Sorry, I'm not sure what your asking. And your `[Bind]` attribute excludes the `Histories` property from binding. But since your creating/editing data, always use a view model and get rid of that awful attribute. And your generating a prefix named `PatientHistories` but the property is named `Histories` - they need to match. –  Jul 27 '17 at 12:50
  • @StephenMuecke: I corrected that `PatientHistories`. And yes `[Bind]` attribute excluding My `Histories` and that was my question, how to include it..... Sorry, I am completely new to .net technologies and asp.net, and learning it from last couple of days. – przbadu Jul 27 '17 at 13:02
  • Just remove the attribute altogether since your wanting to bind everything –  Jul 27 '17 at 13:04
  • As per your comment, ` But since your creating/editing data, always use a view model and get rid of that awful attribute.`, it looks like I need ViewModel to include all attributes and remove `[Bind]` attributes from controller action.... But any chances to include History in that `[Bind]` itself along with `User's` attributes? – przbadu Jul 27 '17 at 13:04
  • There is no need for it - by default every property is bound and it appears you want that - just delete it –  Jul 27 '17 at 13:09
  • @StephenMuecke: Thanks a lot, I think I now understand what I have to do, I am checking `ViewModel` concept, looks like that is what I am missing now. – przbadu Jul 27 '17 at 13:13

1 Answers1

1

You Have First created ViewModel. The view model combine to user and history model below the code

Public class userviewmodel
{

     public string TicketType { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
     public string Gender { get; set; }         
     public int Year { get; set; }
     public int Month { get; set; }
     public int Day { get; set; }

 }

second, your chtml view is modified :

set model userviewmodel

 @model MyApp.Models.Userviewmodel

Form design change code in year, month and day

  <Table>
    <thead> 
       <tr>
          <td>Year</td>
          <td>Month</td>
          <td>Day</td>
       </tr>
      </thead>
      <tbody id="tbodyval">
      </tbody>
    </table>


<div class="form-group">
        <label class="control-label">Year</label>
        @Html.EditorFor(model => model.Year, new { htmlAttributes = new { @class = "form-control" } })
 </div>

 <div class="form-group">
    <label class="control-label">Year</label>
    @Html.EditorFor(model => model.month, new { htmlAttributes = new { @class = "form-control" } })
 </div>

 <div class="form-group">
    <label class="control-label">Year</label>
    @Html.EditorFor(model => model.day, new { htmlAttributes = new { @class = "form-control" } })
  </div>
  <input type="button" value="add">

if the add button click create new Row in the table t body every inside textbox or HiddenField Vlaue Assied the current textbox value

For example jquery function

  $("#add").click(function(){
      string row="<tr><td><input type='text' value='$('#year').val(), 
       name='Yearlist'></td>
        <td><input type='text' value='$('#month').val(), name='monthlist'></td>
       <td><input type='text' value='$('#day').val(), name='daylist'></td>
          </tr>"
        $("#tbodyval").append(row);
    });

cahnge your code in controller:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create( Userviewmodel usermodel, string[] yearList,string[] monthList,string[] dayList())
{
    if (ModelState.IsValid)
    {

        user model=new user()
        model.TicketType=.usermodel.TicketType;
        model.FirstName=usermodel.FirstName;
        model.LastName=usermodel.LastName;
        model.Gender=usermodel.Gender;

       for(int i=0;i<yearlist.count();i++)
       {
         History child=new History()  

         child.Year=yearlist[i].Year;
         child.month=montList[i].month;
         chile.day=dayList[i].day;            
         model.Histories.add(child);  
       }                      

        db.Users.Add(model);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(user);
}
kari kalan
  • 497
  • 3
  • 20
  • Your misunderstanding OP's question. They want to be able to add multiple `History` objects, not just one (its a collection) –  Jul 27 '17 at 12:55
  • @Kari: Thanks, I think this is a simple solution, let me try it. In the create Action I only want to save one history, i.e today's Date, so I think there is no need to add Array of objects. I guess, all I am missing is understanding of `ViewModel`, I better learn about it before moving forward. Thanks a lot – przbadu Jul 27 '17 at 13:12