2

This is a bit of a continuation of a previous question.

Now I'm trying to make a call to an AJAX enabled web service which I have defined within the ASP.NET MVC application (i.e. the MovieService.svc). But the service is never being called in my getMovies javascript function.

This same technique of calling the AJAX web service works ok if I try it in a non ASP.NET MVC application, so it makes me wonder if maybe the ASP MVC routes are interfering with things somehow when it tries to make the AJAX web service call.

Do you have any idea why my web service isn't getting called? Code below.

    <script src="<%= ResolveClientUrl("~/scripts/jquery-1.4.2.min.js") %>" type="text/javascript"></script>

    <script src="<%= ResolveClientUrl("~/scripts/grid.locale-en.js") %>" type="text/javascript"></script>

    <script src="<%= ResolveClientUrl("~/scripts/jquery-ui-1.8.1.custom.min.js") %>"
        type="text/javascript"></script>

    <script src="<%= ResolveClientUrl("~/scripts/jquery.jqGrid.min.js") %>" type="text/javascript"></script>

    <script type="text/javascript">
        var lastsel2;

        function successFunction(jsondata) {
            debugger
            var thegrid = jQuery("#editgrid");
            for (var i = 0; i < jsondata.d.length; i++) {
                thegrid.addRowData(i + 1, jsondata.d[i]);
            }
        }

        function getMovies() {
            debugger
            // ***** the MovieService#GetMovies method never gets called
            $.ajax({
                url: 'MovieService.svc/GetMovies',
                data: "{}",  // For empty input data use "{}",
                dataType: "json",
                type: "GET",
                contentType: "application/json; charset=utf-8",
                success: successFunction
            });
        }

        jQuery(document).ready(function() {
            jQuery("#editgrid").jqGrid({
                datatype: getMovies,
                colNames: ['id', 'Movie Name', 'Directed By', 'Release Date', 'IMDB Rating', 'Plot', 'ImageURL'],
                colModel: [
                  { name: 'id', index: 'Id', width: 55, sortable: false, hidden: true, editable: false, editoptions: { readonly: true, size: 10} },
                  { name: 'Movie Name', index: 'Name', width: 250, editable: true, editoptions: { size: 10} },
                  { name: 'Directed By', index: 'Director', width: 250, align: 'right', editable: true, editoptions: { size: 10} },
                  { name: 'Release Date', index: 'ReleaseDate', width: 100, align: 'right', editable: true, editoptions: { size: 10} },
                  { name: 'IMDB Rating', index: 'IMDBUserRating', width: 100, align: 'right', editable: true, editoptions: { size: 10} },
                  { name: 'Plot', index: 'Plot', width: 150, hidden: false, editable: true, editoptions: { size: 30} },
                  { name: 'ImageURL', index: 'ImageURL', width: 55, hidden: true, editable: false, editoptions: { readonly: true, size: 10} }
                ],
                pager: jQuery('#pager'),
                rowNum: 5,
                rowList: [5, 10, 20],
                sortname: 'id',
                sortorder: "desc",
                height: '100%',
                width: '100%',
                viewrecords: true,
                imgpath: '/Content/jqGridCss/redmond/images',
                caption: 'Movies from 2008',
                editurl: '/Home/EditMovieData/',
                caption: 'Movie List'
            });

            $("#bedata").click(function() {
                var gr = jQuery("#editgrid").jqGrid('getGridParam', 'selrow');
                if (gr != null)
                    jQuery("#editgrid").jqGrid('editGridRow', gr, { height: 280, reloadAfterSubmit: false });
                else
                    alert("Hey dork, please select a row");
            });            

        });

    </script>

    <h2>
        <%= Html.Encode(ViewData["Message"]) %></h2>
    <p>
        To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
            http://asp.net/mvc</a>.
    </p>
    <table id="editgrid">
    </table>
    <div id="pager" style="text-align: center;">
    </div>
    <input type="button" id="bedata" value="Edit Selected" />

Here's my RegisterRoutes code:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("*MovieService.svc*");

    routes.MapRoute(
        "Default",                                              // Route name
        "{controller}/{action}/{id}",                           // URL with parameters
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    );
}

Here's what my MovieService class looks like:

namespace jQueryMVC
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MovieService
    {
        // Add [WebGet] attribute to use HTTP GET
        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        public IList<Movie> GetMovies()
        {
            return Persistence.GetMovies();
        }

    }
}
Community
  • 1
  • 1
