I have two methods in my controller that are called via ajax on click. Both do the exact same thing (retrieving the same data from a database) and return a partial view along with the model that contains the retrieved data. The only difference is the view.
public PartialViewResult FormA()
{
[...// Code]
return PartialView("_FormA", ModelWithData)
}
public PartialViewResult FormB()
{
[...// same Code as in FormA()]
return PartialView("_FormB", ModelWithData)
}
Both views use the same data but show different things.
If FormB() is called FormA() definitely has been called before.
There must be a way to bypass the second method/database request. It perceptibly slows down the application due to the additional database request.
My question seems really stupid to me, but I'm not able to find a workaround...
Thx for your help!
Yes sure by passing some kind of filter to your action method like below
public PartialViewResult ShowForm(string filter)
{
if(TempData["model"] == null)
{
[...// Code]
TempData["model"] = ModelWithData;
}
if(filter == "some_condition")
return PartialView("_FormA", TempData["model"] as ModelWithData);
else
return PartialView("_FormB", TempData["model"] as ModelWithData);
}
Got your point now. You can use any type of state management mechanish. Say TempData
Related
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 have a method inside MVC Controller which is called from href inside anchor tag.
public ActionResult DoSomething(string value)
{
if(true)
{
return new RedirectResult("http://google.com");
}
}
when I debug and hit that method Response.Redirect does nothing no exceptions either. any ideas?
Thanks in advance!
Use Redirect
return Redirect("http://www.google.com");
Response.Redirect is not preferred way of doing redirects in asp.net mvc
Response.Redirect and ASP.NET MVC – Do Not Mix
Update: It seems that you are trying to redirect ajax request. If you redirect ajax request, your main page won't be redirected.
There are a few things you need to do here to avoid all these issues.
Starting with the AJAX errors you're getting, they most like relate to the javascript debugger, which Microsoft refer to as "BrowserLink".
If you use Firefox or Chrome, this feature simply doesn't work, which is probably the easiest way to avoid the issue, however you can disable the feature here:
You can change the default browser to run the website in just to the left.
In terms of Response.Redirect, I think that's been well covered, you should use return Redirect() instead, however your code needs to be refactored to allow for that.
Assuming that method is a helper method which is required to be separate from the controller itself, there are a couple of main approaches to doing what you're trying to to do.
1) Magic Values
This could include "redirect1" or also commonly null, and would look something like:
public ActionResult MyAction
{
string data = ProcessString("some data");
if (data == null) { return Redirect("google.com"); }
}
public string ProcessString(string input)
{
if (condition) { return null; }
string output = input + "!"; // or whatever you need to do!
return input;
}
2) Handle via exceptions
Assuming the problem is that the data is in some way bad, and you want to redirect away because you cant process it, Exception handling is most likely the way to go. It also allows for different types of exceptions to be raised by a single method and handled independently without having magic values which then can't be used as normal data.
public ActionResult MyAction
{
string data; // remember this will be null, not "" by default
try
{
data = ProcessString("some data");
}
catch (OwlMisalignedException ex)
{
return RedirectToAction("Index", "Error", new { exData = ex.Code });
}
// proceed with controller as normal
}
public string ProcessString(string input)
{
if (condition)
{
throw new OwlMisalignedException(1234);
// this is (obviously) a made up exception with a Code property
// as an example of passing the error code back up to an error
// handling page, for example.
}
string output = input + "!"; // or whatever you need to do!
return input;
}
By using that approach you can effectively add extra return states to methods without having to fiddle with your return type or create loads of magic values.
Don't use throw Exception - either use one of the more specific types ArgumentException and ArgumentNullException will probably come in handy, or create your own types if needs be.
You'll find info on creating your own Exception types on here easily enough.
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 have the following Action method that return an _error partial view in case an Exception occur:-
[AcceptVerbs(HttpVerbs.Post)]
public PartialViewResult Register(string id, int classid) {
try
{
Thread.Sleep(3000);
User user = r.FindUser(id);
Users_Classes uc = new Users_Classes();
uc.AddedDate = DateTime.Now;
uc.ClassID = classid;
user.Users_Classes.Add(uc);
r.Save();
ViewBag.classid = classid;
return PartialView("_usersearch2", uc);
}
catch (DataException ex)
{
return PartialView("_error");
}
and the following _error partial view:-
<script type="text/javascript">
alert('The user might have been already Assinged, Search Again to get the latest users');
</script>
The above approach is working fine, but does it consider a bad design to return a partial view to display only an alert ? and is there a better way to do this?
The problem is you are now tying your implementation to your user interface. The Controller suddenly decides how an error message should appear on the client.
What if you want to change it from an alert to displaying a red border around a text input with some description next to it?
Determining how something should be displayed is up to your view. Your controller should only return status codes and then your view should decide what to do.
Instead of returning inline js you should have error handling code on your client side within a js library. Rather than returning the hole js only return the message.
In general, I'd say yes. But, sometimes a bad design is just what the doctor ordered ;)
There's a Controller instance method called Javascript that I use to return executable javascript from my controller, on very limited occasions, when taking the time to do it the "right" way isn't feasible:
[AcceptVerbs(HttpVerbs.Post)]
public PartialViewResult Register(string id, int classid)
{
try
{
... stuff
}
catch (DataException ex)
{
return Javascript("alert('The user might have been already Assinged, Search Again to get the latest users');");
}
}
The fact that something like this exists gives me solace that I'm not completely breaking the law.. unless I'm using it wrong, which I probably am.
I have 2 Action methods in one controller,
Index:
public ActionResult Index(string url)
{
// take the url as a param and do long tasks here
ViewBag.PageTitle = "title";
ViewBag.Images = "images";
// and some more view bags
return View();
}
This index view contains a form which post to another method in the same controller.
public ActionResult PostMessage(string msg, string imgName)
{
// save data in the db
// but on error I want to navigate back to the Index view but without losing data the user fielded before submit the form.
// Also need to pass an error message to this index view to show
}
How to return back to Index view if something went wrong in the PostMessage method, and also don't clear the form fields, plus showing an error message which the PostMessage method specified.
I need to know the best practice for doing such a scenario.
The best approach is usually to create a ViewModel type for your form. Add attributes to the properties of that model to define what would make it "wrong." Make your form use methods like #Html.TextBoxFor the various fields. Then have your PostMessage class take an object of that type, rather than taking the message and image name directly. Then you can validate the model and return the view again if the model is invalid.
See http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx for some code examples following this pattern.
You could specify the name of the view you want to return:
public ActionResult PostMessage(string msg, string imgName)
{
if (SomeErrorWhileSavingInDb)
{
// something wrong happened => we could add a modelstate error
// explaining the reason and return the Index view.
ModelState.AddModelError("key", "something very wrong happened when trying to process your request");
return View("Index");
}
// everything went fine => we can redirect
return RedirectToAction("Success");
}
Just redirect back to the Index action
return RedirectToAction("Index");
There are overloads for this method that allows you to pass route values and other information.