I have a bunch of text inputs on my page (HTML.TEXTBOX, generated through for loop), and I want to read their values and commit to database only when user has modified something in those textboxes.
What would be a proper way to do that? (Do you think it may make more sense to commit the entire thing to database if number of textboxes is less than 100?)
Also, in general, how would I read values from a bunch on textboxes and commit to the database? I would need something that uses a key-value pair, where key would be the id and value would be that input in the textbox.
Unless you use for example JavaScript and hidden fields to keep track of user changes, there is no way for you to know which fields have been modified without querying the database, since the web in general, and ASP.NET MVC in particular, is stateless. However, if you loop out the fields with their values filled in with data stored in an object, you can probably save that object in a session variable to compare against on the next request.
Pseudo-example:
public ActionResult GetFormView()
{
var values = (select relevant information from db and store in a
IQueryable<Dictionary<string, string>> or something similar
where you have a relation between input field id/name and value);
Session["TheInputListValues"] = values;
return View(values); // Your view renders your list of input fields
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveChanges(FormCollection form)
{
var oldValues = (Dictionary<string, string>)Session["TheInputListValues"];
var changedValues = new Dictionary<string, string>();
foreach(string key in form.AllKeys)
{
if(oldValues.ContainsKey(key))
{
if (oldValues[key] != form[key])
{
changedValues(key, form[key]);
}
}
}
SaveToDB(changedValues);
return Redirect("SomeWhereElse"); // PRG is king!
}
I haven't tested this implementation, but it's worth a try =)
I have done something similar using jQuery and it works pretty well. Attach a behavior to the textboxes where if the data changes then submit it's value to an action that saves the data.
$(function(){
$("input[type='text']").change(function(){
$.post("/SaveChanges",{id: $(this).attr("id"),value : $(this).attr("text")});
});
});
This would be assuming you had ID's that were some sort of unique key to you record on each input.
<input id="4576" name="4576"/>
you could also have a callback that would say add a class to this field letting them know that the information was saved by changing it to green or something.
Check this out for more details:
http://docs.jquery.com/Ajax/jQuery.post
Related
I have a Form which will dynamically add textboxes for a particular database field. I wanted to use Model Binding to retrieve values from the Form on a Controller. I know Model Binding uses the textbox's id to retrieve values. However it's never recommended to have multiple textboxes with same ID as its unique. I have it such that in case a user needs more textboxes for the same field, they can add them dynamically by clicking a button. How do I retrieve values from these textboxes? Is there a way I can use html classes with model binding to retrieve the values instead? Or should I do it like this
public ActionResult MyAction(FormCollection form)
{
// ModelBinder will set "form" appropriately
foreach(var value in form.Getvalues("duplicatedFieldId"))
{
//do something with value
}
}
Regards
I really need to maintain this string called "filterParams" in my MVC application. After the user enters some search parameters he clicks submit, and the grid is rebinded with that parameter. That works great. I also save the filterParams data in a Javascript variable, so when the user pages, and the OnDataBinding event is raised, the filter is also passed through that ajax call as well. this is all well and good however there is a huge issue, because when the user updates a question, all the results dissapear because it returns to the View and it does not have any data there. The way I'm using ViewData isn't working, and I could use your help, because if I can store it in ViewData and access it, it would fix my problems. I cannot use TempData because there are a number of other Actions that can be called in between Select and Update...Long question short, how do I implement ViewData correctly to store and retrieve a string in my controller?
Here are some code snippets.
[GridAction]
public ActionResult GetAllQuestion(string filterParams)
{
var _filterParams = new List<string>();
_filterParams.Add(filterParams);
ViewData["filterParams"] = _filterParams;
return View(new GridModel(QuestionManager.Instance.GetQuestion(filterParams)));
}
[GridAction]
public ActionResult EditQuestion(int id, QuestionDTO pQuestion)
{
// var _question = QuestionManager.Instance.GetQuestion(id,false).FirstOrDefault();
// TryUpdateModel(_question);
var _filterParams = (List<string>)ViewData["filterParams"];
var filterParams = _filterParams[0];
QuestionManager.Instance.UpdateQuestion(pQuestion);
// return View(new GridModel(QuestionManager.Instance.GetQuestion(id, false)));
return View(new GridModel(QuestionManager.Instance.GetQuestion(filterParams)));
}
in my aspx page
Html.Telerik().Grid<QuestionDTO>()
.DataBinding(dataBinding => dataBinding.Ajax().Select("GetAllQuestion", "Question", new { filterParams = string.Empty }).Update("EditQuestion", "Question").Insert("CreateQuestion", "Question"))
How can I get this to work please? Help is appreciated
ViewBag/ViewData only works for sending data from an action to a view. It does not get populated by the Model Binder when a request is made to an action, and its state is not saved between requests because ASP.net MVC is entirely stateless. In other words, the ViewData dictionary is always empty at the start of a request.
Meaning this line in your EditQuestion action will not work:
var _filterParams = (List<string>)ViewData["filterParams"];
ViewData is empty, so _filterParams will be null.
You have to manually send filterParams to the EditQuestion action just as you do for the GetAllQuestions action.
Perhaps a better alternative would simply be to persist filterParams using a temp cookie on the client side.
So, just to defy all the misinformation I've read on the subject, TempData infact does persist through multiple action calls in the controller and was able to be used to implement the functionality I needed.
Why just not store the data in Session?
Here's a good explanation with examples
http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications
I have created a mvc3 application.
Currently saving submit form value into Session as i need to do some stuff on that before
save changes to database.
Is there any other good way to store temp data in mvc like ViewState?
I'm saving form input values into Session variable each time when data is entered and and when I clicked on submit button.
After every submit i'm populating my session variable with new added values and displaying all those values into webgrid.
Once i'm done with insertion finally clicked on update button which takes all values from webgrid and pass to one stored procedure as a parameter.
Now here i want to achieve 2 things
1.need to save all inserted data as clicked on submit it gets refresh all time.
so need to save all previous data to.
2.currently using session variable which sets to webgrid to show data into webgrid and once done with insertion passing those values to stored procedure.
So need to know any other good way to save data.
Else can use javascript to do this all on client side.
You should avoid schemes that attempt to make the stateless web seem stateful for two reasons.
Firstly, Session DOES NOT avoid a round-trip, it's only nominally different than storing values in the database. Yes, a fetch of memory is faster than a fetch from SQL but this doesn't scale well -- to scale (more than 1 server) you must add components and hops that reduce the gains and dramatically increase the complexity.
Secondly, Session values are likely to make it to a database anyway so why not just start with them there in the first place.
Thirdly (yes, another one), Session is not necessarily something to trust any more than a hidden form field would be.
In 13 years I've seen session used almost exclusively for the wrong things -- developers regress to using session for convenience and later regret it (as do the developers who replace them and have to maintain their code).
In short, use a database to store this information and be smart about how you fetch and cache it.
You can use TempData (http://msdn.microsoft.com/en-us/library/system.web.mvc.controllerbase.tempdata.aspx), but know that it persists only from one request to the next.
TempData["someKey"] = "";
if you need to persist the data across posts, Session is the proper place. If you need to write the data to a database, then the model should handle the data on post. In your controller:
[HttpPost]
public ActionResult Index(YourModel model)
{
if (ModelState.IsValid)
{
model.SaveToDataBase();
return View("Success");
}
return View("Index", model);
}
Then in the model:
[Serializable]
public class YourModel
{
[Required]
public string YourData { get; set; }
public void SaveToDataBase()
{
//TODO - add code to call stored procedure with data posted to model
}
}
My application needs to do an HTTP post of a table with checkboxes like in the image above. On the controller side I will need to traverse the table and perform certain operations for each row that was checked.
The things that I need to do are:
Identify whether a row is checked
Get the cell values of a checked row
I have a good understanding on how this will be done in Razor in as far as posting the form is concerned. But I am clueless once I am in my controller's action method.
Please help. Thanks.
From what you've show, it appears that all you really need in your action method is a collection of ids to identify which "rows" to modify. I'd use a series of checkboxes with values set to the id of the row they represent. Presumably you have some sort of persistence mechanism in which these rows can be looked up or have them cached server side.
[HttpPost]
public ActionResult Update( List<int> rowIDs ) // where your checkboxes are named rowIDs
{
var messages = DB.Messages.Where( m => rowIDs.Contains( m.ID ) );
foreach (var message in messages)
{
// process the update
}
DB.SaveChanges();
return RedirectToAction( "index" ); // display the updated list
}
Note that it's more likely that you have a model with the collection of ids as well as some other data representing what "update" to perform. Posting collections can be tricky; you might need to play with the name of the input and/or with hidden indexes if you're not getting all the data posted back as expected.
I have a controller with two actions:
[AcceptVerbs("GET")]
public ActionResult Add()
{
PrepareViewDataForAddAction();
return View();
}
[AcceptVerbs("POST")]
public ActionResult Add([GigBinderAttribute]Gig gig, FormCollection formCollection)
{
if (ViewData.ModelState.IsValid)
{
GigManager.Save(gig);
return RedirectToAction("Index", gig.ID);
}
PrepareViewDataForAddAction();
return View(gig);
}
As you can see, when the form posts its data, the Add action uses a GigBinder (An implemenation of IModelBinder)
In this binder I have:
if (int.TryParse(bindingContext.HttpContext.Request.Form["StartDate.Hour"], out hour))
{
gig.StartDate.Hour = hour;
}
else
{
bindingContext.ModelState.AddModelError("Doors", "You need to tell us when the doors open");
}
The form contains a text box with id "StartDate.Hour".
As you can see above, the GigBinder tests to see that the user has typed in an integer into the textbox with id "StartDate.Hour". If not, a model error is added to the modelstate using AddModelError.
Since the gigs property gigs.StartDate.Hour is strongly typed, I cannot set its value to, for example, "TEST" if the user has typed this into the forms textbox.
Hence, I cant set the value of gigs.StartDate.Hour since the user has entered a string rather than an integer.
Since the Add Action returns the view and passes the model (return View(gig);) if the modelstate is invalid, when the form is re-displayed with validation mssages, the value "TEST" is not displayed in the textbox. Instead, it will be the default value of gig.StartDate.Hour.
How do I get round this problem? I really stuck!
I think the problem is that your ViewModel does not match closely enough with your View. It's really important in MVC that your ViewModel matches your View as closely as possible.
In your ViewModel you're assuming an integer, but in your View you're using a TextBox to render the property, which will allow any kind of text. There's a mismatch here and the difficulties you are experiencing trying to map them is a symptom of the mismatch.
I think you should either:
1. Change the type of the ViewModel property to string and then do validation in your controller to ensure the string entered is actually a number or:
2. Change the control that the View renders to a control that will only allow a number to be entered via a custom control or Javascript validation (as #Qun Wang recommends)
Personally, I'd recommend option 1. That way the ViewModel is not dependent on the View implementation.
Could you do this in your PrepareViewDataForAddAction method?..
if (!ViewData.ModelState.IsValid)
{
ViewData["StartDate.Hour"] = "Error";
}
The other fields on the form will still populate based on the properties of the Gig object.
I think you need to do some basic client side validation first.
don't allow it to post to the server.