Update model with Ajax Post - javascript

I'm trying to update the Model with a background Ajax Post, below is my existing code
Javascript (Jquery)
var url = '#Url.Action("UpdateValue", "MyController")';
$.post(url, $('form').serialize(), function (view) {
//...
});
Controller
[HttpPost]
public ActionResult UpdateValue(MyViewModel model)
{
model.FileName = "NewValue";
return Json(new { success = true });
}
This code posts the existing model to controller and then I'm updating the field FileName, but this does not seem to retain the updated value ("NewValue"). How to make it update the existing model with new value?

Setting model.FileName in the action doesn't do anything to the UI or the database. It depends on what you are trying to update, but if you are trying to update the UI, you would need to push the model back down to the client, and then reload the UI via client-JavaScript (since you are doing an AJAX post with JQuery).

How about sending back the updated model:
[HttpPost]
public ActionResult UpdateValue(MyViewModel model)
{
model.FileName = "NewValue";
return Json(model);
}
Then in your $.post you can read the response and update your field.
As Brain Mains commented, you can't persist the model. Model is just nice way of accessing POSTed values.
Let's assume you are posting more values than FileName. So instead of doing this:
Request.Form["FileName"];
Request.Form["Id"];
Request.Form["Size"];
You just do this:
model.FileName
model.Id
model.Size
It's a lot cleaner and nicer to work with.
So If you follow the process of posting data:
User clicks a button > data is submitted via ajax > arrives to the asp.net engine > engine binds submitted data to the model > model is passed to controller > controller returns a result (and in this moment ... hackemate! ... the model is gone)

Related

JQuery DataTable Inline Editing With Submit Button - Post return 405 (Method Not Allowed)

I am using jquery Datatables Editor and can't quite seem to figure out the set-up of how the inline editing should go. Meaning my Controller code reads like this
[HttpGet]
[Route("api/tta")]
public JsonResult Index()
{
var ListData = _context.TTA.FromSqlRaw("Select * from dbo.Test").ToList();
return Json(new { Data = ListData });
}
which when I launch my asp.net core MVC append load the page the data loads exactly as expected. However, when I try the inline edit and press the submit button for the edit, I get this error in the dev console
jquery-3.3.1.js:9600 POST https://localhost:44343/api/tta 405 (Method Not Allowed)
Now from my understanding it seems that the issue is that a POST is being attempted on an API that is only set up to make a GET request. Which leads me to my question of what is the proper way to set this up so that the data will be submitted to the database successfully?
ASP.Net Core & MVC & Microsoft SQL Server if needed here is a link to the inline edit feature of DataTables https://editor.datatables.net/examples/inline-editing/submitButton.html
You need to create an method that accept POST request and recieve your data. Example:
[HttpPost]
[Route("api/tta")]
public JsonResult Post(YourType yourParameter)
{
var result = MethodThatUpdateYourData(yourParameter);
return Json(result);
}
From looking at the code given, your api/tta method allows just [HttpGet]. Simply add a new method with [HttpPost] which you can call to submit the data

Getting parameters from query string

