I send a BOOKVIEWMODEL with fields and a simple IEnumerable in view I get the this list IEnumerable in the view by a method with JSON AJAX in view and I fill my table Ristourne(View) with JQUERY it works very well but I not know how I fill (BIND or refresh) the list IEnumerable of my BOOKVIEWMODEL in the VIEW to recovered it in the Controller:
[HttpPost]
public ActionResult Create(BookViewModel _bookViewModel)
{
if (ModelState.IsValid)
{
_bookViewModel.Ristourne
return RedirectToAction("Index");
}
return View(_bookViewModel);
my bookviewmodel
public class BookViewModel
{
public String book { get; set; }
public String price { get; set; }
public IEnumerable<Ristourne> Ristourne { get; set; }
}
For the model binding to work, you need to "mimic" the convention MVC uses when generating the form fields.
I don't know the contents of the Ristourne class, but let's say it had 1 field called Foo.
In that case, when you render out the elements (from the JSON AJAX callback), make them look like this:
<input type="text" name="Model.Ristourne[0].Foo" id="Model.Ristourne[0].Foo"/>
<input type="text" name="Model.Ristourne[1].Foo" id="Model.Ristourne[1].Foo"/>
<input type="text" name="Model.Ristourne[2].Foo" id="Model.Ristourne[2].Foo"/>
And so on. Easiest thing to do is in your AJAX callback, just use a basic for loop to create the elements indexer.
Altenatively, a cheat/easy way around this problem would be to make your AJAX action return a PartialViewResult:
public PartialViewResult Book()
{
var ristournes = _repo.Get();
var model = new BooksViewModel { Ristourne = ristournes };
return PartialView(model);
}
Then the partial view:
#Html.EditorFor(model => mode.Ristourne)
Then MVC will create the form fields correctly.
I always prefer this option over dynamically generated form fields. If you want to go down this path often, you should consider something like Knockout.js and/or Upshot.
Related
I'm working on an application on .NET Core MVC and I'd like to have the name of the navigated property "Hobbies" instead of the ID in my create view
I have this:
And I would like this, intead:
That I manage to get with that code for the create action in the controller:
public IActionResult Create()
{
ViewData["HobbiesName"] = new SelectList(_context.Set<Hobbies>(), "HobbiesName", "HobbiesName");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("PersonID,PersonName,PersonSurname,HobbiesID")] Person person)
{
if (ModelState.IsValid)
{
_context.Add(person);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["HobbieName"] = new SelectList(_context.Set<Hobbies>(), "HobbiesID", "HobbiesName", person.HobbiesID);
return View(person);
}
And in the view :
<div class="form-group">
<label asp-for="Hobbies.HobbiesName" class="control-label"></label>
<select asp-for="Hobbies.HobbiesName" class ="form-control" asp-items="ViewBag.HobbiesName"></select>
</div>
But that doesn't work because, I need the ID.
Does anybody know how I can manage that?
The following worked for me:
<select asp-for="Name" asp-items="#Model.NameList">
In my view model:
public string Name { get; set; }
public SelectList NameList { get; set; }
It is better to work with a view model and avoid ViewBag.
It is essential to have two different fields: the list of items (with their ids already there, behind the scenes), and a field representing the text (not the id) of the item selected
I have created a very simple view in my MVC3 project that contains a textbox that receives and validates a URL. The controller class is rather simple:
[HttpPost]
public ActionResult Save(ValidationModel model)
{
if (ModelState.IsValid)
{
//Save or whatever
}
return View(model);
}
I'm needing some guidance on how to retrieve the URL entered into the textbox, and subseuquently scan the resulting page for hyperlinks or tags. Once those tags are scanned, I need to return a new view to my user with a list or grid of the tags in alpha order.
Can anyone point me in the correct direction on above steps?
Thanks:)
In your view model you will have a property:
public class ValidationModel
{
[Required]
public string Url { get; set; }
}
and then you will have a corresponding textbox in the view:
#model ValidationModel
#using (Html.BeginForm)
{
#Html.EditorFor(x => x.Url)
<button type="submit">OK</submit>
}
and finally in your POST controller action:
[HttpPost]
public ActionResult Save(ValidationModel model)
{
if (ModelState.IsValid)
{
//Save or whatever
// use model.Url here => it will contain the user input
}
return View(model);
}
Try this:
in your view where your using your model inside your FORM:
#Html.TextBoxFor(m => m.MyHyperLink)
and in your controller you do this:
model.MyHyperLink you can manipulate the string or do what ever you want
easy as that..
hope i helped.
Please help with such a question and do not judge strictly because I'm a newbie in MVC:
I've got a model for storing names of users by ID in my DB
public class Names
{
public int NameId { get; set; }
public string Username { get; set; }
}
,
a conrtoller
[HttpPost]
public ActionResult EditforModel(Names Name)
{
if (ModelState.IsValid)
{
db.Entry(Name).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(Name);
}
adding and editing view
adding is working well, the question is about editing
I use
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend> legend </legend>
#Html.EditorForModel()
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
to edit my model.
when trying to go to this view I see an editor for both Id and Username, but if i fill Id - I've got error, because there is no Entry in DB with such Id.
Ok.Let's look for attributes to hide an editor.
[ScaffoldColumn(false)] is something like a marker whether to render an editor for Id or not.
applaying it to my model I've got "0" id posting from my View.Try another attr.
[ReadOnly(true)] makes a field a readonly-field. But at the same time I've got "0" in posting Id.
Modifying a view I placed an editors for each field in model
#Html.HiddenFor(model => model.NameId)
#Html.EditorFor(model => model.Username)
but using it is dangerous because some user can post wrong Id throgh post-request.
I can't use [ScaffoldColumn(false)] with applying Id at [Httppost] action of the controller,by searching appropriate user-entry in DB, because the name was changed..
I can't believe #Html.HiddenFor is the only way out.But can't find one :(
As you mentioned "[ScaffoldColumn(false)] is something like a marker whether to render an editor for Id or not", and [ReadOnly(true)] means that this property will be excluded by the default model binder when binding your model.
The problem is that the HTTP protocol is a stateless protocol, which means that when the user posts the edit form to the MVC Controller, this controller has no clue which object he was editing, unless you include some identifier to your object in the request received from the user, though including the real object Id isn't a good idea for the reason you mentioned (that someone could post another Id).
A possible solution might be sending a View Model with an encrypted Id to the View, and decrypting this Id in the controller.
A View Model for your object might look like this :
public class UserViewModel
{
[HiddenInput(DisplayValue = false)]
public string EncryptedId { get; set; }
public string Username { get; set; }
}
So your HttpGet action method will be
[HttpGet]
public ActionResult EditforModel()
{
// fetching the real object "user"
...
var userView = new UserViewModel
{
// passing the encrypted Id to the ViewModel object
EncryptedId = new SimpleAES().EncryptToString(user.NameId.ToString()),
Username = user.Username
};
// passing the ViewModel object to the View
return View(userView);
}
Don't forget to change the model for your View to be the ViewModel
#model UserViewModel
Now the HttpPost action method will be receiving a UserViewModel
[HttpPost]
public ActionResult EditforModel(UserViewModel Name)
{
if (ModelState.IsValid)
{
try
{
var strId = new SimpleAES().DecryptString(Name.EncryptedId);
var id = int.Parse(strId);
// select the real object using the decrypted Id
var user = ...Single(p => p.NameId == id);
// update the value from the ViewModel
user.Username = Name.Username;
db.Entry(user).State = EntityState.Modified;
}
catch (CryptographicException)
{
// handle the case where the encrypted key has been changed
return View("Error");
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(Name);
}
When the user tries to change the encrypted key, the decryption will fail throwing a CryptographicException where you can handle it in the catch block.
You can find the SimpleAES encryption class here (don't forget to fix the values of Key and Vector arrays):
Simple insecure two-way "obfuscation" for C#
PS:
This answer is based on the following answer by Henry Mori:
Asp.net MVC 3 Encrypt Hidden Values
I have a model which contains a list of another model.
Let's say I have a MovieModel:
public class MovieModel
{
public int MovieId { get; set; }
public string Title { get; set; }
public string Director { get; set; }
}
Then I have the RentalModel:
public class RentalModel
{
public int RentalId { get; set; }
public string CustomerId { get; set; }
public List<MovieModel> Movies { get; set; }
}
Then I have a place where all the rentals are displayed, which by clicking on the rental, its details will be displayed, from the "ShowRentals.aspx" to "ShowRentalDetails.aspx"
<% using (Html.BeginForm()) { %>
<% foreach(var rent in Model) { %>
<div class="editor-label">
<div class="editor-field">
<%: rent.RentalId %>
<%: Html.ActionLink("Details", "ShowRentalDetails",
new {rentalId = rent.RentalId,
customerId = rent.CustomerId,
movies = rent.Movies,
})%>
When I debug, I see that the Movies list is always null. This is because only primitive parameters are passed successfully, such as the Ids. I was never able to pass complex types. I really need this list to be passed on to the controller. Is it maybe because the actionlink is not capable? What other work-arounds can I do? I've been stuck on this for a while.
Nevermind the bare code here, this is just to show you what I'm doing with the list. Please help.
(follow up)
In the Controller, here's the two actions, ShowRentals and ShowRentalDetails:
public ActionResult ShowRentals()
{
MembershipUser user = Membership.GetUser(User.Identity.Name, true);
Guid guid = (Guid)user.ProviderUserKey;
Entities dataContext = new Entities();
Member member = dataContext.Members.Where(m => m.UserID == guid).First();
IEnumerable<RentalModel> toReturn = from r in member.Rentals
select new RentalModel
{
RentalId = m.RentalID,
CustomerId = m.CustomerID,
};
return View(toReturn);
}
[Authorize]
public ActionResult ShowRentalDetails(RentalModel model, List<MovieModel> movies)
{
return View("ShowRentalDetails", model);
}
I can't set it in ShowRentals because the array of movies in the database is of Movie type and not MovieModel, so the two lists are not compatible. It is null in the model when passed from ShowRentals view and the model is reconstructed by mvc, and it also doesn't work when explicitly passed from the actionlink as a parameter. help!
I believe Html.ActionLink performs a GET and you can't pass complex data types using a GET.
If you could refetch the movie list in your ShowRentDetails controller by using the rental id I think that would be best.
Otherwise, you could look up EditorFor templates. If you make an editorfor template for MovieModel and post a RentalModel to ShowRentDetails then you could get the MovieModel list that way.
See http://weblogs.asp.net/shijuvarghese/archive/2010/03/06/persisting-model-state-in-asp-net-mvc-using-html-serialize.aspx for another way.
On a side note, theres no need to make
List<MovieModel> movies
a second parameter in ShowRentDetails when it's already included in the model
Source: ASP.NET MVC - Trouble passing model in Html.ActionLink routeValues
It is clear you cant pass complex view models through action link. There is a possibility to pass simple objects which does not have any complex properties. There is another way you can do as multiple submit buttons and do a post to controller. Through the submit you have possibilities to post complex view models
I have a view model that contains an object containing a list and I'm outputting this list using a WebGrid.
The page can currently postback to multiple Actions which edit/add to this list. I'd like to persist this data across all postbacks so all Actions can act upon any changes past Actions have performed on the list. Is there anyway I can get the webgrid to bind to my model in the controller? Or am I required the list using another method like session/database?
Here's my code to help clear up what I mean.
I have a view model looking like this:
public class EggBasketViewModel {
public Basket EggBasket { get; set; } // Contains list of eggs & other basket info
public Egg EggToAdd { get; set; }
}
and I'm outputting it like this:
#model Basket.Models.EggBasketViewModel
#{
var grid = new WebGrid(Model.Eggs); // Eggs is my list of eggs
}
#using (Html.BeginForm()) {
#Html.Partial("_CreateOrEditEggBasket")
#Html.Partial("_CreateOrEditEgg")
#grid.GetHtml()
<p>
<button name="button" value="submitEgg">Add Egg</button>
<button name="button" value="submitBasket">Submit Basket</button>
</p>
}
With my controller looking like so:
[HttpPost]
public ActionResult Create(EggBasketViewModel Model, string button)
{
if (ModelState.IsValid) {
switch (button) {
case "submitEgg":
return this.submitEgg(Model);
case "submitBasket":
return this.submitBasket(Model);
default:
break;
}
} else {
return View();
}
}
public ActionResult SubmitBasket(EggBasketViewModel Model) {
eggBasketRepository.InsertOrUpdate(Model.EggBasket);
eggBasketRepository.Save();
return RedirectToAction("Index");
}
public ActionResult SubmitEgg(EggBasketViewModel Model) {
Model.EggBasket.Eggs.Add(Model.EggToAdd);
return View(Model);
}
I created it this way thinking the WebGrid would output data in a way the model binder would be able to bind it to my view model on POST and so persist any new Eggs using the html output by the WebGrid. Is there any way the WebGrid could output the list in a way compatible with the model binder? Or am I better off using a different method?
Thanks.