from my razor edit view I'm hitting mvc controller action where I'm returning ActionResult View.
$.post('/products/details/', { id: someId });
public ActionResult Details(id)
{
...
return View();// in breakpoint this is hitted but never returned as this view.
}
It's always returns me on page where jquery post is initated.
$.post without a callback is like ordering a Pizza then going to bed and forgetting about it. The delivery guy leaves it on the doorstep, where it then gets dragged away by dogs and you never see it :)
Basically: The result of an Ajax post is ignored unless you do something with it.
e.g.
$.post('/products/details/', { id: someId }, function(htmlReturned){
// Do something with the page HTML returned
$('body').html(htmlReturned); // e.g. replace the entire page!
});
Related
I'm in the process of getting my head wrapped around using AJAX to update a portion of an MVC View using some conditional logic. I posted the question earlier and was pointed to using PartialView to build out the whole HTML table and then pass that back to the View.
That lead me to use this article and an example.
https://www.red-gate.com/simple-talk/dotnet/asp-net/revisiting-partial-view-rendering-in-asp-net-mvc/
I then pulled out my table code and put it in a Partial View. All that worked just fine. However, now I'm trying to update with AJAX and the actual updating of the database is working but the updating of the View is not.
In other words, when I click on the "Test" button in the code below I can see it going to the HttpPost in the controller as well as seeing the database being updated. However, the page doesn't get updated. I have to refresh the page again to see the change.
I'm betting it has to do with the #Html.Partial call in the View but I'm not figuring out what I need to do to fix it.
From what I've read it sounds as though when I make the AJAX call and it returns the HTML it should replace everything in side of my DIV.
Here is what I have in my View. Upon initially viewing the page the #Html.Partial is populating the table as expected.
<div id="exceptionTable">
#Html.Partial("_DailyLogExceptionsTable", Model.listExceptions)
</div>
Here is my AJAX call. I can see the values coming through. The RID is the record ID and the arg would be a static value of either "A" or "D" depending on which button they will click.
function ApproveDeny(rid, arg) {
var url = "/Exception/ExceptionApproveDeny/";
$.post(url, {rid:rid, arg:arg})
.done(function (response) {
$("#exceptionTable").html(response);
})
};
This is my ActionResult which updates the database through the DAL. Then I create a new ViewModel, call get the new list of Exceptions, and then call the PartialView again passing it the new list of exceptions.
public ActionResult ExceptionApproveDeny(int rid, string arg, string shift)
{
try
{
DAL.ExceptionApproveDeny(rid, arg);
var vm = new ExceptionLogDailyTableViewModel();
vm.listExceptions = DAL.GetEmployeeExceptionsByDate(CurrentEmployeeId, DateTime.Parse(shift));
return PartialView("_DailyLogExceptionsTable", vm);
}
catch (Exception ex)
{
//error code
}
}
I have a view that displays both the search screen and the results list. To start the results list is hidden.
On clicking the search button, instead of the view posting back to the controller using a Html.BeginForm, it has a JavaScript event handler which is called.
The Handler passes over a complex object, in the form of:
{ "searchData":
{ "ticketNumberCompare":0,
"ticketSearchTextFrom":"0",
"ticketSearchTextTo":null,
"ticketDateCompare":1,
"startDate":"2017-01-06T00:00:00",
"endDate":"2017-01-13T00:00:00",
...
etc
...
},
"searchMode":
{ "mode":2,
"pageNumber":1,
"pageSize":5,
"sortDirection":"desc",
...
etc
...
"ValidationErrorMessages":null
}
}
By using Json.Stringify, I have got the above structure to be converted for use by a function within the c# MVC 5 controller, defined as follows.
[HttpPost]
public JsonResult GetSearchResultsJson(ComplexObject searchCriteria, int? page)
{
}
This function returns back html which is rendered from a Razor view using a model that contains the results from the database.
When I click on the submit button for the initial search all works and I get a partial view of five records displayed with the correct pageList values displayed.
When I click on the pageList items I get a page not found error.
On checking the ajax call for the initial load it is passing over the Json.Stringify of the structure at the top of this question. But the pageList items are not, they just pass over the page
Addendum:
Oh I forgot to mention that I have upgraded this function to use an object instead of the four string that used to be passed, because a new super search now has around 30 properties. Before the change the code worked fine.
Problem Statement:View not able to load on ajax success method.
Description:
I'm having couple of dropdowns as cascaded(second binding based on the value of first).
On change of the first dropdown I'm binding second dropdown and again on change of the second dropdown,now on right side I want to display a list of record based on the two dropdown values.
For this,I'm using the following JS code in onchange event for the dropdown 2:
function ShowDocsList() {
var teamId = $('#TeamID').val();
var projectId = $("#ProjectID").val();
var Url = "#Url.Content("~/DocsHome/DocsList")";
$.ajax({
url: Url,
type:'POST',
dataType: 'html',
data: { TeamID: teamId ,ProjectID : projectId},
success: function (data) {
return data;
$('.docs-detail').html(data);
}
});
Here,in DocsHome Controller,DocsList method is getting hit on change of second dropdown which is project dropdown.But the view is not getting rendered .Following is my Controller Code:
public ActionResult DocsList(int teamId, int projectId)
{
List<CustomerViewModel> customerViewsModels = SmartAdminHelper.GetCustomers(db1);
if (Request.IsAjaxRequest())
return PartialView("DocsList");
else
return View("DocsList");
}
Again ,I'm getting record in List but while debugging it does not pass to the DocsList view which according to me is correct.
Here DocsList is the view I want to render as a partial view on change of the second dropdown.
According to my knowledge,while debugging it comes to the point return PartialView("DocsList") but then again it goes back to the ajax success method and finally I find that there I'm doing something wrong.
Earlier I have Json to get data but here I'm calling actionmethod from ajax. So, not sure that also might be a problem as I'm new to this.
What and where exactly I'm doing wrong?
Saroj, I see that this is an old question and that you needed to get it done quickly, but if you happend to come back to this, I'll add my two cents. You need to remove the return statement that David and Ehsan mention above. The rest of the callback function does what it should. In your action method it doesn't look like you're doing anything with the parameters you pass in. I'm assuming that you are going to figure that out after you get the view down to the client. So, lets get that view down to the client.
I like to pass the rendered partial view back to the client as a string of HTML. I do this using a method that I keep in a controller base class that each of my controllers inherit from. To use the method you will need to reference System.Web.Mvc and System.IO namespaces.
The method looks like this:
private string RenderViewToString( string viewName, object model ) {
ViewData.Model = model;
using ( var sw = new StringWriter() ) {
var viewResult = ViewEngines.Engines.FindPartialView( ControllerContext, viewName );
var viewContext = new ViewContext( ControllerContext, viewResult.View, ViewData, TempData, sw );
viewResult.View.Render( viewContext, sw );
viewResult.ViewEngine.ReleaseView( ControllerContext, viewResult.View );
return sw.GetStringBuilder().ToString();
}
}
You pass your model and the name of the view to the method and it returns the rendered view HTML as a string which you can return to the client as a ContentResult.
Update your action method like so:
public ActionResult DocsList(int teamId, int projectId)
{
List<CustomerViewModel> customerViewsModels = SmartAdminHelper.GetCustomers(db1);
if (Request.IsAjaxRequest())
var viewContents = RenderViewToString("DocsList", customerViewsModels);
return Content(viewContents);
else
return View("DocsList");
}
Assuming that the element that you want the rendered view to appear in has the css class '.docs-detail' on it you'll be in business. Hope this helps!
I have a situation where a when a web page is accessed a controller action runs which retrieves the data for that page based on a user selection. I am attempting to send the data back to the page as a JSON object, however, the data opens up as one large string in an HTML page. The controller action, in a nutshell, looks like the following snippet:
Public JsonResult MyMethod(string userSelection)
{
string userData = (string) Data;
return Json(userData, “text”, JsonRequestBehavior.AllowGet);
}
I first tried to use the JQuery $.getJson() method but I think this is wrong as I believe it issues another call to the action method for the data, which is not what I want to do. What I want is to access the JSON object in JavaScript code so I can use the property data to populate fields on the web page. The basic question is what must I do in my JavaScript to receive the JSON object when the page is first rendered? I apologize if I am missing something fundamental; this is my first try.
I still had no luck today but when I left work I came up with a strategy walking to my car. A user makes a selection from a page that presents a list prior to entering the page on which I cannot figure out how to work with JsonResult. Part of the problem is the user selection contains a link that calls the controller/action that returns the JsonResult which conflicts with using $.getJson() within the page where I want to work with JsonResult. So here is my strategy: When the user makes the selection that brings them to the (problematic) page, I will call a controller/action that strictly works with (ASP) ViewData, and use the ViewData to initially present that page. Once on the page, the user can change the selection -- I will handle this with a JavaScript event that uses a $.getJason() call to a different controller/action method that works with (ASP) JsonResult. After I try this strategy I shall post my results for whomever is interested.
You want parseJSON not getJSON
http://api.jquery.com/jQuery.parseJSON/
Edit - Oh wait you are pointing your browser at the JsonResult as if it was an ActionResult? That is not going to work.
Render a proper view, and use getJSON to call the JsonResult action.
getJSON is what you are looking for. Call that on the DOM ready event which will executes once the DOM finishes loading.
$(function(){
//This code will be executed on the DOM ready ( when the page is loaded)
$.getJSON("YourControllerName/MyMethod?userSelection=someValue",function(data){
alert(data.FirstName);
alert(data.AnotherPropertyName);
});
});
getJSON is a shorthand of jQuery ajax method with datatype set as json
Assuming your JSON data you are retuning is something like this
{
"FirstName": "Scott",
"AnotherPropertyName": "SomeValue"
}
To return data like above, change your Action method like this
public JsonResult MyMethod(string userSelection)
{
var result=new { FirstName="Scott", AnotherPropertyName="Great"};
return Json(result,JsonRequestBehavior.AllowGet);
}
Im starting to build a new app and I would like to use Backbone as my framework. Below is a basic workflow that this (and most apps) follow.
What is the correct/best model to use with Backbone?
Old Way
User navigates to a page.
Selects "Create New widget"
User is presented with a form filled with inputs
At this point I would probably take the values entered (after passing basic validation), wrap them up and send them to the server via an ajax request
Request comes back as "OK" and the user is taken somewhere else (This step isn't entirely important)
Some basic pseudo-code
// Grab values
var userName = $('.UserName').val(),
dateOfBirth = $('.DateOfBirth').val();
...
...
...
$.ajax({
url: "/Webservices/ProcessStuff",
success: function(result){
if (result) {
// Render something or doing something else
} else {
// Error message
}
},
error: function () {
// Error message
}
});
Backbone way
Using the same example as above; I assume I'd have a model for the user information and a view to display the inputs. However, processing the actual call to the web service is one of the things I'm confused about. Where does this need to go? In the model or in the view click of some "Go" button?
Model.UserInformation = Backbone.Model.extend({ username: null, dateOfBirth: null });
Maybe also have a collection of these UserInformation models?
UserInformations = Backbone.Collection.extend({ model: Model.UserInformation' });
So bottom line what I'm asking is...
What is the best way to achieve this functionality?
What is the proper way to actually perform CRUD? Where to put the actual call to delete/update/create/etc?
You have the right idea and Backbone should make it easy for you to get things done using the same basic high level overview of your workflow. Note that you're still going to be using jQuery for this functionality - you'll just be doing it through the organizational aspects of Backbone's types.
There are a couple of key items that you'll want in place, most of which you already mentioned:
A backbone View to coordinate the HTML elements with your Javascript code
A backbone Model to store all of the data that the user input into the HTML elements
A back-end server that can handle RESTful JSON calls via AJAX requests from jQuery
I think the only thing you are missing is that the model has a save method on it, which wraps up all of the logic to call the create / update routes on your back-end server. The model also has a delete method to handle deletion from the server.
As a very simple example, here's a form that renders an HTML template to the screen, gathers the user input in to the model and then saves it to the server.
An HTML template:
<script id="myTemplate" type="text/x-jquery-tmpl">
First name: <input id="first_name"><br/>
Last Name: <input id="last_name"><br/>
<button id="save">Save!</button>
</script>
The code to run this:
MyModel = Backbone.Model.extend({
urlRoot: "/myModel"
});
MyView = Backbone.View.extend({
template: "#myTemplate",
events: {
"change #first_name": "setFirstName",
"change #last_name: "setLastName",
"click #save": "save"
},
initialize: function(){
_.bindAll(this, "saveSuccess", "saveError");
},
setFirstName: function(e){
var val = $(e.currentTarget).val();
this.model.set({first_name: val});
},
setLastName: function(e){
var val = $(e.currentTarget).val();
this.model.set({last_name: val});
},
save: function(e){
e.preventDefault(); // prevent the button click from posting back to the server
this.model.save(null, {success: this.saveSuccess, error: this.saveError);
},
saveSuccess: function(model, response){
// do things here after a successful save to the server
},
saveError: function(model, response){
// do things here after a failed save to the server
},
render: function(){
var html = $(this.template).tmpl();
$(el).html(html);
}
});
myModel = new MyModel();
myView = new MyView({model: myModel});
myView.render();
$("#someDivOnMyPage").html(myView.el);
This will give you a quick start for a form that saves a new model back to the server.
There are a couple of things your server needs to do:
Return a valid HTTP response code (200 or some other response that says everything was "ok")
Return the JSON that was sent to the server, including any data that the server assigned to the model such as an id field.
It's very important that your server do these things and include an id field in the response. Without an id field from the server, your model will never be able to update itself when you call save again. It will only try to create a new instance on the server again.
Backbone uses the id attribute of a model to determine if it should create or update a model when pushing data to the back end. The difference between creating a new model and saving one is only the id attribute. You call save on the model whether it's a new or an edited model.
A delete works the same way - you just call destroy on the model and it does a call back to the server to do the destroy. With some HTML that has a "delete" link or button, you would attach to the click event of that HTML element the same as I've shown for the "Save" button. Then in the callback method for the delete click, you would call this.model.destroy() and pass any parameters you want, such as success and error callbacks.
Note that I included a urlRoot on the model, as well. This, or a url function are needed on a model if the model is not part of a collection. If the model is part of a collection, the collection must specify the url.
I hope that helps.
If the "el" of the view is the form tag, then you could probably use the built in event object to bind a function to submit, but if the root of the view is something else, then you'll need to attach the click handler in the render function.