0

My entities:

public class Meal
{
    [HiddenInput(DisplayValue = false)]
    public int Id { get; set; }
    [Required(ErrorMessage = "Proszę podać nazwę posiłku")]
    public string Name { get; set; }
    [Required(ErrorMessage = "Proszę podać ilość białka")]
    [Range(0.00, double.MaxValue, ErrorMessage = "Proszę podać dodatnią ilość.")]
    public double Protein { get; set; }
    [Required(ErrorMessage = "Proszę podać ilość węglowodanów")]
    [Range(0.00, double.MaxValue, ErrorMessage = "Proszę podać dodatnią ilość.")]
    public double Carbohydrates { get; set; }
    [Required(ErrorMessage = "Proszę podać ilość tłuszczy")]
    [Range(0.00, double.MaxValue, ErrorMessage = "Proszę podać dodatnią ilość.")]
    public double Fat { get; set; }
    [Required(ErrorMessage = "Proszę podać ilość kalorii")]
    [Range(0.00, double.MaxValue, ErrorMessage = "Proszę podać dodatnią ilość.")]
    public double Calories { get; set; }
}

public class EatenMeal
{
    public int Id { get; set; }
    public virtual Meal Meal { get; set; }
    public virtual MealType MealType { get; set; }
    public double Serving { get; set; }
    public string Username { get; set; }
    public DateTime Date { get; set; }
}

public class MealType
{
    public int Id { get; set; }
    public string Name { get; set; }
}

In MealController's view MealList which displays meals from datebase. And there is a button "Add" which refers to action AddEatenMeal in EatenMealController.

public ActionResult AddEatenMeal(int id)
{
    var meal = mealRepository.GetMeal(id);
    EatenMeal eatenMeal = new EatenMeal() { Meal = meal, Username = User.Identity.Name };
    return View(eatenMeal);
}

[HttpPost]
public ActionResult AddEatenMeal(EatenMeal eatenMeal)
{
    if(ModelState.IsValid)
    {
        eatenMealRepository.AddEatenMeal(eatenMeal);
        RedirectToAction("Index", "Home");
    }
    return RedirectToAction("Index", "Home");
}

I am creating there object EatenMeal and partially initializing this object. Then I am passing this object to View to further initializing.

@model Domain.Entities.EatenMeal

@{
    ViewBag.Title = "Dodawanie posiłku do dziennika";
}

@using (Html.BeginForm("AddEatenMeal","EatenMeal", FormMethod.Post, new {@class = "form"}))
{
    @Html.HiddenFor(x => x.Meal.Name)
    @Html.HiddenFor(x => x.Username)
    @Html.HiddenFor(x => x.Meal.Calories)
    @Html.HiddenFor(x => x.Meal.Carbohydrates)
    @Html.HiddenFor(x => x.Meal.Fat)
    @Html.HiddenFor(x => x..Meal.Protein)
    @Html.HiddenFor(x => x.Meal.Id)
    @Html.HiddenFor(x=>x.Username)
    <div class="form-group">
        @Html.Label("Nazwa posiłku")
        @Html.Label(Model.Meal.Name, new { @class = "form-control" })
    </div>
    <div class="form-group">
        @Html.Label("Porcja (g)")
        @Html.TextBoxFor(x => x.Serving, new { @class = "form-control" })
    </div>
    <div class="form-group">
        @Html.Label("Typ posiłku")
        @Html.DropDownListFor(x=>x.MealType)????
    </div>
    <div class="form-group">
        @Html.Label("Data spożycia")
        @Html.TextBoxFor(x => x.Date, new { @class = "form-control", @id="date-eaten", @Value=DateTime.Today.ToShortDateString()})
    </div>
    <input type="submit" class="btn btn-info" value="Dodaj" />
}

Now I have a question. Is it correct to hiding fields? I don't know how I can save data from first controller to second in other way. And is a second question. How I can make DropDownListFor for property MealTye in EatenMeal?

thenewgai
  • 55
  • 6
  • It is quite common to use hidden fields for different things so I'd say it's ok. Polski kod :-) – t3chb0t Dec 07 '14 at 20:28
  • It is not necessary to include all those hidden fields (you are just sending a lot of data to the client and then posting it back unchanged, degrading performance and opening yourself to overposting attacks). Better to just get the data again when you post back (including the date and username). Where does the list of `MealType` come from to display in your dropdown? –  Dec 07 '14 at 20:29

1 Answers1

0

