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/… .
Related
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);
}
My models has some fields that are not to be presented in views (like Id field).
So, when I post the form, these fields return with "null" value, unless I insert then as hidden fields in form.
There are another away to update a model, using only the fields in form ?
My actual code:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Profissao model)
{
if (ModelState.IsValid)
{
using (var escopo = Db.Database.BeginTransaction())
{
try
{
if (model.Id == 0)
Db.Profissoes.Add(model);
else
Db.Profissoes.Update(model);
Db.SaveChanges();
escopo.Commit();
return RedirectToAction("Index");
}
catch (Exception)
{
escopo.Rollback();
}
}
}
return View(model);
}
You should use Dto's (Data transfer objects) to handle this.
public class User
{
public string Name { get; set; }
public string Passord { get; set; }
public string Email { get; set; }
}
public class UserDto
{
public string Name { get; set; }
public string Passord { get; set; }
public string Email { get; set; }
public UserDto FromModel(User user)
{
Name = user.Name;
Passord = user.Passord;
Email = user.Email;
return this;
}
public User UpdataModel(User user)
{
user.Name = Name;
user.Email = Email;
return user;
}
}
then you can pass around the Dto object to your view and in your post.
your post controller should look somthing like
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ProfissaoDto model)
{
if (ModelState.IsValid)
{
using (var escopo = Db.Database.BeginTransaction())
{
try
{
if (model.Id == 0)
Db.Profissoes.Add(ProfissaoDto.UpdateModel(new Profissao()));
else
var model = Db.Profissao.find(Model.id);
Db.Profissoes.Update(ProfissaoDto.UpdateModel(model));
escopo.Commit();
return RedirectToAction("Index");
}
catch (Exception)
{
escopo.Rollback();
}
}
}
return View(model);
}
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();
}
I am building a web application in ASP.NET MVC. I have a comment page where comments are displayed in a list with the latest to oldest and also have a form at the bottom where a user can post new comments.
Form entries should also be highlighted in addition to having the page displaying the latest comments.
Whats the best way to do this, where displayed data and a post form are on the same page?
Is it possible to do this without ajax as well ?
--Code extract--
class CommentsViewModel
{
public IList<Comment> comments { get; set; }
public Comment comment { get; set; }
public SelectList commentCategories { get; set; }
}
class Comment
{
[Required]
public string commentData { get; set; }
[Required]
public int? commentCategory { get; set; }
}
class Comments : Controller
{
public ActionResult Index()
{
Site db = new Site();
CommentsViewModel commenstVm = new
{
comments = db.GetComments(),
comment = new Comment(),
commentCategories = db.GetCommentCategories()
};
return View(commentsVm);
}
[HttpPost]
public ActionResult AddNewComment(CommentsViewModel commentVm)
{
Site db = new Site();
if (!ModelState.IsValid)
{
return View("Index", commentVm);
}
db.AddComment(commentVm.comment);
return RedirectToAction("Index");
}
}
Here's a basic View and the Controller that you can use as a starting point.
Model and ViewModel:
public class CommentsViewModel
{
public IList<Comment> comments { get; set; }
public CommentsViewModel()
{
comments = new List<Comment>();
}
}
public class Comment
{
[Required]
public string commentData { get; set; }
/** Omitted other properties for simplicity */
}
View:
#using (#Html.BeginForm("Index", "Comments"))
{
#Html.TextBoxFor(t => t.comment.commentData)
#Html.ValidationMessageFor(t=> t.comment.commentData, "", new {#class = "red"})
<button name="button" value="addcomment">Add Comment</button>
}
#foreach (var t in Model.comments)
{
<div>#t.commentData</div>
}
Controller:
public class CommentsController : Controller
{
/** I'm using static to persist data for testing only. */
private static CommentsViewModel _viewModel;
public ActionResult Index()
{
_viewModel = new CommentsViewModel();
return View(_viewModel);
}
[HttpPost]
public ActionResult Index(Comment comment)
{
if (ModelState.IsValid)
{
_viewModel.comments.Add(
new Comment() {commentData = comment.commentData});
return View("Index", _viewModel);
}
return RedirectToAction("Index");
}
}