I want to pass to RedirectToAction model with List type property
For example, I have this simple model:
public class OrgToChooseFrom
{
public string OrgId { get; set; }
public string FullName { get; set; }
}
And complex model as this:
public class SelectCounteragentViewModel
{
public List<OrgToChooseFrom> Counteragents { get; set; }
public OrgToChooseFrom SelectedOrg { get; set; }
}
When I pass simple model with RedirectToAction every value is in place
[HttpGet]
public IActionResult ConfirmChoice(OrgToChooseFrom vm)
{
return View(vm);
}
But when I try to pass complex model SelectCounteragentViewModel, there are empty list and null for the "SelectedOrg" field
[HttpGet]
public IActionResult SelectFromCAOrganizations(SelectCounteragentViewModel vm)
{
return View(vm);
}
How can I do it?
RedirectToAction cannot pass complex model.You can try to use TempData as
Kiran Joshi said.Here is a demo:
public IActionResult Test()
{
SelectCounteragentViewModel vm = new SelectCounteragentViewModel { Counteragents = new List<OrgToChooseFrom> { new OrgToChooseFrom { OrgId ="1", FullName = "d" } }, SelectedOrg = new OrgToChooseFrom { OrgId = "1", FullName = "d" } };
TempData["vm"] = JsonConvert.SerializeObject(vm);
return RedirectToAction("SelectFromCAOrganizations", "ControllerName");
}
[HttpGet]
public IActionResult SelectFromCAOrganizations()
{
SelectCounteragentViewModel vm = JsonConvert.DeserializeObject<SelectCounteragentViewModel>(TempData["vm"].ToString());
return View(vm);
}
Related
I am doing Web project with MVC 5 . I need pass to some data to layout page (data as Category_id or Category_Name).
I read some answers that say I need to make View Model , but my project must be in MVC and not in MVVM,
Do you any ideas?
Thanks!
you have to create a base view model that you will have to use for ALL your views
using Microsoft.AspNetCore.Mvc.Rendering;
public interface IBaseViewModel
{
public int CategoryId { get; set; }
public List<SelectListItem> CategoryList { get; set; }
}
public class BaseViewModel : IBaseViewModel
{
public int CategoryId { get; set; }
public List<SelectListItem> CategoryList { get; set; }
}
action
public IActionResult Index()
{
var baseViewModel=new BaseViewModel();
InitBaseViewModel(baseViewModel);
return View(baseViewModel);
}
private void InitBaseViewModel(IBaseViewModel baseViewModel)
{
//this is for test
// in the real code you can use context.Categories.Select ....
var items = new List<SelectListItem> {
new SelectListItem {Text = "Category1", Value = "1"},
new SelectListItem {Text = "Category2", Value = "2"},
new SelectListItem {Text = "Category3", Value = "3"}
};
baseViewModel.CategoryList= items;
}
layout
#model IBaseViewModel // you can omit it but I like to have it explicitly
#if(Model!=null && Model.CategoryList!=null && Model.CategoryList.Count > 0)
{
<select class="form-control" style="width:450px" asp-for="CategoryId" asp-items="CategoryList">
}
for another view you can create this action code
public IActionResult MyAction()
var myActionViewModel= new MyActionViewModel {
..... your init code
}
InitBaseViewModel(myActionViewModel);
return View(myActionViewModel)
}
public class MyActionViewModel : BaseViewModel
//or
public class MyActionViewModel : IBaseViewModel
{
public .... {get; set;}
}
You can pass directly a obj to View if you want in this way:
public virtual async Task<IActionResult> Index()
{
var model = await MethodThatRedurnModel();
return View(model);
}
I'm having a trouble with my project (ASP.NET MVC 5/AJAX/BOOTSTRAP).
When click on Save button on Page, .Net calls in POST the proper action, but the Hidden Fields for PSATOKEN does not contain value (see #Html.HiddenFor(m => m.PSAToken) in the View), despite PSAToken contains a GUID value (saw in Debug Mode) in the Controller method.
Let's see some code below.
Many thanks to answerers!
Model
public interface IPSAPageViewModel
{
String PSAToken { get; set; }
int IdPSAAzienda { get; set; }
}
public abstract class BasePSAPageViewModel : IPSAPageViewModel
{
public String PSAToken { get; set; }
public int IdPSAAzienda { get; set; }
}
public class DatiGeneraliViewModel : BasePSAPageViewModel
{
public DatiGeneraliViewModel()
{
this.Item = new InformazioniGenerali();
}
public Crea.PSA.ServiceLayer.BO.InformazioniGenerali Item { get; set; }
public List<SelectListItem> FormeGiuridicheList { set; get; }
public List<SelectListItem> FormeConduzioneList { set; get; }
}
Controller
private ViewResult ViewPSAPage(IPSAPageViewModel vm)
{
base.createViewBagPaginePrecSucc();
return View(vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
[HttpParamAction]
public ActionResult SalvaDatiGeneraliProsegui(DatiGeneraliViewModel vm)
{
return salvataggioDatiGenerali(vm, true);
}
[HttpPost]
[ValidateAntiForgeryToken]
[HttpParamAction]
public ActionResult SalvaDatiGenerali(DatiGeneraliViewModel vm)
{
//Here vm.PSAToken doesn't contain the value setted
return salvataggioDatiGenerali(vm);
}
private ActionResult salvataggioDatiGenerali(DatiGeneraliViewModel vm, bool proseguiCompilazione = false)
{
if (ModelState.IsValid)
{
var resp = aziendeManager.Save(vm.PSAToken, vm.Item, SessionManager.UserIdConnected, CONTROLLERNAME);
if (resp.Success)
{
var psaAzienda = resp.DataObject;
setVarsInSession(psaAzienda.idToken.ToString(), psaAzienda.idPsaAzienda.ToString(), psaAzienda.Aziende.ragioneSociale);
//Here there is some Value (POST)
vm.PSAToken = psaAzienda.idToken.ToString();
//vm.IdPSAAzienda = psaAzienda.idPsaAzienda.ToString();
if (proseguiCompilazione)
return RedirectToAction("DatiAziendaliRiepilogativi", new { id = psaAzienda.idToken });
}
else
ModelState.AddModelError("", resp.Message);
}
setSuccessMessage();
vm.FormeGiuridicheList = aziendeManager.GetAllFormeGiuridiche().ToSelectItems();
vm.FormeConduzioneList = aziendeManager.GetAllFormeConduzione().ToSelectItems();
return ViewPSAPage(vm);
}
View
to see the view click here
Here you can see the value at debug in VS
But in the generated HTML the Hidden Field of PSATOKEN is empty
I found the solution here:
patrickdesjardins.com/blog/… .
Consider the following
MODEL
public partial class ElementType
{
public long ElementTypeId { get; set; }
public LocalizedString TypeName { get; set; }
}
[ComplexType]
public class LocalizedString
{
public string French { get; set; }
public string English { get; set; }
[NotMapped]
public string Current
{
get { return (string) LanguageProperty().GetValue(this,null); }
set { LanguageProperty().SetValue(this, value,null); }
}
public override string ToString()
{
return Current;
}
private PropertyInfo LanguageProperty()
{
string currentLanguage = Thread.CurrentThread.CurrentUICulture.DisplayName;
return GetType().GetProperty(currentLanguage);
}
}
CONTROLLER
public ActionResult ElementType_Read([DataSourceRequest]DataSourceRequest request)
{
List<ElementType> elementTypeList = db.ElementType.ToList();
IQueryable<ElementType> elementTypes = elementTypeList.AsQueryable();
DataSourceResult result = elementTypes.ToDataSourceResult(request, elementType => new
{
ElementTypeId = elementType.ElementTypeId,
TypeName = elementType.TypeName,
});
return Json(result);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ElementType_Destroy([DataSourceRequest]DataSourceRequest request, ElementType elementType)
{
if (ModelState.IsValid)
{
var currentElementType = db.ElementType.Find(elementType.ElementTypeId);
db.ElementType.Attach(currentElementType);
db.ElementType.Remove(currentElementType);
db.SaveChanges();
}
return Json(new[] { elementType }.ToDataSourceResult(request, ModelState));
}
Read action is working as expected. I'm getting the current TypeName in the Grid view by TypeName.Current
But if I try to execute any other actions, like Destroy or Update I'm getting this exception Can not convert an object of type 'Iolite.Models.LocalizedString' to type 'System.String'.
Any suggestion to fix it?
Regards
I suggest you to use DTOs (Data Transfer Object) to flat your model into easier to serialize/deserialize objects. You will also reduce the amount of data that will be transferred between your server & clients within every request.
For your particular implementation you might define a DTO as follow:
public class ElementTypeDTO
{
public long ElementTypeId { get; set; }
public string TypeName { get; set; }
}
According to this architectural pattern you have to change your actions as follow:
public ActionResult ElementType_Read([DataSourceRequest]DataSourceRequest request)
{
List<ElementType> elementTypeList = db.ElementType.ToList();
IQueryable<ElementType> elementTypes = elementTypeList.AsQueryable();
DataSourceResult result = elementTypes.ToDataSourceResult(request, elementType => new ElementTypeDTO
{
ElementTypeId = elementType.ElementTypeId,
TypeName = elementType.TypeName.Current,
});
return Json(result);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ElementType_Destroy([DataSourceRequest]DataSourceRequest request, ElementTypeDTO elementType)
{
if (ModelState.IsValid)
{
var currentElementType = db.ElementType.Find(elementType.ElementTypeId);
db.ElementType.Attach(currentElementType);
db.ElementType.Remove(currentElementType);
db.SaveChanges();
}
return Json(new[] { elementType }.ToDataSourceResult(request, ModelState));
}
I have no idea what's the reason, it's so straight and simple, but, curiously, doesn't work. I always recieve NULL as model in controller.
Here is the code:
Model
public class EnrolleePlaces
{
[HiddenInput(DisplayValue=false)]
public int id { get; set; }
public string SpecialtyCode { get; set; }
public string Specialty { get; set; }
public int Places { get; set; }
}
Controller
public ViewResult EditPlaces(int id)
{
return View(repo.EnrolleePlaces.FirstOrDefault(p => p.id == id));
}
[HttpPost]
public ActionResult NewPlaces(EnrolleePlaces places) // 'places' is ALWAYS null
{
if (ModelState.IsValid)
{
repo.SaveEnrolleePlaces(places);
return RedirectToAction("Settings");
}
return View("EditPlaces", places);
}
public ViewResult CreatePlaces()
{
return View("EditPlaces", new EnrolleePlaces());
}
And a view
#model Domain.Entities.EnrolleePlaces
#{
Layout = null;
}
Edit: #Model.Specialty
#using (Ajax.BeginForm("NewPlaces", "Enrollee", new { area = "Admin" },
new AjaxOptions() { UpdateTargetId = "AdminContent" } ,
new { enctype = "multipart/form-data" }))
{
#Html.EditorForModel()
// here is input type="submit"
}
I have over 15 controllers in my project, made by the same pattern, but only this one is strange
have you tried changing the name of the parameter that your action method receives ?
for example:
[HttpPost]
public ActionResult NewPlaces(EnrolleePlaces dd) // any name other than "places"
{
if (ModelState.IsValid)
{
repo.SaveEnrolleePlaces(places);
return RedirectToAction("Settings");
}
return View("EditPlaces", places);
}
I would like to get into the habit of using ViewModels.
In the past I have only used them in my Create Actions and I never figured how to use them in Edit Actions. I used Domain Entities instead.
Let's say I have the following:
Using Entity Framework Code First
POCO class in Domain project
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonId { get; set; }
public string Name { get; set; }
public string Website { get; set; }
public DateTime? Created { get; set; }
public DateTime? Updated { get; set; }
}
In my Data Project
Abstract Folder:
public interface IPersonRepository
{
IQueryable<Person> People{ get; }
void SavePerson(Person person);
}
Concrete Folder:
EfDb class
public class EfDb : DbContext
{
public EfDb() : base("DefaultConnection") {}
public DbSet<Person> People{ get; set; }
}
EfPersonRepository class
#region Implementation of Person in IPersonRepository
public IQueryable<Person> People
{
get { return _context.People; }
}
public void SavePerson(Persona person)
{
if (person.PersonId == 0)
{
_context.People.Add(person);
}
else if (person.PersonId> 0)
{
var currentPerson = _context.People
.Single(a => a.PersonId== person.PersonId);
_context.Entry(currentPerson).CurrentValues.SetValues(person);
}
_context.SaveChanges();
}
#endregion
PersonCreateViewModel in WebUI Porject ViewModels folder
public class PersonCreateViewModel
{
[Required]
[Display(Name = "Name:")]
public string Name { get; set; }
[Display(Name = "Website:")]
public string Website { get; set; }
}
Person Controller and Create Action:
public class PersonController : Controller
{
private readonly IPersonRepository _dataSource;
public PersonController(IPersonRepository dataSource)
{
_dataSource = dataSource;
}
// GET: /Association/
public ActionResult Index()
{
return View(_dataSource.Associations);
}
// GET: /Person/Details/5
public ActionResult Details(int id)
{
return View();
}
// GET: /Person/Create
[HttpGet]
public ActionResult Create()
{
return View();
}
// POST: /Person/Create
[HttpPost]
public ActionResult Create(PersonCreateViewModel model)
{
if (ModelState.IsValid)
{
try
{
var Person = new Person
{
Name = Model.Name,
Website = model.Website,
Created = DateTime.UtcNow,
Updated = DateTime.UtcNow
};
_dataSource.SavePerson(person);
return RedirectToAction("Index", "Home");
}
catch
{
ModelState.AddModelError("", "Unable to save changes. ");
}
}
return View(model);
}
}
Now unless I am mistaken, I expect my PersonEditViewlModel to look exactly like my PersonCreateViewlModel. But I can't figure out how to use that in my Edit action, provided I also have to call SavePerson(Person person) like I did in my Create action.
Note: Please no suggestions of AutoMapper or ValueInjecter.
How is this done?
It'll be just like create except you need the record Id.
[HttpGet]
public ActionResult Edit(int id)
{
var personVm = _dataSource.People.Single(p => p.PersonId == id)
.Select(e => new PersonEditViewModel {
e.PersonId = p.PersonId,
e.Name = p.Name,
e.Website = p.Website
...
});
return View(personVm);
}
[HttpPost]
public ActionResult Edit(PersonEditViewModel model)
{
if (ModelState.IsValid)
{
var person = _dataSource.People.Single(p => p.PersonId == model.PersonId);
person.Name = model.Name;
person.Website = model.Website;
...
_dataSource.EditPerson(person);
return RedirectToAction("Index", "Home");
}
return View(model);
}
Edit:
So you don't do another query on edits
public void EditPerson(Person person)
{
_context.Entry(person).State = EntityState.Modified;
_context.SaveChanges();
}