Infragistics grid's GET returning empty response - asp.net-mvc

I'm trying to use Infragistics' grid to display a list of items from my Database. I'm using code-first method with Entity Framework in an MVC application with Razor engine. Every thing is working fine in the view except the Infragistics grid.
Here is my home view:
#using Infragistics.Web.Mvc
#model IEnumerable<BusinessModel.Models.TestPlan>
#{
ViewBag.Title = "Home";
}
#( Html.Infragistics().Grid<BusinessModel.Models.TestPlan>(Model)
.AutoGenerateColumns(true)
.DataSourceUrl(Url.Action("igListTestPlan"))
.DataBind()
.Render())
Here is my controller:
[GridDataSourceAction]
public ActionResult igListTestPlan()
{
return View(service.getListTestPlan());
}
Using firebug I can clearly see that the request is sent with a status code "200 OK", but the response tab is empty. It also causes an error in the console (in infragistics.js):
Uncaught TypeError: Cannot read property 'length' of undefined
I guess it's because of the empty response.
What I tried:
Debugging my controller showed me that return View(service.getListTestPlan()); doesn't return an empty list: I have 3 valid items in.
I also tried Html.Infragistics().Grid<BusinessModel.Models.TestPlan>(Model__.ToList()) but nothing changed. Also Html.Infragistics().Grid(Model) tells me I've got invalid parameters
Thanks in advance.

I think I have a pretty good idea why you are getting this, happened to me as well.
The MVC wrappers provide defaults for the way the grid model handles data on the server (serializing the data source to an object with 'Records' of your data and supportive 'Metadata'). However, if you do that yourself since you don't define a key of your own you are stuck with a default key 'Records' that is used to filter the response and since it's not there..well you get 'undefined' data fed to the grid :)
So solutions:
1) Wrap your response and define matching key using the "ResponseDataKey" property of the grid. I am suggesting this because as far as I recall it's a good practice to wrap responses in a single object - think there were some security implications.
2) If you don't feel like doing this and just want to get it working now then set the "ResponseDataKey" to empty string ("" will do) so your response will be properly filtered(or rather not).
On the second part of binding the grid directly to model data in the view - you are correctly getting the error as far as I see. The DataSource property explicitly states the source must implement IQueryable instead of IEnumerable. Slap a .AsQueryable() in there and that should work fine as well.
Let me know if this helps :)

Related

Is returning different view with object bad practice in ASP.net MVC 5?

I need to pass objects between ActionMethods and Views in an ASP.net MVC 5 app.
I'm using it for a multi page signup - and for a multi page payment.
Is this bad practice? I haven't seen a good way to pass objects between different controllers.
Code:
public ActionResult Join1()
{
//
return View("Join2", MyObject);
}
[HttpPost]
public ActionResult Join2(MyObject MyObj)
{
//manipulate object
//return
}
It seems to be an effective way to do it - though I haven't seen many people do it this way. I haven't seen objects being passed between action methods much at all.
Is there a flaw in this approach, a better way of passing models between Views - or should each ActionMethod stick to passing simple data with, say, TempData instead of objects?
Why haven't I seen any sample projects doing things like this?
I've seen return RedirectToAction("act"); plenty - but that is Get and passing an object in a URI is limiting - and I don't want users to be able to manipulate or see the data being passed.
thx.
Unless I have misunderstood the description, your code is not doing what you think it's doing. That first return View("Join2", MyObject); statement is not executing the second ActionMethod, it is only passing your data into the View that happens to have the same name as the second method. Therefore the code implied by //manipulate object will not run before that View is rendered and sent back to the user.
Assuming the View file Join2.cshtml exists, and it contains the default #using (Html.BeginForm()), then users submitting the form will cause the Join2 Action to be executed and the same view rendered with the manipulated data - unless, of course, you add another return View() statement that names a different View.
The reason you haven't seen this done much is that the MVC convention is to have a View named the same as the ActionMethod, this makes the code slightly simpler and also much easier for other ASP.NET developers to understand because it is what they are expecting to see.
If you want the form rendered by each View to then execute a different ActionMethod when it is posted back, the place to do that is in the View code, where Html.BeginForm() has several overloads that allow you to do just that, e.g. in Join.cshtml you could write:
#using (Html.BeginForm("Join2", "JoinController"))
{
// form fields and stuff
}
// Produces the following form element
// <form action="/JoinController/Join2" action="post">
To address the final part of your question, "I don't want users to be able to manipulate or see the data being passed", sorry to say it but your proposed code doesn't prevent that: users can see the data in the web form, before it is ever posted back to the Join2 method; they can manipulate the data by sending an HTTP POST containing any data they want back to the Join2 method.
If you absolutely, positively need to actually execute Join2() from within Join(), before anything is passed back to the user, then you can call it just like any other C# method:
var myResult = Join2(MyObject);
Then you have an ActionResult object that you can manipulate or return straight to the browser. But why you would want to do this, is beyond me.

