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!
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
}
}
So I'm trying to use a join table to display a list of data in my Parse app. The javascript API is similar enough to backbone.js that I'm assuming anyone who knows that could help me. I can't show my actual source code but I think I simple twitter-like "user follows user" scenario can answer my question. So assume I have a join table called "follows" that simply contains its own objectId, the id of each user in the relationship, and some meta-data about the relationship (needing metadata is why I'm using a join table, instead of Parse.Relation). I want to have a view that finds all of the users the current user follows and renders an instance of another view for each case. From what I have so far, that would looks something like this.
In the intialize of the top level view (let's call it AllFollowsView), I would have something like this.
var currentUser = Parse.User.current();
var followsQuery = new Parse.Query(Follows);
followsQuery.equalTo("userId", currentUser.id);
followsQuery.find({
success: function(followsResult){
for (var i = 0; i < followsResult.length; i++){
var view = new OneFollowView({model:followsResult[i]});
this.$("#followed-list").append(view.render().el);
}//for loop
},
error: function(error){
console.log("error finding plans query");
}
});
OneFollowsView is just a view that renders an showing data about the relationship and listens for changes on that particular relationship (mainly change or delete in my case). I understand that by passing in the corresponding model with
var view = new OneFollowView({model:followsResult[i]});
I can print out attributes of that model in the OneFollowsView template like this
<li>You are following a user with the id of <%= _.escape(followedUserId) %></li>
My problem is that this only gives me access to the information stored in the "follows" object. How would I pass in the corresponding user models (or any other models that I can query for the id of) into the template so I can access them in the html in the same way. I would like to be able to run queries in one of the views and then access those models in the html. I know I can add attributes to the object before declaring a new instance of the lower level class with that object as the model, but that doesn't help me because I don't want to save it with new attributes attached.
EDIT: My render function for the top level function is empty at the moment. It's initilize function contains this line to render the template. I guess this should probably be in the render function and then I would call render from initialize.
this.$el.html(_.template($("#all-follows-template").html()));
Here's the render for the lower (individual li) view
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
this.delegateEvents();
}
From my understanding this just renders the template to el while parsing the model to JSON and then returns to allow chained calls.
The problem here lies in you render method. When you call this.template in your render method. That method, this.template is a template function returned by calling the _.template function. When you call your this.template method, the properties of the object you pass in will be available as instance variables in your template.
In your case you're passing in the JSON of the object. So, the properties of the model become names of variables available in your template. If you want to expose additional variables to the template you have a couple options: 1) Add to the jsonified model's attributes. 2) Send in the model as a top level variable and any additional variables you may want.
// option 1
render: function() {
var templateArgs = _.extend(this.model.toJSON(), { additionalVar: 'new var' });
var content = this.template(templateArgs);
$(this.el).html(content);
this.delegateEvents();
return this;
}
// option 2
render: function() {
var templateArgs = {
followResult: this.model.toJSON(),
additionalVar: 'new var'
};
var content = this.template(templateArgs);
$(this.el).html(content);
return this;
this.delegateEvents();
}
Either option is reasonable. I would probably go with option 2. Which allows you in the template to say something like:
<li> <%= followResult.someProperty %> <%= additionalVar %> </li>
Hope that helps. :)
I am trying to call a method inside a controller in MVC from a javascript action. The javascript action is supposed to invoke this method inside the controller and send some parameters to it.
My Javascript code looks like this:
location.href = '#Url.Content("~/Areas/MyArea/MyMethod/"+Model.MyId)';
My Method is defined as follows:
[HttpGet]
public ActionResult MyMethod(int? MyId)
{
doSomething(MyId);
return View("MyView");
}
However, when i debug the application, when the method is called the MyId parameter is passed as null and not as the current value of the MyId parameter in my model. What can I do to correctly send or retrieve this value? Thanks!
In your route definition I suppose that the parameter is called {id} and not {MyId}:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"MyArea_default",
"MyArea/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
So try to be more consistent and adapt your controller action parameter name accordingly:
[HttpGet]
public ActionResult MyMethod(int? id)
{
doSomething(id);
return View("MyView");
}
Also you probably wanna use url helpers instead of hardcoding some url patterns in your javascript code:
window.location.href = '#Url.Action("MyMethod", "SomeControllerName", new { area = "MyArea", id = Model.MyId })';
The Url.Content helper is used to reference static resources in your site such as javascript, css and image files. For controller actions it's much better to use the Url.Action helper method.
tl;dr
How to use backbone.stickit with a html form to change an existing model fetched from the server and only PATCH the changed attributes (changed by user input within the html form) to the server?
/tl;dr
I'm using backbone.stickit in a backbone.js application to bind a model to a HTML-form which is part of a backbone view. This works fine so far, but it becomes a little bit complicated if I'm going to save the bound model. This is because I want to use the PATCH-method and only send the changed attributes to the server. I try to illustrate what I've done so far:
Fetching the model from Server
user = new User(); //instatiate a new user-model
user.fetch(); //fetching the model from the server
console.log(user.changedAttributes()); // Returns ALL attributes, because model was empty
The last line indicates my problem, because I thought I can used the changedAtrributes() method later to get the attributes which need a patch on the server. So I tried this workaround which I found here
user.fetch({
success: function (model, response, options) {
model.set({});
}
});
user.changedAtrributes(); //Returns now "false"
Do stickit-bindings
Now I render my view and call the stickit() method on the view, to do the bindings:
//Bindings specified in the view:
[...]
bindings: {
"#username" : "username"
"#age" : "age"
}
[...]
//within the render method of the view
this.stickit();
The bindings work fine and my user model gets updated, but changedAttributes() remain empty all the time.
Save the model to the server
If the user has made all required changes, the model should be saved to the server. I want to use the PATCH method and only send the changed attributes to the server.
user.save(null, {patch:true}); //PATCH method is used but ALL attributes are sent to the server
OR
user.save(user.changedAttributes(),{patch : true});
With the second approach there are different outcomes:
if I didn't use the user.set({}) woraround, all attributes get PATCHED to the server
if I use the user.set({}) woraround the return value of changedAttributes() is "false" and all attributes are PUT to the server
if I call a user.set("age","123") before calling save(), then only the age attribute is PATCHED to the server
So outcome 3 is my desired behaviour, but there are 2 problems with this: First stickit doesn't seem to use the set() method on the model to update the attributes if they are changed within the html-form. And second, if you call set() with one attribute and afterwards with another, only the second attributes is returned by changedAttributes().
Maybe I just overseen something in the backbone or backbone.stickit docs, so I didn't get the desired behaviour working. Any ideas about that?
NOTE: As found out the problem wasn't directly related to backbone.stickit, more to backbone itself.
Solved this problem on my own, maybe this helps someone who may stumble upon this question:
Backbone only keep track of unchanged attributes, but not of unsaved attributes. So with
model.changedAttributes();
you will only get the attributes of the model, which was changed since the last
model.set("some_attribute","some_value")
Finally I stumbled upon backbone.trackit which is a backbone.js plugin maintained by the creator of backbone.stickit. With this plugin you can track unsaved attributes (all attributes which have changed since the last model.save()) and then use them in the save-method of a model. Example (my usecase):
Backbone.View.extend({
bindings: {
"#name" : "name",
"#age" : "age"
},
initialize: function () {
this.model = new User();
this.model.fetch({
success: function (model, response, options) {
//this tells backbone.stickit to track unsaved attributes
model.startTracking();
}
});
},
render: function () {
this.$el.html(tmpl);
this.stickit();
return this;
},
onSaveUserToServer: function () {
//first argument: only unsaved attributes, second argument: tell backbone to PATCH
this.model.save(this.model.unsavedAttributes(), { patch: true });
});
});
In my controller, I send an object list into the view (index.cshtml)
return View(AdsPrevModel);
in my index.cshtml:
<div id ="ele">
<ul>
<li> name1<input id="a1" type="checkbox"/></li>
</ul>
</div>
when the user clicks the checkbox, I use jquery to know if the user checked the box or not:
My javascript file:
$('#ele :checkbox').click(function () {
if ($(this).is(':checked')) {
alert($(this).attr('id'));
} else {
alert('unchecked');
}
});
How can I get my AdsPrevModel into my js file?
I know I can do something like this:
In my html, add:
<input type="hidden" id="AdsPrevModel" value="#Model.AdsPrevModel" />
and in the js:
var adsPrevModel = JSON.parse(document.getElementById('AdsPrevModel').value);
Is there another option without adding a hidden input in my html?
Maybe something like the following in the js file:
var adsPrevModel = JSON.parse(Model.AdsPrevModel));
The best practise is
do an ajax call to that controller and that controller should return json results
return JSON( model ) ;
In the code you've shared there's nothing emitting the model to the client, so there's currently no direct way for the JavaScript code to access it.
Since you're binding the view to the model, the view can include it in various ways. It could be a series of hidden fields for the members of the model (not the model in its entirety, unless it can be represented as a string in its entirety). Something like this:
#Html.HiddenFor(x => x.SomeField)
#Html.HiddenFor(x => x.AnotherField)
This would create two hidden inputs for two fields on the model. Depending on how complex the model is, this could get cumbersome.
You might also emit the model to the JavaScript code directly in a similar fashion:
var someField = #Model.SomeField;
var anotherField = #Model.AnotherField;
Again, if the model is complex, this gets cumbersome quickly. Even if you try to build an actual JavaScript object from it:
var theModel = {
someField : #Model.SomeField,
anotherField : #Model.AnotherField
};
(Note also that I've seen Visual Studio get very confused when you mix razor syntax and JavaScript like this. Not so much in 2012 anymore, but a lot in 2010.)
You might use something like the JavaScriptSerializer to add a property on the model for a serialized version of itself. I've never done this before, but it should work. Something like this on the model:
public string SerializedCopy
{
get
{
return new JavaScriptSerializer().Serialize(this);
}
}
It might take some tweaking to get it to work, though.
Finally, a particularly clean option which only requires another request to the server would be to have another action which just returns the JSON version of that model. Something like this:
public ActionResult SomeActionName()
{
// get the model somehow, then...
return Json(AdsPrevModel);
}
Your JavaScript code would then just need to call this action to get the JSON object representing the whole model:
var theModel = {};
$.get('#Url.Action("SomeActionName", "SomeController")', function (data) {
// maybe do some error checking here?
theModel = data;
});
Then if your actual view isn't actually binding anything to the model then the action which returns that view doesn't need to fetch the model and supply it to the view. The JavaScript code would get the model by calling this other action which returns JSON data instead of a view.