dcp
  • 54,410
  • 22
  • 144
  • 164
  • Can you show your routes? You probably need an IgnoreRoute for MovieService.svc – Joel May 14 '10 at 16:50
  • I've edited my response to show the routes. I added the IgnoreRoute for the MovieService, but it didn't change anything, the web service is still not called. – dcp May 14 '10 at 18:05
  • Could you post a prototype (interface) of the GetMovies function inside of MovieService.svc. The problem in your code is clear. I would be rewrite a little your code and make it more easy (and fix your main problem of cause). – Oleg May 14 '10 at 18:37
  • Interesting. Just tried this with a new test project and can confirm the behavior. Does not seem to be a routing issue since you can browse to the web service directly. I'll have to play around a bit when I get more time and see if I can figure this out. – Bradley Mountford May 14 '10 at 18:58
  • I hope my code will works in your environment. I had no problems with with routing, but `routes.IgnoreRoute("*MovieService.svc*")` seems me a good idea. If something will not work with MVC/WFC we can compare more parts from your and my projects. One more remark: how you could see my English is not good, so it you find some clear errors, please edit my answer and just fix these. – Oleg May 15 '10 at 00:42
  • dcp, if I do something I'll try to bring this till the end. I want, that you solve you problem. So you use VS2008. Which version of ASP.MVC you use 2.0 or 1.0? – Oleg May 15 '10 at 23:00
  • @Oleg - I appreciate your dedication :). I use the 1.0 of ASP.MVC. – dcp May 16 '10 at 00:45
  • You have a good opportunity to make update to 2.0! I helps you more because you have a high reputation. This means for me, that you helped a lot of people before. So you *deserve*, that somebody helps you if you have a problem. About 20% of persons for which I written an answer with the real solution of his problem don't answer me anything. I am sure, you knows this problem. But so is the life. I spending the most of my time to make money and for my family, but to help other people I find also important (I am not religious by the way) and I am not waiting more as a "thanks". So good luck, dcp! – Oleg May 16 '10 at 01:26

4 Answers4

11

Your main problem is that you use not absolute URLs in the ajax call. Wrong entries in web.config can make also problems. Moreover you use datatype: getMovies instead of datatype: 'json' and postData: yourData. The way with datatype as functions exist (see http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#function), but since jqGrid 3.6.5 you have more direct way inside of jsonReader to read the data returned from web server.

UPDATED: It seems to me that describing of editing features I'll make later and explain here just how to get JSON data and fill there inside of jqGrid.

First of all jqGrid can request itself the JSON data from the server. So we don’t need to make a separate jQuery.ajax call. You need only define a URL which point to the server and define some additional jQuery.ajax parameters which you prefer. You don’t post in your question the definition of the Movie class. So I define it myself like following

public class Movie {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Director { get; set; }
    public string ReleaseDate { get; set; }
    public string IMDBUserRating { get; set; }
    public string Plot { get; set; }
    public string ImageURL { get; set; }
}

You should remark, that Microsoft serialize DataTime type not as a readable date string but as a string /Date(utcDate)/, where utcDate is this number (see jQuery.param() - doesn't serialize javascript Date objects?). To make fewer problems at the beginning I define ReleaseDate as string.

Method IList<Movie> GetMovies() returns JSON data like an array of objects Movie. So jqGrid as a response to HTTP GET request receive from the MovieService.svc/GetMovies URL the data like following:

 [{"Id":1, "Name": "E.T.", "Director": "Steven Spielberg",...},{...},...]

I can say that it is not typical format of data, which are waiting jqGrid (compare with http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data). To be able to place the data inside of jqGrid we must define a jsonReader. So we do following

jQuery("#editgrid").jqGrid({
    url: '<%= Url.Content("~/MovieService.svc/GetMovies")%>',
    datatype: 'json',
    ajaxGridOptions: { contentType: "application/json" },
    jsonReader: { repeatitems: false, id: "Id", root: function(obj) { return obj; }},
    headertitles: true,
    sortable: true,
    colNames: ['Movie Name', 'Directed By', 'Release Date',
               'IMDB Rating', 'Plot', 'ImageURL'],
    colModel: [
        { name: 'Name', width: 250},
        { name: 'Director', width: 250, align: 'right' },
        { name: 'ReleaseDate', width: 100, align: 'right' },
        { name: 'IMDBUserRating', width: 100, align: 'right' },
        { name: 'Plot', width: 150 },
        { name: 'ImageURL', width: 55, hidden: true }
    ],
    pager: jQuery('#pager'),
    pginput: false,
    rowNum: 0,
    height: '100%',
    viewrecords: true,
    rownumbers: true,
    caption: 'Movies from 2008'
}).jqGrid('navGrid', '#pager', { add: false, edit: false, del: false, search: false });

REMARK: I remove from the example any sorting parameters, because in case of request of JSON data, the sorting parameter will be only send to server (some additional parameters append the server URL) and server must give back sorted data. For more information see description of prmNames parameter on http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options and description of sopt parameter on http://www.trirand.com/jqgridwiki/doku.php?id=wiki:singe_searching.

With respect of datatype: 'json' we define dataType: 'json' parameter of jQuery.ajax (don’t confuse the case inside of datatype parameter). The names of all fields inside of colModel we define exact the same as the field names inside our JSON objects. Some additional parameters viewrecords, rownumbers, sortable and headertitles are not very important in this example, I choosed there because 1) I like there and 2) I set rowNum: 0 to make possible the options rownumbers: true works correct and not show us negative row numbers started with -5 if rowNum: 5 like in your original example.

