I have lots of experience with c#, but I am fairly new to WebForms.
I am working on a project where a lot of data is stored in the ViewState and I don't understand why.
My understanding is that the object representing my Page (System.Web.UI.Page) is persistent for the life of that Page. There may be some behind-the-scenes magic where an identifier is stored in ViewState, but when reacting to events on that page, can I not simply refer to "this" and its properties/methods?
When would I explicitly store data in the ViewState as opposed to simply using the properties of the current object (Page in this case)?
--Jacob
My understanding is that the object representing my Page (System.Web.UI.Page) is persistent for the life of that Page
This is correct, however the lifetime of the page is only for a single request, after the HTML has been delivered to the client then the Page instance is destroyed.
I recommend reading this article: https://msdn.microsoft.com/en-us/library/ms178472.aspx
Note that WebForms' design: an abstraction of the stateless-web into a pseudo-stateful WinForms-like environment is largely considered a mistake and that's why ASP.NET MVC and ASP.NET Core has a radically different design (though Controller instances can be compared to Page instances, with similar lifecycle semantics but considerably less overhead).
There may be some behind-the-scenes magic where an identifier is stored in ViewState, but when reacting to events on that page, can I not simply refer to "this" and its properties/methods?
I'm having difficulty understanding this - but if you think that all instance members of a Page, including custom properties using only a backing field are automatically persisted between requests (including "postback" requests) then no, that is not true. There is no magic persistence for fields, you need to use the Page.ViewState property as a backing-store for those properties, and that data is only persisted between special "postback" POST requests.
Let me try my own explanation:
Browser makes request for GET /MyForm.aspx
ASP.NET creates a new instance of MyForm : System.Web.UI.Page, creates all child-control instances declared in the .aspx file, and calls the Init and Load events on all the Controls, then Render to generate output HTML, then Unload. The MyForm instance for the request is then garbage-collected.
If MyForm.aspx contains a <form runat="server"> and makes use of "postback", then when the user does something that triggers a postback then:
Browser makes a request to POST /MyForm.aspx where the request body is the data from the <input > elements. Under the rules of HTML, only the contents of <input> (and <select>, <textarea>, etc) are submitted in the POST request and not the entire DOM - this is a problem for ASP.NET WebForms because all those Controls have many properties and settings (e.g. <asp:Label FontColor=""> which are not persisted through their own <input type="hidden" name="label123_FontColor">, besides, even if they were that's a lot of verbose data to send back to the server.
So instead, ASP.NET instructs all the Control instances to serialize all their non-<input> data to the ViewState dictionary (which represents the state of the View (in MVP/MVC parlance, the .aspx/HTML is the View - hence persisting its state separate from the Request state which by-definition is short-lived).
In a Control or Page subclass you need to use ViewState, not a backing field:
public String SomeName {
get { return this.ViewState["SomeName"] as String; }
set { this.ViewState["SomeName"] = value; }
}
...and then ViewState is serialized and compressed and signed and rendered to the page in <input name="__VIEWSTATE" type="hidden">).
The reason state is carried in __VIEWSTATE instead of simply regenerating the entire page's data on every page request is because sometimes generating the page initially can involve heavy lifting (e.g. a DB query). The argument is that if you have a page with a list of data and the user is simply manipulating that data before saving it back to the DB then you shouldn't need to reload from the DB and instead store that view-level data in the page's __VIEWSTATE as though it's some kind of super-cookie or something.
Related
Spring MVC 4.1
Hi,
I have a situation where, on a single page, there are several input fields. As the users enters numbers into these fields, a bunch of calculations will occur and update various other fields on the page.
I want this whole calculation model to take place in Java on the server-side. I really want to avoid replicating this logic in Javascript on the client.
What I envision is...
User opens the page, the object that does the calculations (let's call it Calculator) is created and its initial state is set (many of its fields are pre-populated with values).
This Calculator instance is stored and available for the duration of the user's time on that page.
Whenever the user changes a value in an input field, that new value is sent to the server via ajax and plugged into our Calculator object. The Calculator, re-calculates the other fields based on the new state and returns the results to the page.
The other fields on the page are updated accordingly.
The key here is that I'm not sending the state of all fields with each ajax request. I'm only sending the current value that was updated. Essentially, I'm trying to ensure that the form state and the Calculator state on the back-end are always synchronized.
I have looked into #SessionAttributes and #ModelAttribute.
The problem with #ModelAttribute, as I understand it, is that it will be re-created with each ajax request.
The problem with #SessionAttributes is that it is a session variable. What if the user has two of these windows open? And how do I ensure the object is removed from the session when they leave the page? etc...
Maybe there's no magic Spring bullet and I just have to figure out the session variable thing. But any pointers on dealing with this would be much appreciated.
Thanks!
You have a couple of options:
.1. Like you have said using the #SessionAttributes, however yes it suffers from the issue that you have mentioned, multiple instances of the same session will see the same variable.
.2. Store state somewhere else and re-hydrate the state using #ModelAttribute annotated method. I would personally prefer this approach, essentially when you create the form, create it with a identifier for the current state:
#RequestMapping(params = "form")
public String createForm(Model uiModel) {
uiModel.addAttribute("calculationId", UUID.randomUUID().toString());
return "calculationpage/create";
}
Then for subsequent ajax requests, ensure your previous calculationId is sent across:
#ModelAttribute("calculationState")
public CalculationState rehydrateState(String calculationId) {
//retrieve current state of calculation from some persistent store..
}
#RequestMapping("/calculate")
public String handleCalculation(#ModelAttribute("calculationState") CalculationState c) {
//you will get a calculationstate with the delta's at this point..
}
.3. Another potential approach may be to use session but disambiguate different instances within the session with a custom id:
public String handleCalculation(HttpSession session, #RequestParam("calcId") String calcId) {
CalculationState calcState = (CalculationState) session.getAttribute("calculation" + calcId);
}
You need any sort of persistent store outside session to store and retrieve the state of your calculator model. Like Biju said, I will go for solutions like No 2.
In a MVC app we implemented a mini forum. New posts in this forum are ajaxed. The AJAX POST action either returns a response form (partial view) or new post html and form html as a JSON.
New post and form are both rendered from views by this method. The model provided for the form has some null values, but the corresponding inputs store values taken from POST data (I verified the generated data to make sure it's not something that is done by the browser). The inputs are generated by html helpers (such as TextBoxFor).
So my question is, is this normal behavior in MVC and if it is, then how do I go about making those inputs have empty/null values (or even some specific value)? When debugging the values in the model are exactly as I set them (which is null, but same thing happens for any value really), but inputs for this very model still hold values taken from POST data.
I tested how does this work with good old PartialView instead of rendering html to string and returning it through JSON, but the results were exactly the same (so the method I use for rendering those views should be unrelated to the problem).
I came across this question: View data dictionary overriding model data in ASP.NET MVC
But from what I checked in my app, the POST data isn't actually stored in ViewData and the OP wasn't AJAXing data so redirects made more sense in his case.
I came across this post which explain this problem in detail and shows ways to deal with it.
To sum it up. Yes, this is normal behavior of htmlHelpers during POST action. To prevent it you can run ModelState.Clear(); in your post action (preferably just before you return\render the view). Optionally it is also possible to remove just one field using ModelState.Remove("PropName"); where PropName is the name of the model property which you don't want to be passed from POST data.
My background is more server-side than front-end. So I may be lacking in some basic front-end knowledge.
I have a endpoint called
/quotations/:id/products
which if you do a POST action, this means you want to add products to the specified quotation where :id represents the quotation id.
In my schema, quotations and products have a many-to-many relationship.
The endpoint also expects the data is sent in the form of products[].
Meaning to say, if I want to add products with id 2, 7, and 154 to the quotation 3
I am POSTING
<input name="products[]" value="2" />
<input name="products[]" value="7" />
<input name="products[]" value="154" />
to the url /quotations/3/products
My question is how do I create the Model and View using Backbone for this setup?
I bought Addy Osmani's book on Developing Backbone.js Applications. So I have setup my backbone similar to his example.
There is only an example of a straight forward add model.
Hopefully I get an answer that follows the convention that Osmani sets out for adding children to a parent type of behavior.
Osmani also mentioned about Backbone Relational.
I am not sure if I should use this or even how. I have read the documentation, but I am not sure how to fit this into the way Osmani has structured his Backbone example apps.
UPDATE:
If it is a success, I want the page to redirect to another page called /success.
If it is a failure, I want the page to display a failure message somewhere. Assume there is a <div id="message"></div> for me to update.
In other words, for failure, the page stays as a single page app.
For success, the page goes to another page.
As for server-side code for returning JSON replies etc, I can do this without any problems. Assume I use PHP.
I faced a similar problem recently, I can't tell your for certain this is the best way to do it but it's the way i've found to work reliably. I tried BackboneRelational but was having troubles with it.
This assumes you will need to create the Quote object & ProductQuotation in 1 step.
Set up a view for New Quote
On initialization of this view:
A) Create a new empty Quote model
B) Create a new collection for ProductQuotes
C) Create a new ProductQuote view for each ProductQuote you're adding
D) Subscribe the Quote view to a custom Backbones event binding (Backbone JS: can one view trigger updates in other views?), i.e.
Backbone.pubSub.on('quote_new-product-save', this.AddProduct, this)
This view will now call AddProduct any time you trigger this event.
On initializing each ProductQuote view create a new Product model
Wire this view up for your user to fill in any applicable information & when they are done set this information to that views Product model & trigger the "quote_new-product-save" event mentioned above and pass it that product model.
Back in the Quote view, AddProduct will automatically be called as it was binded to this event. You should wire this to receive the new Product model & add it to the ProductQuotes collection.
So now you have your Product model data set to your ProductQuotes collection but this is still not directly linked to the current Quote (which still hasn't been saved) so set the 'order_items' attribute on your model to all models in your ProductQuote collection i.e.
this.model.set('product_quotes', this.collection.models)
Now your unsaved Quote model will have all members of your ProductQuotes collection set as an attribute.
Now you'll need to call save on the Quote model & send the data to the server. Things to keep in mind here is that your urlRoot is set properly & that the data being sent to the server is being properly formatted. I recommend reading Saving nested objects with Rails, backbone.js, and accepts_nested_attributes_for about overriding the toJSON method in your Quote model.
I've asked a question about what is "rendering a view". Got some answers:
Rendering a view means showing up a View eg html part to user or
browser.
and
So by rendering a view, the MVC framework has handled the data in the
controller and done the backend work in the model, and then sends that
data to the View to be output to the user.
and
render just means to emit. To print. To echo. To write to some source
(probably stdout).
but don't understand then the difference between rendering a view and using the Response class to send the output to the user using its sendResponse() method. If render a view means to echo the output to the user then why sendResponse() exists and vise versa? sendResponse() exactly sends headers and after headers outputs the body. They solve the same tasks but differently? What is the difference?
In ZF, rendering a view doesn't actually output any content. Instead the rendering process returns the content as a string to the caller. Zend_Application automatically takes care of taking the rendered view and inserting it into your layout (assuming you use layouts) through a placeholder, and putting that data into the Zend_Controller_Response_Http object which is ultimately responsible for delivering the content to the user. The reason for the Response object is so it can encapsulate your HTML output, and also manage any additional HTTP headers or redirects you want to send.
You can also manipulate further the contents of the response in the Response object prior ot sending the data to the client if you wish.
sendResponse() takes care of sending any headers (including the HTTP response code), checking for any exceptions that may have occurred (due to not being able to send headers or other reasons) and then sends the HTML which could include your layout and one or more rendered view scripts.
Hope that helps.
They are two very different things.
Rendering a view provides another layer in which you can template your data. This will allow you to easily dynamically populate HTML/templates keeping logic seperate from presentation.
Echoing data directly skips this step, and is usally reserved for reterning json/xml (data) responses to user instead of html response.
You didn't post which framework you are talking about but both should allow you to specify response headers.
Please don't oversimplify. Every server's purpose is to render resource but that doesn't mean they are all the same.
I have this menu that I want to make available on my layout (essentially every view on my site). I want to maintain the menu's state (whether or not tree items are opened or closed) across page requests. What I want to do is possibly have a child action that is called from my layout page that grabs some cookie or session info which contains the tree's state information so I can rebuild the tree exactly how it was the last request. My problem is, from what I read, it's bad practice to call things like Session and Cookie from within ones controller actions. What I'd like to know is what's a more elegant way to solve this problem. If I must use Cookie and Session, anyone has ideas on how to mock them? Thank you
My problem is, from what I read, it's bad practice to call things like
Session and Cookie from within ones controller actions
Interesting. And did the author of this article explain why it would be a bad practice? There's nothing wrong accessing the session and cookies from your controller actions. Actually using the session for this kind of things might not be suitable because if the user closes his browser you will not be able to persist the layout of the tree whereas with persistent cookies that would be possible.
We can have the Menu as a Partial View.
The Action to Render Partial view will accept parameters to render the state.
Each link will invoke the action with different parameters.
for Example,
public PartialViewResult RenderNavigation(int Menuroot, int subMenuItem)
{
IList<MyNavigationItem> navigationItems=GetNavigation();// Some method that'll read nvigationitems.
navigationItems.Where(n=> n.menuId.Equals(Menuroot) ||n.IsOpen.Equals(subMenuItem)).ToList().ForEach(i=> i.IsOpen=true);
return View("SomeMenuView",navigationItems);
}
public class MyNavigationItem{
int menuId{get;set;}
bool IsOpen{get;set;}
int parentMenuId{get;set;} // set default -1 for Root Items
}