View
#using (Html.BeginForm("UpdateClient", "Client")) {
Controller
[HttpPost]
public ActionResult UpdateClient(Client client)
{
if (ModelState.IsValid) {
bool ret = _clientRepository.UpdateClient(client);
return RedirectToAction("Index", "Home");
}
return View(client);
}
Repository
public bool UpdateClient(Client client)
{
using (var context = new Entities()) {
context.AttachTo("Clients",client);
context.ObjectStateManager.ChangeObjectState(client, EntityState.Modified);
context.SaveChanges();
}
return true;
}
When I call the UpdateClient in the controller, the client ID is 0. How do I pass the ID I have updated?
When you call your UpdateClient method from the client-side code (i.e, the browser), you need to make sure that the fields you want set are posted back to the receiving page.
For example, I often use jQuery and a simple ajax call to post data back to the server. The post needs to contain matching values for the Client object:
$.post("/controller/UpdateClient", {
"clientId": 1,
"otherProp1": "some value",
"otherProp2": "some value 2"
}, function (data) {
}
};
In your case, you probably need to make sure you've got an input field that contains a field named after ClientId so that it gets posted to the server that way:
<input type="hidden" id="clientId" name="clientId" value="1" />
Additionally, you may find it useful to installer Fiddler2 and use it to watch the data actually being posted to the server. It's invaluable when dealing with this type of thing.
There is not much info u gave us but your id can be 0 in 3 cases.
1. If you generate or input id on client side u just didn't post it properly. Missed hidden input, wrong variable name.
2. If you generate ur I'd in model constructor you should take in mind that there LL be default constructor called. Then it ll overwrite its properties with new values. Look at your id property is there something that can block or overwrite incoming value.
3. If u generate Id on database level then you should know that data base don't return id when creating new object.
Its a common problem, but I'm not very experienced in it(we generate id in business logic of application).
I'm sure ull track problem if u will have another look at your code.
Related
I'm trying to hide the parameters from the querystrings in my web application.
I have been able to do that by using the session to store temporary variables. So it would work like this:
1. Click the view profile button:
href="#Url.Action("RedirectWithId", "Redirect", new { act = "ProfileView", ctrl = "User", id = member.Id})"
2. Calls the redirection method and stores the temp data:
public class RedirectController : Controller
{
public ActionResult RedirectWithId(string act, string ctrl, int id)
{
Session["temp_data"] = id;
return RedirectToAction(act, ctrl);
}
}
3. Use it in the action method without the parameter:
public ActionResult ProfileView()
{
if (Session["temp_data"] == null)
{
return Redirect(Request.UrlReferrer.ToString());
}
int id = (int)Session["temp_data"];
var model = GetUserById(id);
return View(model);
}
So it works just fine, however, this way to hide parameters doesn't handle the case where let's say I go to a first profile(id 4), and then go to a second one(id 8). If from the second profile I press the back button on the navigator trying to go back to the first profile(id 4), I'm going to be redirected to the current profile(id 8), since 8 is the current value of the Session["temp_data"].
Is there a way to handle this perticular case? Or is the another totally different and better way to hide parameters in the URL?
Thank you!
You can try this instead of Session
TempData["temp_data"]
I came to the conclusion that since I am already using authorizations and roles within my application, I don't need to always hide the parameters. I can simply hide whenever I am passing a complex object as a parameter.
I need feature that is something similar to Laravel's old input helper but in MVC 5.
https://laravel.com/docs/5.6/requests#old-input
If validation fails, I need to reload all my model data as it was in the previous request except those inputs where user entered something wrong.
The problem is that my form has many disabled inputs and fields that program is fetching within [HttpGet] method, and they're getting lost during submission. So I need to store them in session.
The code below seems to work but is there any more efficient and beautiful way to do so with a less amount of code within each controller?
[HttpGet]
[Route(#"TaskManagement/Edit/{guid}")]
public async Task<ActionResult> Edit(Guid guid)
{
var model = new EditTaskViewModel();
model.Guid = guid;
await model.GetTaskFromRemoteService(new UserInfo(User));
ControllerHelpers.DisplayAlerts(model, this);
TempData["OldModel"] = model;
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
[Route(#"TaskManagement/Edit/{guid}")]
public async Task<ActionResult> Edit(EditTaskViewModel model, Guid guid, string submit)
{
model.Guid = guid;
if (ModelState.IsValid) {
await model.UpdateTaskInRemoteService(new UserInfo(User), submit);
ControllerHelpers.DisplayAlerts(model, this, "Task successfully updated");
if (model.ErrorCode == null)
return RedirectToAction("Edit", new { guid = model.Guid });
return RedirectToAction("Index");
}
if (TempData["OldModel"] != null) {
model = (EditTaskViewModel)TempData["OldModel"];
}
return View(model);
}
Using session state (including TempData) like this may break when you have multiple copies of the page open. You can work around this by generating a unique ID for the session key and storing it in a hidden field.
However, I would try to avoid using session altogether.
A simple approach is to use hidden fields to store the values that aren't sent to the server because they are in disabled fields.
A more robust approach is a separate class (or at least a private method) that knows how to setup your model for the first time and in transition (e.g. failed server validation). I call these classes "composers" and I describe the approach here.
Pseudocode for how an action method with a composer might look:
if( ModelState.IsValid ){
return Redirect();
}
var rebuiltModel = _composer.ComposeEdit( incomingModel );
return View( rebuiltModel );
I think the answer was quite simple. The shortest and easiest way is to populate the object from the database\remote service once more.
The fields that user entered whether they're valid or not will stay as they were before. The rest of them will load once again.
I want to create a multilingual webpage. To switch between languages I've got a dropdown on my page. If the change event of the dropdown gets fired the Method called "ChangeLanguage" in my Controller is called.
public ViewModels.HomeViewModel HVM { get; private set; }
// GET: Home
public ActionResult Index()
{
this.HVM = new ViewModels.HomeViewModel();
return View(this.HVM);
}
public JsonResult ChangeLanguage(int id) {
return Json(new {Success = true});
}
Now I'd like to to change my "SelectedLanguage" Property in my ViewModel (HVM) - but the Reference is null. May anyone explain why HVM is null in my ChangeLanguage Method?
After my SelectedLanguage Property is changed I'd like to reload my whole page to display it's texts in another language
e.g.
#model ViewModels.HomeViewModel
<html>
<div class="HeaderText">
Text = #{
#Model.TextToDisplay.Where(o =>
o.Language.Equals(Model.SelectedLanguage)).First()
}
</div>
Here's what I want to do in PseudoCode:
PseudoCode:
public JsonResult ChangeLanguage(int id) {
this.HVM.SelectedLanguage =
this.HVM.AvailableLanguages.Where(o =>
o.ID.Equals(id)).First();
Page.Reload();
return Json(new {Success = true});
}
May anyone explain why HVM is null in my ChangeLanguage Method?
Adhering to stateless nature of HTTP protocol, all (unless explicitly added into request header) requests (MVC method calls) loose state data associated with it. Web server treats every request a new request and creates new instances of classes right from controller itself.
In your case since it is a new request, controller has a HVM property defined but in ChangeLanguage it is not instantiated (it gets instantiated only into Index method which is not called when you invoke ChangeLanguage) hence it is null.
After my SelectedLanguage Property is changed I'd like to reload my
whole page to display it's texts in another language.
Option 1: Refresh page
Simple option to implement. Pass the language selection to server, server will return a new view with specific data. Drawback, whole page will refresh.
Option 2: Update view selectively
If option 1 is really not acceptable, then consider this option. There are multiple ways you can achieve it. Basically it involves either (a) breaking you view into partial view and update only the portion that is affect by selection or (b) bind data element with a JS object.
(a) - Not much need to be said for this.
(b) - Data binding can easily be done if you employ a JS library like KnockoutJS.
Change your methods to these methods , This trick will work for you =>pass your model to Change language from view. Also update JsonResult to ActionResult.
public ActionResult ChangeLanguage(ViewModels.HomeViewModel model,int id)
{
this.HVM.SelectedLanguage =
this.HVM.AvailableLanguages.Where(o =>
o.ID.Equals(id)).First();
return RedirectToAction("Index",model);
}
public ActionResult Index(ViewModels.HomeViewModel model)
{
if(model == null)
{
this.HVM = new ViewModels.HomeViewModel();
}
return View(this.HVM);
}
I am working on a code project in Asp.net MVC. I have an issue with redirecting the user after they have completed an action. I have these controllers:
Index Search Page:
public ActionResult Index(){
//this method sets up viewmodel data for search preferences
Viewmodel obj = new Viewmodel();
//set values of dropdowns and searching capabilities
return View("Search", obj);
}
The user then fills out the search boxes in the view, chooses dropdowns. This will return a post search method that handles the data:
[HttpPost]
public ActionResult Index(Viewmodel obj, int? page)
{
data = from i in db.Database
select i;
if(!String.IsNullOrEmpty(obj.Example)
{
data = data.Where(x => x.poss == obj.poss);
}
//PAGING and other data formatting here
return View("Results", data);
}
Once the result list is displayed, I have a checkbox/button system in the result view that allows the user to select multiple results and mark them as "Good", "Bad" ETC. This is a method that changes the database very simply. My problem is that after the database alters the data, im not sure how to return the user back to the exact result set they were at. A method that returns void doesn't work, and the parameters are not separated, (one whole viewmodel), so i can't simply save the URL and return them back to the unique URL. I want to keep the viewmodel as the parameter. How can I save the viewmodel data that contains their search preferences for use later as well as the page number without changing my method to this:
[HttpPost]
public ActionResult Index(string dropdown1, string dropdown2, int num......){}
One thing I did was save view model to the session. Then I deserialize in the index method. Something like this in the index method:
[HttpGet]
public ActionResult AdvancedSearch()
{
HttpContext currentContext = System.Web.HttpContext.Current;
AdvancedSearchViewModel advancedSearchViewModel = (AdvancedSearchViewModel)Session["AdvancedSearchViewModel"];
if (advancedSearchViewModel == null)
{
advancedSearchViewModel = new AdvancedSearchViewModel();
AddAdvancedSearchLists(advancedSearchViewModel, currentContext);
}
return View(advancedSearchViewModel);
}
Here is some code to save to session in the post:
Session["AdvancedSearchViewModel"] = advancedSearchViewModel;
Note that if you have listboxes (drop down and multi select) you have to rebuild the listboxes in the post method and reselect the selections (for multi select). HTML is stateless. MVC does not send the listbox contents back to the server on the post, it only sends what was selected.
You can use TempData to achieve this.
Store ViewModel and Page in TempData within your POST Index action. The action method used to accept and store result status (i.e. good, bad, etc...) in the database will be able to access ViewModel and Page from the TempData given that it is the next immediate request. Once the database operation is done, just use RedirectToAction with the ViewModel and Page present in the TempData.
If the action which updates result status is not the next immediate request then you need to keep the data in session as answered by BGStack.
I'm pulling my hair out over this one and I'm looking for guidance before I start fudging together my own approach.
Here's what I've got:
View snippet
<td>#Html.ActionLink("More Details", "Index", new { id = product.ProductId })</td>
<td>#Html.ActionLink("Compare", "Compare", new { id = product.ProductId, compare = true })</td>
Controller snippet
public ActionResult Index(FormCollection values)
{
// Does stuff, works
}
public ActionResult Index(int productId)
{
// Does stuff, works
}
Now, here lies my problem. The Index functions are both taken now, from the POST to the form, and the "More Details" ActionLink being clicked. This works fine.
Now I want "Compare" to be functional, in which I want on the same page and will hold a list of compared products, which is fine. But how do I get that Compare functionality on the same View/Page?!
I've tried:
public ActionResult Compare(int productId)
{
}
But obviously that doesn't work as it requires a Compare.cshtml, which I don't want to happen. I want it to be modify my ViewModel and return it with newly Compared products, so I'd be able to do this from my original View:
#foreach(var products in Model.ComparedProducts)
The only way I can see me doing this is "fudging it" to have:
public ActionResult Index(int productId = 0, bool compare)
{
}
Which could become unruly with lots of functionality on the same page.
Surely there's something obvious I'm missing here?
Oh, and the AjaxLink options isn't right for me, as this is part of the site that has to work via postbacks (Progress Enhancement and all that jazz).
I think you return an ActionResult by calling return View(model), is that right? Without naming a view explicitly, the MVC resolving mechanism looks for views with the same name as the action, in your case "Compare.cshtml".
If you change your call to return View("Index", model) you will be using the Index.cshtml view regardless of the action name.
Is that what you were looking for?
An action in MVC does not require a corresponding View. It can return any view by supplying a name parameter to the View() function - see http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.view%28v=vs.98%29.aspx for details.
You can in your compare function do all the logic required and redirect back to the index action or any other that display the page as needed using RedirectToAction().