With ajaxGridOptions: { contentType: "application/json" } we define additional parameters which will be direct forwarded to the jQuery.ajax.

The most complex part of this example is

jsonReader: { repeatitems: false, id: "Id", root: function(obj) { return obj; }}

It defines that id of all rows have the name "Id" (see definition of the class Movie). "repeatitems: false" say that every data field we want identify by the field name (defined in colModel) instead of default definition per position. The definition of root is a little strange, but it defines how to find the root of rows inside of JSON data. Default format of JSON data is following

{
  total: "xxx", 
  page: "yyy", 
  records: "zzz",
  rows : [
    {id:"1", cell:["cell11", "cell12", "cell13"]},
    {id:"2", cell:["cell21", "cell22", "cell23"]},
      ...
  ]
}

and the root of rows are defined as root: "rows". So if the JSON data assigned to the variable res, the root can be returned as res.rows. To allow jqGrid to read our data we define jsonReader.root as a function (this feature exist since jqGrid 3.6.5 see http://www.trirand.com/jqgridwiki/doku.php?id=wiki:change#additions_and_changes). You can verify that this strange method work. The typical additional parameters page, total (lastpage) and records are not exist inside of our JSON data and they will be initialized as following page:0, total:1, records:0. So we are not able to make data paging. You can expand jsonReader with functions defining page, total and records (also as functions) like

jsonReader: {
    repeatitems: false,
    id: "Id",
    root: function (obj) { return obj; },
    page: function (obj) { return 1; },
    total: function (obj) { return 1; },
    records: function (obj) { return obj.length; }
}

which will complete our jsonReader. Then setting of rowNum: 0 will not more needed.

I showed this way only to show the flexibility of jqGrid. You should use described way only if you access a web server which you cannot change. jqGrid has features like paging, sorting and two kind of searching (more as filtering with WHERE in the corresponding SELECT) of data: simple and advanced. If we want have these nice features inside of jqGrid on our web pages we should define in Web Service an additional method like

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "jqGridGetTestbereiche?_search={_search}&page={page}&"+
                      "rows={rows}&sidx={sortIndex}&sord={sortDirection}&"+
                      "searchField={searchField}&searchString={searchString}&"+
                      "searchOper={searchOper}&filters={filters}")]
public jqGridTable jqGridGetMovies(
  int page, int rows, string sortIndex, string sortDirection,
  string _search, string searchField, string searchString,
  string searchOper, string filters)

where jqGridTable

public class jqGridTable
{
    public int total { get; set; }      // total number of pages
    public int page { get; set; }       // current zero based page number
    public int records { get; set; }    // total number of records
    public List<jqGridRow> rows { get; set; }
}
public class jqGridRow
{
    public string id { get; set; }
    public List<string> cell { get; set; }
}

Or if we want use the most compact form of data transferred from server to client then

// jsonReader: { repeatitems : true, cell:"", id: "0" }
public class jqGridTable {
    public int total { get; set; }          // total number of pages
    public int page { get; set; }           // current zero based page number
    public int records { get; set; }        // total number of records
    public List<List<string>> rows { get; set; }// first element in every row must be id of row.
}

