MVC 3 Ajax.ActionLink Pass Parent Model Property - ajax

I have a view which displays a model eg:
public class MyModel()
{
public string name {get;set;}
public IList<Note> notes {get;set;}
}
The view displays all the notes for the model, i am trying to use Ajax.ActionLink to delete a note, but in order to delete a note i need to pass my controller action result the ID of the model.
public ActionResult DeleteNote(int modelId, int noteId)
{
var franchise = _franchiseRepository.FindById(modelId);
Note note = new Note(noteId);
franchise.RemoveNote(note);
_franchiseRepository.SaveOrUpdate(franchise);
return View();
}
Ajax.ActionLink("Delete", "DeleteNote", new {id=item.id}, new AjaxOptions{HttpMethod="POST"})
Can this be accomplished with ajax.actionlink?
Thanks in advance

Your DeleteNote action expects two parameters. I think it should work if you change the ActionLink to:
Ajax.ActionLink("Delete", "DeleteNote", new { modelId = [modelId], noteId = [noteId] }, new AjaxOptions { HttpMethod = "POST" })

Related

mvc dropdownlist not showing data value

I'm having an issue displaying data from the database into drop-downlist.
controller
TowinsEntities db = new TowinsEntities();
public ActionResult TMakes()
{
//T_Make make_db = new Models.T_Make();
ViewBag.carMaker = new SelectList(db.T_Make, "Make");
return View();
}
view
#Html.DropDownList("carMaker", "Select Make")
model
public partial class T_Make
{
public string Make { get; set; }
}
The output of a view is:
You need to overload your DropDownList with the string field names you want for value/display. You're only passing the model and selected value. I guess you'd use Make for both value and display (though, most people would use an ID for a value)
ViewBag.carMaker = new SelectList(db.T_Make, "Make","Make");
https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.selectlist.-ctor?view=aspnet-mvc-5.2#System_Web_Mvc_SelectList__ctor_System_Collections_IEnumerable_System_String_System_String_

Single property not getting bound on HttpPost

