0

My Drop-Downs works well the problem is when I want to save my form.

here is my controller

[HttpPost]
[ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "Id,TourId,StartDate,EndDate")] TourDate tourDate)
    {
        if (ModelState.IsValid)
        {
            db.TourDates.Add(tourDate);
            db.SaveChanges();

            return RedirectToAction("Index", "Home");
        }

        ViewBag.TourId = new SelectList(db.Tours, "Id", "TourName", tourDate.TourId);
        return RedirectToAction("Index", "test");
    }


     [HttpPost]
    public JsonResult GetT(int? id)
    {
        var tours = db.Tours.Where(e => e.CountryId == id).ToList()
           .Select(e => new
           {
               Id = e.Id,
               TourName= e.TourName
           }).ToList();


        return Json(tours);
    }

and here is My form. in this From, selecttag with id=TourId is filling with data in ajax from the outer drop down dropdownId and it works fine

@Html.DropDownList("dropdownId",
Model.Countries.Select(m => new SelectListItem
{
    Value = m.Id.ToString(),
    Text = m.CountryName
}),
new { @class = "form-control" })


@using (Html.BeginForm("Create", "test"))
{
 @Html.AntiForgeryToken()

 <div class="form-horizontal">
    <h4>TourDate</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

    <div class="form-group">
        @Html.LabelFor(model => model.TourDate.TourId, "TourId", htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            <select class="form-control" id="TourId"></select>

            @Html.ValidationMessageFor(model => model.TourDate.TourId, "", new { @class = "text-danger" })
        </div>
    </div>

    <br />
    <div class="form-group">
        @Html.LabelFor(model => model.TourDate.StartDate, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.TourDate.StartDate, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.TourDate.StartDate, "", new { @class = "text-danger" })
        </div>
    </div>
    <br />
    <div class="form-group">
        @Html.LabelFor(model => model.TourDate.EndDate, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.TourDate.EndDate, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.TourDate.EndDate, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
</div>

}

the problem is when I submit the form no diffrent which Tour is chosen always there is TourId=0.

enter image description here

Appreciate any help and also here is the ajax if needed

$("#dropdownId").change(function () {
    $('#TourId').empty();
    var countrySelected = $(this).val();

    $.ajax
        ({
            url: '/test/GetT/' + countrySelected,
            type: 'POST',
            data: {
                'countryId': countrySelected
            },
            success: function (data)
            {
                var $select = $('#TourId');
                for (var i = 0; i < data.length; i++)
                {

                    $('<option/>').attr('value', data[i].Id).html(data[i].TourName).appendTo('#TourId');

                }



            }
        });

});
neda Derakhshesh
  • 1,103
  • 2
  • 20
  • 43
  • 1
    Your select has no `name` attribute so it does not submit a value. But your code has numerous other issues. Refer [this answer](http://stackoverflow.com/questions/28627421/better-way-to-load-2-dropdown-in-mvc/28640420#28640420) and this [DotNetFiddle](https://dotnetfiddle.net/1bPZym) for how to implement cascading dropdownlists –  Sep 24 '16 at 10:26
  • 1
    And your `return RedirectToAction("Index", "test");` line of code if `ModelState` is invalid makes no sense (the poor user just assumes the record has been saved). You need to return the view so errors can be corrected (but in your case validation will not work correctly anyway and all the dropdownlists will be reset to display the first option) –  Sep 24 '16 at 10:34
  • @StephenMuecke thanks really for you care. those two links helped me alot, my code is what I could understand in my project situation, and .yes I just want to know if it saved or not thats a test code I need to work more on it.I put this here just to test if it saves then makes me happy – neda Derakhshesh Sep 24 '16 at 10:37
  • 1
    If you want to get a value to be submitted, then add a `name` attribute - `` - but only do that to test it (you have multiple other issues with your code that will cause failure - you must use a view model and study the controller code in the DotNetFiddle) –  Sep 24 '16 at 10:41
  • I put `name="TourId"`to my select tag but still no difference. using view model would affect on saving methods? because i'm sticking in saving methods unfortunately. and thanks really so much again for all you'r helps with every little details. Really Appreciate @StephenMuecke – neda Derakhshesh Sep 24 '16 at 11:00
  • 1
    If course it will work - but looking at your view, everything else is prefixed with `TourDate` (so to match this it would need to be `name="TourDate.TourId"` but then that makes no sense based on your POST method and its `[Bind]` attribute (based on the code you have shown, nothing will bind at all) –  Sep 24 '16 at 11:03
  • 1
    And your editing data, so **always** use a view model - refer [What is ViewModel in MVC?](http://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc) –  Sep 24 '16 at 11:04
  • @StephenMuecke I don't know how to thank you. it solved. Really thank you for telling me how much important is view models. I've never used to them. I will work more on them and will understand them well for sure. and please if possible answer the question.it solved thank you so much – neda Derakhshesh Sep 24 '16 at 11:11

1 Answers1

1

The reason your second <select> element does not submit a value is because it does not have a name attribute. It would need to be

<select class="form-control" name="TourDate.TourId" id="TourId"></select>

However there are multiple other errors and problems with your code. To note just a couple of them:

  1. The model in your view is is not typeof TourDate (its a model containing a property which is TourDate) so none of your controls can be bound to your TourDate tourDate parameter in the POST method (the model in the parameter needs to be the same as the model in the view, or you need to use the [Bind(Prefix = "TourDate")] attribute and you also need to remove the [Bind(Include = "..")] attribute).
  2. Your not getting client side validation associated with your dropdownlists and in the POST method, if ModelState is invalid, your just redirecting (the user would just assume the object has been saved and not understand what is going on). You need to return the view so errors can be corrected, but in your case, the values the user has entered will be reset to the defaults (an annoying user experience).

Your editing data, so you should always use a view model and in the controller method you need to populate the SelectList's to account for initial values and edited values (for when you need to return the view). You code should be

public class TourDateVM
{
    [Required(ErrorMessage = "Please select a country")]
    [Display(Name = "Country")]
    public int? SelectedCountry { get; set; }
    [Required(ErrorMessage = "Please select a tour")]
    [Display(Name = "Country")]
    public int? SelectedTour { get; set; }
    [Required(ErrorMessage = "Please enter a start date")]
    [Display(Name = "Start date")]
    public DateTime? StartDate { get; set; }
    .... // ditto above for EndDate
    public IEnumerable<SelectListItem> CountryList { get; set; }
    public IEnumerable<SelectListItem> TourList { get; set; }
}

And in the controller

public ActionResult Create()
{
    TourDateVM model = new TourDateVM();
    ConfigureViewModel(model);
    return View(model);
}
[HttpPost]
public ActionResult Create(TourDateVM model)
{
    if (!ModelState.IsValid)
    {
        ConfigureViewModel(model);
        return View(model);
    }
    TourDate tour = new TourDate()
    {
        TourId = model.SelectedTour,
        StartDate = model.StartDate,
        EndDate= model.EndDate
    };
    db.TourDates.Add(tour);
    db.SaveChanges();
    return RedirectToAction("Index", "Home");
}
private ConfigureViewModel(TourDateVM model)
{
    var counties = db.Countries;
    model.CountryList = new SelectList(counties, "ID", "Name"); // adjust to suit your property names
    if (model.SelectedCountry.HasValue)
    {
        var tours = db.Tours.Where(e => e.CountryId == model.SelectedCountry);
        model.TourList = new SelectList(tours, "Id", "TourName");
    }
    else
    {
        model.TourList = new SelectList(Enumerable.Empty<SelectListItem>());
    }
}

and finally in the view (note that both dropdownlists need to be inside the <form> element)

@model TourDateVM
....
@using Html.BeginForm())
{
    ....
    <div class="form-group">
        @Html.LabelFor(m => m.SelectedCountry, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(m => m.SelectedCountry, Model.CountryList, "- Please select -", new { @class = "form-control" })
            @Html.ValidationMessageFor(m => m.SelectedCountry, "", new { @class = "text-danger" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.SelectedTour, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(m => m.SelectedTour, Model.TourList, "- Please select -", new { @class = "form-control" })
            @Html.ValidationMessageFor(m => m.SelectedTour, "", new { @class = "text-danger" })
        </div>
    </div>
    ....
}

and the script should be

var url = '@Url.Action("GetT")'; // assumes its in the same controller
var tours = $('#SelectedTour');

$('#SelectedCountry').change(function() {
    tours.empty();
    $.post(url , { id: $(this).val() }, function(data) {
        if (!data) {
            return;
        }
        tours.append($('<option></option>').val('').text('- Please select -'));
        $.each(data, function(index, item) {
            tours.append($('<option></option>').val(item.Id).text(item.TourName));
        });
    });
})
Community
  • 1
  • 1
  • really really thank you for this complete help. I don't know how to appreciate.just thanks for ever – neda Derakhshesh Sep 25 '16 at 19:40
  • Excuse me I know I shouldn't continue on this. my problem solved completely but I had to change it a little. as initializing has an Error I did something like this . before sending my model to configureviewmodel initialize it like this `model.StartDate = Convert.ToDateTime("2015-03-25"); model.EndDate = Convert.ToDateTime("2015-03-25");` first I need to know if its correct way? and then I have some problem in its format which saved in database – neda Derakhshesh Oct 10 '16 at 11:50
  • 1
    Why are you needing to use `Convert.ToDateTime()`? You should not need to do that (the properties should already be typeof `DateTime`) –  Oct 10 '16 at 11:56
  • because I needed to set a default value for `StartDate`and `EndDate ` before sending it to configure view model and also I didn't know how to set a default value for a `datetime`property. you helped me in a way which by using `?` it should accept null but when I write this question mark it has an error that it shouldn't be null and also another error which both solved after initializing like this – neda Derakhshesh Oct 10 '16 at 12:09
  • 1
    Sorry, still not clear what the issue is. When you first pass the model to the view and you want to set some defaults, then initialize a new instance of the view model and then `model.StartDate = DateTime.Today;` (for today's date) or `model.StartDate = new DateTime(2015, 3, 15);` for a specific date. Or you could that in a parameterless constructor for the view model. –  Oct 10 '16 at 12:14
  • 1
    The reason for using `DateTime?` in a view model with a `[Required]` attribute is to protect against under-posting attacks. –  Oct 10 '16 at 12:16
  • Thank you got it. and still I really don't know how to thank you. you did a lot for me. I know I shouldn't thanks too much but I can't :) Best Regards – neda Derakhshesh Oct 10 '16 at 12:43