How to access data from differnt partial view after POST

I have 3 partialviews with 3 viewmodels on page:
List of accounts
Modal popup (you can modify multiple accounts here)
Search panel
I want to refresh 1. after doing POST on 2. This is straightforward, but what if I want to keep results I got after using Search Panel?
I can do this in 2 ways but both seems bad (correct me if I am wrong).
First (the one I chose and works) is to store viewmodel used in 3. in TempData. I do Search (POST) and save passed viewmodel in TempData. Then whenever I do POST on different partialview I can refresh 1. using data(search parametrs) from TempData.
private const string SearchDataKey = "SearchData";
[HttpGet]
public PartialViewResult RefreshData()
{
if (TempData[SearchDataKey] != null)
return PartialView("AccountListView", PrepareAccountListViewModelForSearchData(TempData[SearchDataKey] as AccountSearchViewModel));
else
return PartialView("AccountListView", PrepareAccountListViewModel());
}
and saving ViewModel:
public PartialViewResult Search(AccountSearchViewModel searchParameters)
{
...
TempData[SearchDataKey] = searchParameters;
return PartialView("AccountListView", databaseAccountListViewModel);}
Second approach is to always POST "big" viewmodel with all 3 viewmodels. This way I will have data from Search's viewmodel but I will send many not needed information instead just Modal Popup's viewmodel which I need to call procedure.
I asked few MVC folks with better experience and they said they never had to store viewmodel in TempData but it still seems more reasonable than having 1 Big form and passing everything in every POST.
Do you know any better ways to handle this or which one is correct?
PS. Topic had "Best Practice" but was removed cause of warning message. I hope asking about opinion is still allowed on SO.
PS2. Most of my POSTs & GETs after initial load are through Ajax.
I do Search (POST)
This seems semantically incorrect to me. Searching is an action that shouldn't modify any state on the server. So using GET seems more appropriate. And when you use GET you have the benefit that all parameters are already present in the query string and thus preserved upon successive POST actions (like modifying an account in your case). So your RefreshData action could take the AccountSearchViewModel as parameter and the model binder will take care of the rest.

dynamically generating an Enumerable object of a specific type from a string representation of the type