I'm working on the first MVC3 project at our company, and I've hit a block. No one can seem to figure out what's going on.
I have a complex Model that I'm using on the page:
public class SpaceModels : List<SpaceModel> {
public bool HideValidation { get; set; }
[Required(ErrorMessage=Utilities.EffectiveDate + Utilities.NotBlank)]
public DateTime EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
}
In the Controller, I create a SpaceModels object with blank SpaceModels for when Spaces get combined (this would be the destination Space).
// Need a list of the models for the View.
SpaceModels models = new SpaceModels();
models.EffectiveDate = DateTime.Now.Date;
models.DisplayEffectiveDate = true;
models.Add(new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
return View("CombineSpaces", models);
Then in the View, I am using that SpaceModels object as the Model, and in the form making a TextBox for the Effective Date:
#model Data.SpaceModels
#using (Html.BeginForm("CombineSpaces", "Space")) {
<div class="EditLine">
<span class="EditLabel LongText">
New Space Open Date
</span>
#Html.TextBoxFor(m => m.EffectiveDate, new {
size = "20",
#class = "datecontrol",
// Make this as a nullable DateTime for Display purposes so we don't start the Calendar at 1/1/0000.
#Value = Utilities.ToStringOrDefault(Model.EffectiveDate == DateTime.MinValue ? null : (DateTime?)Model.EffectiveDate, "MM/dd/yyyy", string.Empty)
})
#Html.ValidationMessageFor(m => m.EffectiveDate)
</div>
<hr />
Html.RenderPartial("_SpaceEntry", Model);
}
The Partial View that gets rendered iterates through all SpaceModels, and creates a containing the Edit fields for the individual SpaceModel objects. (I'm using the List to use the same Views for when the Spaces get Subdivided as well.)
Then on the HttpPost, the EffectiveDate is still back at it's DateTime.MinValue default:
[HttpPost]
public ActionResult CombineSpaces(SpaceModels model, long siteID, long storeID, DateTime? effectiveDate) {
// processing code
}
I added that DateTime? effectiveDate parameter to prove that the value when it gets changed does in fact come back. I even tried moving the rendering of the TextBox into the _SpaceEntry Partial View, but nothing worked there either.
I did also try using the #Html.EditorFor(m => m.EffectiveDate) in place of the #Html.TextBoxFor(), but that still returned DateTime.MinValue. (My boss doesn't like giving up the control of rendering using the #Html.EditorForModel by the way.)
There has to be something simple that I'm missing. Please let me know if you need anything else.
Looking at the source code for DefaultModelBinder, specifically BindComplexModel(), if it detects a collection type it will bind the individual elements but will not attempt to bind properties of the list object itself.
What model binding does is attempt to match the names of things or elements in the view to properties in your model or parameters in your action method. You do not have to pass all of those parameters, all you have to do is add them to your view model, then call TryUpdateModel in your action method. I am not sure what you are trying to do with SpaceModel or List but I do not see the need to inherit from the List. Im sure you have a good reason for doing it. Here is how I would do it.
The view model
public class SpacesViewModel
{
public DateTime? EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
public List<SpaceModel> SpaceModels { get; set; }
}
The GET action method
[ActionName("_SpaceEntry")]
public PartialViewResult SpaceEntry()
{
var spaceModels = new List<SpaceModel>();
spaceModels.Add(
new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
var spacesVm = new SpacesViewModel
{
EffectiveDate = DateTime.Now,
DisplayEffectiveDate = true,
SpaceModels = spaceModels
};
return PartialView("_SpaceEntry", spacesVm);
}
The POST action method
[HttpPost]
public ActionResult CombineSpaces()
{
var spacesVm = new SpacesViewModel();
// this forces model binding and calls ModelState.IsValid
// and returns true if the model is Valid
if (TryUpdateModel(spacesVm))
{
// process your data here
}
return RedirectToAction("Index", "Home");
}
And the view
<label>Effective date: </label>
#Html.TextBox("EffectiveDate", Model.EffectiveDate.HasValue ?
Model.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.empty,
new { #class = "datecontrol" })
Sometimes you need to explicitly bind form data using hidden fields such as
#Html.HiddenField("EffectiveDate", Model.EfectiveDate.)
In order to bind the properties of the SpaceModel object you can add individual properties such as SiteID to the view model or add a SpaceModel property for a single SpaceModel. If you want to successfully bind a complex model, add it as a Dictionary populated with key-value pairs rather than a List. You should then add the dictionary to the view model. You can even add a dictionary of dictionaries for hierarchical data.
I hope this helps :)

ASP.NET MVC 3 Viewmodel Pattern

I am trying to work out the best way of using a viewmodel in the case of creating a new object.
I have a very simple view model that contains a contact object and a select list of companies.
private ICompanyService _Service;
public SelectList ContactCompanyList { get; private set; }
public Contact contact { get; private set; }
public ContactCompanyViewModel(Contact _Contact)
{
_Service = new CompanyService();
contact = _Contact;
ContactCompanyList = GetCompanyList();
}
private SelectList GetCompanyList()
{
IEnumerable<Company> _CompanyList = _Service.GetAll();
return new SelectList(_CompanyList, "id", "name");
}
I then have contact controller that uses this viewmodel and enable me to select a related company for my contact.
[Authorize]
public ActionResult Create()
{
return View(new ContactCompanyViewModel(new Contact()));
}
My issue is with the create method on the controller.
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Contact _Contact)
{
try
{
_Service.Save(_Contact);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The problem is that the view returns an empty contact object, but! the company id is populated, this is because the dropdown list explicitly declares its field name.
#Html.DropDownList("parent_company_id",Model.ContactCompanyList)
The standard html form fields pass the objects values back in the format of contact.forename when using the HTML.EditorFor helper...
#Html.EditorFor(model => model.contact.forename)
I can access them if I use a FormCollection as my create action method paremeter and then explicitly search for contact.value but I cannot use a Contact object as a parameter to keep my code nice and clean and not have to build a new contact object each time.
I tried passing the actual view model object back as a parameter but that simply blows up with a constructor error (Which is confusing seeing as the view is bound to the view model not the contact object).
Is there a way that I can define the name of the Html.EditFor field so that the value maps correctly back to the contact object when passed back to the create action method on my controller? Or Have I made some FUBAR mistake somewhere (that is the most likely explanation seeing as this is a learning exercise!).
Your view model seems wrong. View models should not reference any services. View models should not reference any domain models. View models should have parameterless constructors so that they could be used as POST action parameters.
So here's a more realistic view model for your scenario:
public class ContactCompanyViewModel
{
public string SelectedCompanyId { get; set; }
public IEnumerable<SelectListItem> CompanyList { get; set; }
... other properties that the view requires
}
and then you could have a GET action that will prepare and populate this view model:
public ActionResult Create()
{
var model = new ContactCompanyViewModel();
model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
{
Value = x.id.ToString(),
Text = x.name
});
return View(model);
}
and a POST action:
[HttpPost]
public ActionResult Create(ContactCompanyViewModel model)
{
try
{
// TODO: to avoid this manual mapping you could use a mapper tool
// such as AutoMapper
var contact = new Contact
{
... map the contact domain model properties from the view model
};
_Service.Save(contact);
return RedirectToAction("Index");
}
catch
{
model.CompanyList = _Service.GetAll().ToList().Select(x => new SelectListItem
{
Value = x.id.ToString(),
Text = x.name
});
return View(model);
}
}
and now in your view you work with your view model:
#model ContactCompanyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedCompanyId, Model.CompanyList)
... other input fields for other properties
<button type="submit">Create</button>
}

assigning variables in the View in ASP MVC 3

Actually I'm very new to ASP.NET MVC and I need your help.
Here I have some Create Method that takes an argument from the URL to use it as id:
in 'vote' controller :
public ActionResult Create(int id)
{
Meeting meeting = db.Meetings.Find(id); // get the object
ViewBag.meetingID = meeting.meetingID; // get its id and assign it to a ViewBag
return View();
}
and I would like to do something like :
vote.meetingID = #ViewBag.meetingID
in the model so that is directly assgin this property without excplicitely typing it from the HTML view (I mean #Html.EditorFor(model=>meetingID) )
The question is not that clear, but try this.
You could pass the entire model to the view.
Controller:
public ActionResult Create(int id)
{
Meeting meeting = db.Meetings.Find(id); // get the object
ViewData["myMeeting"] = meeting;
return View();
}
To use in the view you can declare it as a variable at the top of the view:
Declare:
#
{
var meetingData = ViewData["myMeeting"] as Meeting;
}
Usage:
<div>
#meetingData.meetingID
</div>

When using DropDownListFor how do I bind the SelectList to the Model

This page works in two steps,
Step 1 - The user hits Index() and the SelectList is populated with the applications from the databse.
Step 2 - they select an applicaiton from the list, which posts the page back, which reloads the page with the application Details added
Error: When I run this and get to step 2, I get an error back saying:
The ViewData item that has the key 'ApplicationId' is of type 'System.Int32' but must be of type 'IEnumerable'.
This appears to be because the Model.ApplicationList is now null as it hasn't bound back to the model when the form was posted, can I make it do this?
View:
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.ApplicationId, Model.ApplicationList, "Select an Application" , new { #onchange = "this.form.submit();" })
}
Model:
public class IndexModel
{
public int ApplicationId { get; set; }
public List<SelectListItem> ApplicationList { get; set; }
public string Detail { get; set}
}
Controller:
public ActionResult Index()
{
using (var dc = new Entities())
{
var model = new IndexModel();
model.ApplicationList = new List<SelectListItem>();
var applications = dc.Applications.OrderBy(a => a.Name).ToList();
foreach (var application in applications)
{
model.ApplicationList.Add(new SelectListItem
{
Selected = false,
Text = application.Name,
Value = application.Id.ToString()
});
}
model.ApplicationId = 1;
return View(model);
}
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
model.Detail = GetDetail(model.ApplicationId);
return View(model);
}
I was struggling with the same problem. It doesn't look like .net mvc3 lets you do this without the help of jquery. Drop down lists will get their selected item bound to the model when posting but not all the items in the combo box. You would have to rebuild it each time you pass the viewmodel back to the view.
Another way around losing the dropdown list is to use ajax.

Resources