Rather than sending and receiving a whole lot of unused data across the wire and opening yourself to over posting attack, create a view model that represents what you want to display and edit. See What is a view model in MVC?

View model

public class EatenMealVM
{
  public int MealID { get; set; }
  [Display(Name="Nazwa posiłku")]
  public string MealName { get; set; }
  [Display(Name = "Typ posiłku")]
  [Required(ErrorMessage = "Please select a meal")]
  public int? MealTypeID { get; set; }
  [Display(Name = "Porcja (g)")]
  public double Serving { get; set; } // is this really double?
  [Display(Name = "Data spożycia")]
  [DataType(DataType.Date)]
  public DateTime Date { get; set; }
  public SelectList MealTypeList { get; set; }
}

Controller

public ActionResult AddEatenMeal(int id)
{
  var meal = mealRepository.GetMeal(id);
  var mealTypes = // get the list of meal types from the database
  EatenMealVM model = new EatenMealVM()
  {
    MealID = meal.Id,
    MealName = meal.Name,
    MealTypeList = new SelectList(mealTypes, "ID", "Name")
  };
  return View(model);
}

View

@model EatenMealVM
....
@using (Html.BeginForm())
{
  @Html.HiddenFor(m => m.MealID)

  @Html.DisplayNameFor(m => m.MealName)
  @Html.DisplayFor(m => m.MealName)

  @Html.LabelFor(m => m.MealTypeID)
  @Html.DropDownListFor(m => m.MealTypeID, Model.MealTypeList, "--Please select--")
  @Html.ValidationMessageFor(m => m.MealTypeID)

  @Html.LabelFor(m => m.Serving)
  @Html.TextBoxFor(m => m.Serving, new { @class = "form-control")
  @Html.ValidationMessageFor(m => m.Serving)

  @Html.LabelFor(m => m.Date)
  @Html.TextBoxFor(m => m.Date)
  @Html.ValidationMessageFor(m => m.Date, new { @class = "form-control" })

  <input type="submit" class="btn btn-info" value="Dodaj" />
}

Post method

[HttpPost]
public ActionResult AddEatenMeal(EatenMealVM model)
{
  if (!ModelState.IsValid)
  {
    var mealTypes = // get the list of meal types from the database
    model.MealTypeList = new SelectList(mealTypes, "ID", "Name");
    return View(model);
  }
  // Initialize new EatenMeal class
  // Map properties from view model (including setting user name) 
  // Save and redirect
}

Note also the use of [Display] attribute and @Html.LabelFor(). Currently you not creating 'real' labels (they are not associated with the corresponding control)

Community
  • 1
  • 1
  • ok, thanks for very usefulness answer but I don't sure about entity EatenMeal. Do I should change public virtual MealType MealType { get; set; } to public int MealTypeId {get; set;}? – thenewgai Dec 08 '14 at 13:54
  • and I can't initialize EatenMeal.Meal in Post method. So I should add MealId in EatenMealVM? – thenewgai Dec 08 '14 at 14:36
  • Yes you should add `int MealID` in `EatenMealVM` and use `@Html.HiddenFor(m => m.MealID)` in the view. Now when you post back you have initialize a new `EatenMeal` and assign the `Meal` based on the `MealID` (answer updated) and you can add `public int MealTypeId {get; set;}` (but keep `public virtual MealType MealType { get; set; }`) –  Dec 08 '14 at 22:24
  • Look [link](http://i.imgur.com/Ay8tade.png) MealId is 1 but in database [link](http://i.imgur.com/HSzcFRU.png) MealId is 1022 because is 1022 meal in DB, but why? MealId in the second screen should be 1 not 1022 – thenewgai Dec 11 '14 at 21:30
  • Based on your [other question](http://stackoverflow.com/questions/27433195/add-new-entity-with-complex-type), it looks like its a problem with the `eatenMealRepository.AddEatenMeal()` method. You need to debug your code and check its inserting the correct value into the database. –  Dec 12 '14 at 03:51
  • i debug mode when I am in repository in method AddEatenMeal(EatenMeal eatenMeal) then passing model eatenMeal has MealId 1. And after that in DB MealId is equal 1022 – thenewgai Dec 12 '14 at 10:15
  • Have you made it an auto increment property? –  Dec 12 '14 at 10:59
  • I don't use any attribute in entities. This is the definition of my tables [link](http://pastebin.com/R4guPPbQ) – thenewgai Dec 12 '14 at 11:09