I'm trying to design a solution in MVC in which a string representation of a class is passed to the controller which should then build a grid with all the data belonging to that class in the DB. (I'm using an ORM to map classes to tables).
//A method in the Model that populates the Item Property
foreach (MethodInfo method in sDRMethods)
{
if (method.Name.Contains(_domainTable))
{
Items = method.Invoke(repositoryObject, null);
break;
}
}
//View uses this Items property of the Model to populate the grid.
public object Items;
//_domainTable is the name of the table/class (in string format).
//repositoryObject is the object that has methods to return IEnumerable<class> collection object of each type.
The problem I have is that I do not know how to cast the "Items" property in my view to iterate through it and build a grid.
I have tried using the "http://mvcsharp.wordpress.com/2010/02/11/building-a-data-grid-in-asp-net-mvc/" but the generic extension method is expecting to know the specific type that it should work with.
I would prefer to use MVC but it looks like I cannot easily have this working(which is very hard to believe).
I really don't like the sound of what you are trying to do. Why convert the table to a string?
The only time you would convert to a string, is when the view gets rendered. And that, in most cases, should be left to the MVC framework.
The code you mentioned uses an HtmlTextWriter which is fine, because it will render straight to the response.
However, it sounds as if you are trying to reinvent the wheel by rendering everything to a string, rather than leaving that to the framework.
Note that in MVC the views are just templates for rendering strings, which is, if I have understood you, exactly what you need.
So, if I have remotely understood what you are trying to do, and it is a big if because your post is not clear, you should pass your class to view as part of the strongly typed model, and then write some basic design logic into the view.
If I am right, which is not certain, I think you have misunderstood how MVC works.
Have a look at a few examples of how to use views to render the data in a model. The model can be any class, it can be an IEnumerable, a list, whatever, and you can use foreach loops in the view to render out what you want, how you want it.
In this sense, MVC is very different to writing custom controls in plain vanilla ASP.NET.
Thanks for your reply awrigley.
The requirement is quite simple. I perhaps made it sound awfully complex in my post.
On an Index view, I have to populate a dropdownlist with all the tables of the application that are system lookup. The "Admin" of the app, selects an item from the dropdownlist which should show the contects of that table in a grid so that the admin can perform CRUD operations using that grid.
What I am trying to do is, pass the selected item (which is the name of the table) to the controller which in turn passes it to the ViewModel class. This class uses reflection to invoke (code shown in my original question) the right method of a repository which has got methods like:
public IEnumerable GetAllTable1Data()
{
.....
}
The problem I have is that when I invoke the method, it returns a type "object" which I cannot cast to anything specific because I don't know the specific type that it should be cast to. When this object is passed to the view, the grid is expecting an IEnumerable or IEnumerable but I do not know this information. I am not able to do this:
(IEnumerable)method.Invoke(repositoryObject, null)
I get: cannot cast IEnumerable to IEnumerable
I (kind of) have the grid now displaying but I am using a Switch statement in the view that goes:
Switch(SLU_Type)
{
case "SLU_Table1": Html.Grid((IEnumerable)Model.Items);
case "SLU_Table2": Html.Grid((IEnumerable)Model.Items);
.....
}
I don't like this at all, it feels wrong but I just cannot find a decent way!
I could have partial views for each of the system look up tables but for that I'll have to add around 30 partial views with almost exactly same code for the Action & View. This does not seem right either!
Hopefully, this gives you a better understanding of what I'm trying to achieve.

ASP.NET MVC auto-binds a refreshed model when ModelState is invalid on HttpPost