(you can read more about this kind of data transfer on http://www.trirand.com/blog/jqgrid/jqgrid.html if you choose on the left tree part "Data Mapping" and then "Data Optimization")

P.S.: About jsonReader you can read more on http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data. One my old answer Mapping JSON data in JQGrid can be also interesting for you.

UPDATED 2: Because you don't mark the answer as accepted, you stay have some problems. So I created a new Project in Visual Studio 2010 which demonstrate what I written. You can download the source from http://www.ok-soft-gmbh.com/jqGrid/jQueryMVC.zip. Compare with your project, especially the part with full url as a parameter of jqGrid and a part of web.config which describes WCF service interface.

UPDATED 3: I use VS2010 not so long time. So I could very quickly downgrade this to VS2008. So almost the same code working working in Visual Studio 2008, but with ASP.NET MVC 2.0 you can download from http://www.ok-soft-gmbh.com/jqGrid/VS2008jQueryMVC.zip. The code in ASP.NET MVC 1.0 should be the same, but a GUID from the project file and some strings from Web.config should be patched (see http://www.asp.net/learn/whitepapers/aspnet-mvc2-upgrade-notes).

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • @Oleg - Yes, I am new at jqGrid but am trying hard to learn it, but it's very difficult to use and the examples are poor. I don't really understand your answer though. I already had this working with using datatype: 'json' and letting control flow to the controller. I was trying to get it to work with a call to the web service. That works in my non MVC project, but it doesn't work in MVC, which seems to indicate that MVC is messing things up somwhere. – dcp May 14 '10 at 19:07
  • @Oleg - I should qualify what I meant when I said the examples are poor. The JQGrid examples themselves are very good, but they are all in php, they don't have any in asp.net. I guess this is due to the fact that they want you to buy the commercial controls :). – dcp May 14 '10 at 19:16
  • I use jqGrid in a project written in ASP.NET MVC with a RESTfull WCF service which is a part of the same ASP.NET site. So we use the same technique. I play a little with my daughter and post you an example – Oleg May 14 '10 at 20:09
  • @Oleg - Thanks for the great help. I'm going to be trying out your solution very soon, I just have not had a chance yet because I've been busy with some other projects. I will let you know what I find. Again, I appreciate the detailed explanation. – dcp May 15 '10 at 12:30
  • @Oleg - Is this a VS 2010 solution? I only have VS 2008 so I cannot open the solution file :(. Anyway, you've spent enough time on this, I'll go ahead an accept the answer so you get your rep points. – dcp May 15 '10 at 22:38
  • @Oleg - Regarding Updated 3, thanks, I was able to run it just fine after I installed MVC 2.0. I think I get the basic idea of it. The other examples I looked at actually handled the paging by getting the parameters inside the controller method, like this: string sortColumn = (Request.Params["sidx"]);. One other question, you ever used jqGrid in non-ASP.net application with just regular web forms? I didn't find easy way to do such a thing, I guess it's geared more towards MVC pattern. Thanks again! – dcp May 17 '10 at 14:10
0

That is because the registered route in the global.asax, will not be recognizing this .svc file. it will try to search for that controller with the action getmovies and will fail. try out debugging using firebug. you can fix this by ignoring this route in the global.asax

Sundararajan S
  • 1,358
  • 2
  • 14
  • 24
  • It made no difference. I added routes.IgnoreRoute("*MovieService.svc*"); to the RegisterRoutes, and also tried it with routes.IgnoreRoute("MovieService.svc/GetMovies"). Neither worked. – dcp May 14 '10 at 18:03
  • did u try accessing the service from browser? – Sundararajan S May 14 '10 at 18:31
  • From what I can tell it works ok from the browser. Of course, we can't invoke WCF web methods from browser (unlike asmx web methods). – dcp May 14 '10 at 20:28
0

I encountered the same issue. I came to the conclusion that the routes were interfering with the service call. Have you tried Phil Haack's Route Debugger? It's saved my bacon a couple times.

In the ended, I created an endpoint off one of the controllers.

Chuck Conway
  • 16,287
  • 11
  • 58
  • 101
  • Yes, I just got through trying the route debugger and the route matched. As I said in my response to Oleg, I think MVC just hoses things up somewhere when trying to call the web service. Nice to know someone else had the same problem and that I'm not alone :). – dcp May 14 '10 at 19:08
0

Oleg,

Do you have the example you were talking about as i am working with jqgrid/asp.net mvc and a restful service and having a bugger of a time. It would help out seeing an example as i am at a wall. Thx

SEM