I have the following code in Edit get method.
public async Task<ActionResult> Edit(int? id)
{
Event e = await db.Events.FindAsync(id);
ViewBag.OldCategories = e.Categories.ToList();
// ViewBag.OldCategories is not null (tested in VS quick viewer)
And I have the following code in the post method.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,....,Categories")] EventCreateViewModel eventVm)
{
var oldCategories = ViewBag.OldCategories; // Null
However, the oldCategories is always null in the post method. Did I miss anything?
ViewBag is only accessible in View when you set it from controller, when form is posted ViewBag values are removed, if you want the values to be accessible in form post you can post them via hidden fields.
<input type="hidden" name="MyHidden" value="#ViewBag.AnyKey"/>
or other way around is to use TempData, instead of placing in viewbag, place data in TempData and remember TempData is once read, means once you read its value it will be removed from TempData, you can retain it by calling TempData.Keep()
public ActionResult MyAction()
{
TempData["myKey"] = "";
}
and View:
#{
string value = TempData["myKey"] as stirng;
TempData.Keep("myKey") // retains value in TempData
}
Once the View is rendered with Razor, the ViewBag is emptied. It's only good for one trip to the client, and then it's wiped. If you want to be able to read it back in your Controller when the client calls back to the server, you'll need to use TempData instead.
Even if you do a Redirect() inside a Controller Action, your ViewBag data will be lost, because you're moving onto a second page, and out of the scope of ViewBag.
Related
I have two methods:
public ActionResult EditNote(int? id)
{
NotesModel edit = NotesProcessor.LoadNote(Convert.ToInt32(id));
if (edit.Author != Session["UserName"].ToString())
{
edit.Id = null;
edit.Title = null;
edit.Note = null;
edit.Author = null;
return View(edit);
}
return View(edit);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditNote(NotesModel model)
{
if(model.Id == null) NotesProcessor.SaveNoteEdit(model);
else
{
model.Author = Session["UserName"].ToString();
NotesProcessor.SaveNote(model);
}
return View();
}
I've simplified the code to only show the problematic part which is:
A logged in user is trying to edit a note which they're not allowed to because it was made by another user. So all the values are set to null so the user will create a new note. The first method will receive an id to search the database for the note the user is trying to edit.
In the next HttpPost method however model.Id is still the same as the integer received by the first method despite changing all the values to null in the first method.
The first method is called from the views page like this:
#Html.ActionLink("Edit", "EditNote", new { id = Model.Id })
Anyone got an idea whats going on here?
You are actually not posting anything to the system as it seems, you are just calling the HttpGet method via URL and then clearing out the view model data at page level only. So, it seems your HttPost method is not even hitting. Try debugging and see whether your HttpPost method gets hit.
You don,t even need to create two methods for what you want to achieve. All you need is just single HttpPost method.
For HtpPost method to be called, you need to either create Form control at UI level with action type as post or you need to hit HttpPost method via JavaScript / JQuery
I hope this helps.
I have this neat model, which is populated on a screen and inserted to a database on successful validation. To make the web app easier to use, I make it redirect to a specific URL after it posts the data. To do that, I pass the URL as a hidden field (the URL is dynamic and depends on the Get request). Of course, on failed validation the model is returned and textboxes and other editors are repopulated, but the hidden field with the URL is not. How can I make it repopulate after validation error, without it beign the part of the model?
Here's some of my code:
-get method:
public ActionResult Create()
{
ViewBag.returnUrl = System.Web.HttpContext.Current.Request.UrlReferrer; ....
-post method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Issue_ID,Issue,Case_Status,
Issued_by,Issue_Date...,HelpDesk_Service_Request_Ticket_Number")] Case #case, string returnUrl)
.
.
.
if (ModelState.IsValid)
{
db.Cases.Add(#case);
db.SaveChanges();
if (returnUrl == null)
{
return RedirectToAction("Index");
}
else
{
return Redirect(returnUrl);
}
}
return View(#case);
Thanks in advance!
From you question I understand you want to pass the return url value from one action (GET) to another (POST). You can store the value in TempData
TempData["returnUrl"] = new Uri("<return url>");
and then try accessing it using
var returnUrl= TempData["returnUrl"];
Note that once the value is read from TempData, it is automatically removed from the collection. For retaining the value you can use keep() or peek() method. Please refer a similar question answered here
Viewbag only lives for current request. You need to use TempData instead.
Please check this thread Viewbag passing value
I have created a page that takes in a string that will search a list of vendors. My goal is to output them to a grid list on an HTML page. Oddly enough, the first page loads, and I can break point the code until the return view of the actual list page. However, it never actually loads. It is even more frustrating because if I don't pass the model to the grid page, it gives me the typical "You can't use a null model", but then it still doesn't load the new page. I have tried several versions. The most current is below.
[HttpPost]
public ActionResult Search(String searchString)
{
this.searchString = searchString;
List<VendorInvoice> v = VendorSearches.publicSearch(searchString);
test = v;
ViewData.Model = v;
TempData.Add("test",v);
return RedirectToAction("Search");
}
[HttpGet]
public ActionResult Search()
{
List<VendorInvoice> v = (List<VendorInvoice>)TempData["test"];
return View("Search",v);
}
So if I take the v out, then I get the error about not passing the model. If it is there, then nothing will happen. The new page won't load.
In your HttpPost search action method, you are setting the result data to show in TempData and doing calling the RedirectToAction method.
RedirectToAction returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. That means, it will be totally new request coming to your search GET action again. Since Http is stateles, it does not have any idea about what you did in your previous request. The data stored in TempData won't be available to this request.
What you should be doing is, similar to your GET action method, simply
return the result to the view.
[HttpPost]
public ActionResult Search(String searchString)
{
this.searchString = searchString;
List<VendorInvoice> v = VendorSearches.publicSearch(searchString);
return View("Search",v);
}
That should fix your problem. But as Stephen Muecke mentioned, you can keep just your GET action method for your Initial view and search result view
public ActionResult Search(String searchString="")
{
List<VendorInvoice> v = new List<VendorInvoice>();
v = VendorSearches.publicSearch(searchString);
return View("Search",v);
}
And your view
#model List<VendorInvoice>
#using(Html.BeginForm("Search","YourControllerName",FormMethod.GET)
{
<input type="text" name="searchString" />
<input type="submit" />
}
<h2>Results</h2>
#foreach(var item in Model)
{
<p> #item.SomePropertyNameOfYourVendorInvoiceHere </p>
}
Let's say I populate a viewbag object in my page action result like this
public ActionResult MiEmployeeDetails()
{
var employees = _db.geo_employees.Select(x => new SelectListItem
{
Value = x.name,
Text = x.name
}).ToList();
ViewBag.Employees = employees;
return View();
}
and then populate in my view as such
<%= Html.DropDownList("Employees") %>
That's fine but then when I hit the submit button and it goes to Httppost Action Result i.e.
[HttpPost]
public ActionResult MiEmployeeDetails(FormCollection fc)
{
return View();
}
I get a null reference exception on my viewbag option. So in my Httppost action result do I also need to reset the viewbag object created in my original page load action result ?
Each request is completely independent. You need to reinitialize all view data if you call the view again from a post. The only exemption is data that is sent from the Client during the post
ViewData and ViewBag allows you to access any data in view that was passed from controller.
The main difference between those two is the way you are accessing the data.
In ViewBag you are accessing data using string as keys - ViewBag[“numbers”]
In ViewData you are accessing data using properties - ViewData.numbers.
ViewData example
CONTROLLER
var Numbers = new List<int> { 1, 2, 3 };
ViewData["numbers"] = Numbers;
VIEW
<ul>
#foreach (var number in (List<int>)ViewData["numbers"])
{
<li>#number</li>
}
</ul>
ViewBag example
CONTROLLER
var Numbers = new List<int> { 1, 2, 3 };
ViewBag.numbers = Numbers;
VIEW
<ul>
#foreach (var number in ViewBag.numbers)
{
<li>#number</li>
}
</ul>
Session is another very useful object that will hold any information.
For instance when user logged in to the system you want to hold his authorization level.
// GetUserAuthorizationLevel - some method that returns int value for user authorization level.
Session["AuthorizationLevel"] = GetUserAuthorizationLevel(userID);
This information will be stored in Session as long as user session is active.
This can be changed in Web.config file:
<system.web>
<sessionState mode="InProc" timeout="30"/>
So then in controller inside the action :
public ActionResult LevelAccess()
{
if (Session["AuthorizationLevel"].Equals(1))
{
return View("Level1");
}
if (Session["AuthorizationLevel"].Equals(2))
{
return View("Level2");
}
return View("AccessDenied");
}
TempData is very similar to ViewData and ViewBag however it will contain data only for one request.
CONTROLLER
// You created a method to add new client.
TempData["ClientAdded"] = "Client has been added";
VIEW
#if (TempData["ClientAdded"] != null)
{
<h3>#TempData["ClientAdded"] </h3>
}
TempData is useful when you want to pass some information from View to Controller. For instance you want to hold time when view was requested.
VIEW
#{
TempData["DateOfViewWasAccessed"] = DateTime.Now;
}
CONTROLLER
if (TempData["DateOfViewWasAccessed"] != null)
{
DateTime time = DateTime.Parse(TempData["DateOfViewWasAccessed"].ToString());
}
ViewBag, ViewData, TempData, Session - how and when to use them?
ViewBag
Avoid it. Use a view model when you can.
The reason is that when you use dynamic properties you will not get compilation errors. It's really easy to change a property name by accident or by purpose and then forget to update all usages.
If you use a ViewModel you won't have that problem. A view model also moves the responsibility of adapting the "M" (i.e. business entities) in MVC from the controller and the view to the ViewModel, thus you get cleaner code with clear responsibilities.
Action
public ActionResult Index()
{
ViewBag.SomeProperty = "Hello";
return View();
}
View (razor syntax)
#ViewBag.SomeProperty
ViewData
Avoit it. Use a view model when you can. Same reason as for ViewBag.
Action
public ActionResult Index()
{
ViewData["SomeProperty"] = "Hello";
return View();
}
View (razor syntax):
#ViewData["SomeProperty"]
Temp data
Everything that you store in TempData will stay in tempdata until you read it, no matter if there are one or several HTTP requests in between.
Actions
public ActionResult Index()
{
TempData["SomeName"] = "Hello";
return RedirectToAction("Details");
}
public ActionResult Details()
{
var someName = TempData["SomeName"];
}
TempData
is meant to be a very short-lived instance, and you should only use it during the current and the subsequent requests only! Since TempData works this way, you need to know for sure what the next request will be, and redirecting to another view is the only time you can guarantee this. Therefore, the only scenario where using TempData will reliably work is when you are redirecting. This is because a redirect kills the current request (and sends HTTP status code 302 Object Moved to the client), then creates a new request on the server to serve the redirected view. Looking back at the previous HomeController code sample means that the TempData object could yield results differently than expected because the next request origin can't be guaranteed. For example, the next request can originate from a completely different machine and browser instance.
ViewData
ViewData is a dictionary object that you put data into, which then becomes available to the view. ViewData is a derivative of the ViewDataDictionary class, so you can access by the familiar "key/value" syntax.
ViewBag
The ViewBag object is a wrapper around the ViewData object that allows you to create dynamic properties for the ViewBag.
public class HomeController : Controller
{
// ViewBag & ViewData sample
public ActionResult Index()
{
var featuredProduct = new Product
{
Name = "Special Cupcake Assortment!",
Description = "Delectable vanilla and chocolate cupcakes",
CreationDate = DateTime.Today,
ExpirationDate = DateTime.Today.AddDays(7),
ImageName = "cupcakes.jpg",
Price = 5.99M,
QtyOnHand = 12
};
ViewData["FeaturedProduct"] = featuredProduct;
ViewBag.Product = featuredProduct;
TempData["FeaturedProduct"] = featuredProduct;
return View();
}
}
namespace TempData.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
TempData["hello"] = "test"; // still alive
return RedirectToAction("About");
}
public ActionResult About()
{
//ViewBag.Message = "Your application description page.";
var sonename = TempData["hello"]; // still alive (second time)
return RedirectToAction("Contact");
}
public ActionResult Contact()
{
var scondtime = TempData["hello"]; // still alive(third time)
return View();
}
public ActionResult afterpagerender()
{
var scondtime = TempData["hello"];//now temp data value becomes null
return View();
}
}
}
In above conversation, there is little confuse for everyone. if you look at my above code, tempdata is like viewdata concept but then it is able to redirect other view also. this is first point.
second point:
few of them are saying it maintains value till read and few of them are asking that so will it read only time? not so.
Actually, you can read any number of time inside your code in one postpack before page render. once page render happened, if you do again postpack (request) means, the tempdata value becomes NULL.
even you are requesting so many time , but the tempdata value is still alive -->this case your number of request should not read temp data value.
In simple terms:
ViewBag is a dynamic (dynamic: ability to assign more than one value by different programs on the same object) object which is used to send data from controller to view. It can be used when we jump from controller's action to some view. However the value which we get in the view(in the viewbag object) can not be further replicated in other view/controller/js page etc. Meaning: Controller->View (possible). Now this value can not be send to next view/controller. Meaning Controller->View->View/controller/some js file (not possible), to carry this value you need to define some other variable and store viewbag value into it and then send it as a parameter.
Tempdata is very much different than viewbag. It has nothing to do with view at all. It is used when we send data from one action(method) to other action in the same controller. That's the reason we use RedirectToAction whenever sending tempdata value from one action to another. Value of tempdata are not retained when send from controller to veiw (because it is not meant to do so).
ViewData is simillar to viewbag but is a dictionary object. ViewData may require type casting while viewbag may not. It depends on the user which one he may want to use.