I'm working on an ASP.NET MVC2 app. I've come to realize a very surprising, yet amazing thing that MVC does behind the scenes having to do with the ModelState and model binding. I have a ViewModel which has a whole bunch of data - some fields being part of a form while others are simply part of the UI. On HttpPost, my Action method uses the DefaultModelBinder which attempts to bind the whole model, but only fields which were part of the form are successfully deserialized - all others remain null. That's fine and understandable. If the ModelState is invalid, I need to refresh the model from the db and bind those particular form fields before returning to the same edit view to display those associated ModelState validation errors.
Here's where my amazement and curiosity comes. It was my assumption that in order for me to bind the form fields with the refreshed model, I needed to make a call to either UpdateModel() or TryUpdateModel<>(), passing in the newly refreshed model. For example:
[HttpPost]
public ActionResult EditDetail(EditDetailItemModel model)
{
if (model.IsValid)
{
// Save the results to the db
return RedirectToAction(...)
}
// Can't simply "return View(model)". Not all fields in EditDetailItemModel
// were part of the form - thus they returned null. Have to refresh
// model from the db.
var refreshedModel = RefreshModelFromDB();
// Is this line necessary?????
TryUpdateModel<EditDetailItemModel>(refreshedModel);
return View(refreshedModel);
}
But, what I found was that if I simply returned refreshedModel to the view WITHOUT making a call to TryUpdateModel<>(), the refreshed model was automatically bound with the form field values posted!! Hence, the TryUpdateModel<>() is not needed here!
The only way I can make any sense of it is that since the ModelState is in an invalid state, once I returned the view with the refreshed model, the "MVC rendering engine" looped through the ModelState errors and bound those property values with my refreshed model. That is simply AWESOME! But, I want proof as to this assumption. I can't find documentation regarding this anywhere on the web. Can anyone either confirm my hypothesis of WHY/HOW this AWESOME auto binding behavior is occuring and/or educate me as to why/how it's happening, hopefully backed up with some online documentation links so I understand more fully what's going on under the covers?
public ActionResult EditDetail(EditDetailItemModel model)
That line will perform model binding. Think of ActionMethod parameters as always being populated by a call to UpdateModel.
You are not seeing refreshedModel's values in the view, you are seeing the ModelState entries and values from EditDetailItemModel.

DropDownListFor does not set selected value

I have a view that is displaying a Drop down list using the HTML helper DropDownListFor
<%: Html.DropDownListFor(Function(model) model.Manufacturer, New SelectList(Model.ManufacturerList, Model.Manufacturer))%>
My controller is passing the View a ViewModel containing the Manufacturer and the ManufacturerList
Function Search(ByVal itemSrch As ItemSearchViewModel) As ActionResult
'some code mapping and model code'
Return View(itemSrch)
End Function
My ViewModel is nothing crazy just fills the ManufacturerList with a list of string values and then the Manufacturer property is just a string value containing the selected value from the drop down list.
Public Property Manufacturer As String
Public Property ManufacturerList() As List(Of String)
I'm having an issue with the view setting the selected value on the drop down list if we are reloading the Search View. I've checked the View Model (ItemSearchViewModel) when it comes into the Search function and the Manufacturer is populated with the proper selected value and successfully passes that value back to the Search View. At some point the data passed to the view doesn't seem to populate the selected value, was wondering if anyone had some ideas on why this is happening and what I can do to fix it.
Thanks
Didn't get much for answers on this so started digging into how I could fix this somewhat easily. Also in my research this seemed to be a common problem for many people with the DropDownListFor HTML Helper. I figured there had to be a way to get this working since I knew the selected value property from my ViewModel was actually getting passed to the View. So javascript and jQuery to the rescue, I ended up trying to set the selected value using the jQuery .ready function and was able to successfully get this to work. So here's my jQuery fix:
$(document).ready(function() {
$("#Manufacturer").val("<%: Model.Manufacturer %>");
});
For sake of making this easy to follow I used the full .ready syntax, or you can just use $(function () { 'code' });
If you want to solve this without using jQuery you can just use basic javascript syntax as well, it'll look something like this:
document.getElementByID("Manufacturer").Items.FindByValue("<%: Model.Manufacturer %>").Selected = true;
If you using the plain javascript call make sure to call this when the page is done loading data to the dropdownlist.
In either case all this code is doing is setting the selected value on the drop down list on the client side from the passed in Manufacturer value from the Model.
If you have any other ways to solve this problem please let me know, but this is working for me and hopefully it'll help someone else with their problem.
Thank,
I've done a similar quick-fix in JQuery today to fix this behaviour too :
$(document).ready(function() {
$(".wrapper<%: Model.Language %> option[value=<%: Model.Language %>]").attr("selected","true");
});
Though I could share it with others if needed.
I'd still like to see a real patch to this problem so many persons seems to have in a different way, maybe with an extension method of the already existing DropDownListFor Html.helper method.

Resources