I am creating a web page using ASP.Net WebAPi, MVC and Knockout.
I have a normal MVC controller that loads the pages when I need them:
[Authorize]
public class AdminController : Controller
{
public ActionResult Clients()
{
return View();
}
public ActionResult ClientEdit(int? Id)
{
return View();
}
}
And once the page is loaded, my Knockout model takes care of the loading of the data. So, the 'Clients' controller simply loads a list of all clients. When on that screen, a user can click 'Edit' next to a client, and the page is navigated to the 'ClientEdit' controller, which takes an id.
So, my knockout click event looks like this in my knockout view model:
self.EditClick = function () {
if (this.ClientId && typeof this.ClientId !== 'undefined') {
window.location.href = "/Admin/ClientEdit/" + this.ClientId;
}
else
window.location.href = "/Admin/ClientEdit/";
}
(It handles the 'Create New' button and the edit button, hence the 'if')
Once I redirect, the MVC controller loads the page, and the URL is:
http://localhost:49389/Admin/ClientEdit/1
I then load the knockout model, and would like to make an API call to get the data...
After my page loads, I want to bind the view model to the page. Here's my view model at the moment:
function AdminClientEditor() {
var self = this;
self.Name = ko.observable("");
self.ContactName = ko.observable("");
ko.applyBindings(new AdminClientEditor(), $("#clienteditor")[0]);
So, I will create a $.get method that calls a webAPI method that will return me data based on the id. I just need to get the ID somehow.
But, how do I get the Id (In this case, '1', from the URL?
And, is this the right way to achieve what I am trying to do?
You can pass the id value to view via viewbag.
public ActionResult ClientEdit(int? Id)
{
ViewBag.ClientId=id;
return View();
}
and in the view's script section
var clientId="#ViewBag.ClientId";
alert(clientId);
// use this
If your javascript code which accesses this id value is inside a separate external js file, you may set this value to a js variable in your view and access it in your js file. Make sure to use namespacing to avoid global variable overwriting value issues.
So in your view
<script>
var myApp = myApp || {};
myApp.ClientId= "#ViewBag.ClientId";
</script>
<script src="~/Scripts/PageSpecificExternalJsFile.js"></script>
And in the PageSpecificExternalJsFile.js file,
var clientId=myApp.ClientId;
//use this as needed
I'm not sure if this is the best way, but you can get the ID from the URL by using JS:
var id = GetID();
function GetID() {
var href = location.href;
var results = href.split("/");
return results[results.length - 1];
}
I've come up with this solution which works, but I am unsure if it's the best way. It seems pretty good.
I created a MVC ViewModel class in my application code, called 'GenericParameteModel', which at the moment, has a single parameter, "Id".
I then modified my page loading MVC method:
public ActionResult ClientEdit(int? Id)
{
var mv = new GenericParameteModel { Id = Id };
return View(mv);
}
On my View page, I added the model 'GenericParameteModel' to the View.
I created a hidden field, called 'ClientId' on the view.
<input type="hidden" id="clientId" value="#model.Id">
Then, within my knockout view model, I check if $("#clientId").val() has a value. If so, I do the $.get call, using that value, and populate my view model.
In doing so, all my initial page loads from MVC will have the ability to you the GenericParameteModel, and it will be a pattern for other pages. As it's a model, I can add new fields as my application requires.
This seems to work well. I'm unsure if this is an acceptable way as I am new to this (MVC to load views and the Knockout/WebApi to get the data after loading). But it seems neat and manageable.

In an action method, how can I bind post data to a dynamic object?

I want to do this:
public ActionResult SaveStuff(dynamic vm) {
StoreTheValue(vm.myvalue);
return Content("Saved :)");
}
This doesn't work, MVC doesn't seem to want to create a dynamic object with properties that correspond to the post data of the request.
Now I know that the whole point of properly defined view models is to create strongly typed data structures and have MVC bind data into them, but given that I'm posting data from javascript using ajax it's not strongly typed data anyway, so I don't see that I'm loosing any maintainability by doing this, and it will save me time and effort creating view model classes.
Can anyone help suggest how I can bind post data to a dynamic object, posssibly using a custom model binder?
One possible way to achieve this would be to use a custom model binder, assuming that you are posting Json to the action
public class DynamicBinder : IModelBinder
{
public object BindModel( ControllerContext controllerContext, ModelBindingContext bindingContext )
{
using( var streamReader = new StreamReader( controllerContext.HttpContext.Request.InputStream ) )
{
return JsonConvert.DeserializeObject< dynamic >( streamReader.ReadToEnd() );
}
}
}
then in your action you can tell it, to use the custom binder
public ActionResult SaveStuff([ModelBinder(typeof(DynamicBinder))]dynamic vm) {
StoreTheValue(vm.myvalue);
return Content("Saved :)");
}
then post your json as such :
{
"myvalue":{...}
}
dynamic type and ajax request that you do with javascript is not corresponding.
You always can create your strongly typed object properties on javascript side.
Anyway you can use FormCollection like this:
[HttpPost]
public ActionResult yourAction(FormCollection collection)
{
StoreTheValue(Convert.ToString(collection["myvalue"]));
return Content("Saved :)");
}
But I think it's better to think of a strongly typed way.

How to have an ActionResult not redirect the page?

So I have an HttpPost in my controller that requires an object from my model. It then returns an HttpStatusCodeResult depending on success of the action. On my view, I want to run some basic javascript based on the status code result instead of redirecting to a new page. I would simply do this with AJAX but I need to send the object with a form. How would I go about doing this?
You can serialize your form and send it via ajax. Your model binding will work fine. Send a response which your client side js code can read and execute your CUSTOM functions then.
Example, You might have a create view with a form like this
#model CreateCustomerVM
#using(Html.Beginform())
{
#Html.TextBoxFor(s=>s.Name)
<input type="submit" id="btnSubmit" />
}
and the script to handle the form posting
$(function(){
$("#btnSubmit").click(function(e){
e.preventDefault();
var frm=$(this).closest("form");
$.post(frm.attr("action"),frm.serialize(),function(res){
//do something with res here'
// if(res.Success)
// {
// alert(res.Message)
// }
});
});
});
So your HttpPost action method should read the posted form, Do whatever it needs to do and send a response back, like this
[HttpPost]
public ActionResult Create(CreateCustomerVM model)
{
// to do : Read from model and save
// someService.Save(model)
return Json(new { Success = true, Message="Saved successfully"});
}

Return View from controller after jquery post

Hi I am new to MVC and I have the below senario
VIEW:
step3.cshtml is my view
This has data binded to step3.js(using knockout)
$.post("/Step/Step4", { "some":some_data }, function (data) {
}, 'JSON');
CONTROLLER:
[HttpPost]
public ActionResult Step4(model foo)
{
//save this data to the database and return a view
using (DBContext dbContext = new DBContext())
{
dbContext.Table.Add(foo);
dbContext.SaveChanges();
}
return View(foo);
}
public ActionResult Step4()
{
//get this view with the model
}
I am able to see that the view Step4.cshtml is able get the property values from model but.
I see that my View Step4.cshtml is a response header from the post how do I render this in the browser.
Is this the right approach??
So is it that an HTML form post can return a view and replace the whole content and AJAX cannot do that except for the partial view update?
When you call $.post, you are making a jQuery ajax request with a POST method. That data is given back to you in the success method. If you are returning the view, then you will have to manually replace the html in your document with that response.
Normally, when you use ajax like this, you would return a PartialView to update a section of your page, not your whole page. You might want to see if you can return a partial and update a div.
You can also do that by using an Ajax.ActionLink, and in the AjaxOptions, specify the id of the element you want to update when the response is returned, and the InsertionMode, which should be set to Replace. And you won't have to worry about replacing the content yourself.
Please see this stackoverflow question, which may help you: How To Use Ajax.ActionLink

Categories

Resources