Esto es un poco la continuación de una pregunta anterior.
Ahora estoy tratando de hacer una llamada a un servicio web habilitado para AJAX que he definido dentro de la aplicación ASP.NET MVC (es decir, el MovieService.svc
). Pero el servicio nunca se llama en mi getMovies
función javascript.
Esta misma técnica de llamar al servicio web AJAX funciona bien si lo pruebo en una aplicación que no sea ASP.NET MVC, por lo que me pregunto si tal vez las rutas ASP MVC están interfiriendo con las cosas de alguna manera cuando intenta realizar la llamada al servicio web AJAX. .
¿Tiene alguna idea de por qué no se llama a mi servicio web? Código a continuación.
<script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/scripts/jquery-1.4.2.min.js") %>" type="text/javascript"></script>
<script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/scripts/grid.locale-en.js") %>" type="text/javascript"></script>
<script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/scripts/jquery-ui-1.8.1.custom.min.js") %>"
type="text/javascript"></script>
<script src="https://stackoverflow.com/questions/2835957/<%=%20ResolveClientUrl("~/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" />
Aquí está mi código de RegisterRoutes:
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
);
}
Así es como se ve mi clase MovieService:
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();
}
}
}
Su principal problema es que no utiliza URL absolutas en el ajax
llamar. Entradas incorrectas en web.config
puede hacer también problemas. Además usas datatype: getMovies
en vez de datatype: 'json'
y postData: yourData
. el camino con datatype
como funciones existen (ver http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#function), pero desde jqGrid 3.6.5 tienes una forma más directa dentro de jsonReader
para leer los datos devueltos por el servidor web.
ACTUALIZADO:
Me parece que describiré las funciones de edición que haré más adelante y explicaré aquí cómo obtener datos JSON y llenarlos dentro de jqGrid.
En primer lugar, jqGrid puede solicitar los datos JSON del servidor. Así que no necesitamos hacer un separado jQuery.ajax
llamar. Solo necesita definir una URL que apunte al servidor y definir algunos adicionales jQuery.ajax
parámetros que prefiera. No publicas en tu pregunta la definición de la Movie
clase. Así que lo defino yo mismo como siguiente
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; }
}
Deberías comentar que Microsoft serializa DataTime
escriba no como una cadena de fecha legible sino como una cadena /Date(utcDate)/
, donde utcDate
es este número (ver jQuery.param() – ¿no serializa objetos de fecha de javascript?). Para hacer menos problemas al principio defino ReleaseDate
como cuerda.
Método IList<Movie> GetMovies()
devuelve datos JSON como una matriz de objetos Movie
. Entonces jqGrid como respuesta a HTTP GET
solicitar recibir de la MovieService.svc/GetMovies
Dirija los datos de la siguiente manera:
[{"Id":1, "Name": "E.T.", "Director": "Steven Spielberg",...},{...},...]
Puedo decir que no es el formato típico de datos, que están esperando jqGrid (comparar con http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data). Para poder colocar los datos dentro de jqGrid debemos definir un jsonReader
. Entonces hacemos lo siguiente
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 });
OBSERVACIÓN: elimino del ejemplo cualquier parámetro de clasificación, porque en caso de solicitud de datos JSON, el parámetro de clasificación solo se enviará al servidor (algunos parámetros adicionales agregan la URL del servidor) y el servidor debe devolver los datos ordenados. Para más información ver descripción de prmNames
parámetro en http://www.trirand.com/jqgridwiki/doku.php?id=wiki:opciones y descripción de sopt
parámetro en http://www.trirand.com/jqgridwiki/doku.php?id=wiki:singe_searching.
Con respeto de datatype: 'json'
definimos dataType: 'json'
parámetro de jQuery.ajax
(no confunda el caso dentro de datatype
parámetro). Los nombres de todos los campos dentro de colModel
definimos exacto lo mismo que los nombres de campo dentro de nuestros objetos JSON. Algunos parámetros adicionales viewrecords
, rownumbers
, sortable
y headertitles
no son muy importantes en este ejemplo, elegí allí porque 1) me gusta allí y 2) establecí rowNum: 0
para hacer posibles las opciones rownumbers: true
funciona correctamente y no nos muestra los números de fila negativos que comienzan con -5 si rowNum: 5
como en tu ejemplo original.
Con ajaxGridOptions: { contentType: "application/json" }
definimos parámetros adicionales que serán directo reenviado a la jQuery.ajax
.
La parte más compleja de este ejemplo es
jsonReader: { repeatitems: false, id: "Id", root: function(obj) { return obj; }}
Define que el id de todas las filas tenga el nombre “Id” (ver definición del class Movie
). “repeatitems: false
“digamos que cada campo de datos que queremos identificar por el nombre del campo (definido en colModel
) en lugar de la definición predeterminada por posición. La definición de root
es un poco extraño, pero define cómo encontrar el raíz de filas dentro de los datos JSON. El formato predeterminado de los datos JSON es el siguiente
{
total: "xxx",
page: "yyy",
records: "zzz",
rows : [
{id:"1", cell:["cell11", "cell12", "cell13"]},
{id:"2", cell:["cell21", "cell22", "cell23"]},
...
]
}
y la raíz de las filas se definen como root: "rows"
. Entonces, si los datos JSON asignados a la variable res
, la raíz se puede devolver como res.rows
. Para permitir que jqGrid lea nuestros datos, definimos jsonReader.root
como una función (esta característica existe desde jqGrid 3.6.5 ver http://www.trirand.com/jqgridwiki/doku.php?id=wiki:cambio#adiciones_y_cambios). Puedes comprobar que este extraño método funciona. Los parámetros adicionales típicos page
, total
(lastpage
) y records
no existen dentro de nuestros datos JSON y se inicializarán de la siguiente manera page:0, total:1, records:0
. Así que no podemos hacer paginación de datos. Puedes ampliar jsonReader
con funciones que definen page
, total
y records
(también como funciones) como
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; }
}
que completará nuestro jsonReader. Entonces ajuste de rowNum: 0
no será más necesario.
Mostré esta forma solo para mostrar la flexibilidad de jqGrid. Debe usar la forma descrita solo si accede a un servidor web que no puede cambiar. jqGrid tiene características como paginación, clasificación y dos tipos de buscando (más como filtrar con DONDE en el SELECT correspondiente) de datos: simple y avanzado. Si queremos tener estas características agradables dentro de jqGrid en nuestras páginas web, debemos definir en el servicio web un método adicional como
[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)
donde 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; }
}
O si queremos usar la forma más compacta de datos transferidos del servidor al cliente, entonces
// 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.
}
(puede leer más sobre este tipo de transferencia de datos en http://www.trirand.com/blog/jqgrid/jqgrid.html si elige en la parte izquierda del árbol “Mapeo de datos” y luego “Optimización de datos”)
PD: Acerca de jsonReader puedes leer más en http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data. Una de mis respuestas anteriores Mapear datos JSON en JQGrid también puede ser interesante para usted.
ACTUALIZADO 2: Debido a que no marcas la respuesta como aceptada, te quedas con algunos problemas. Así que creé un nuevo Proyecto en Visual Studio 2010 que demuestra lo que escribí. Puedes descargar la fuente de http://www.ok-soft-gmbh.com/jqGrid/jQueryMVC.zip. Compare con su proyecto, especialmente la parte con la URL completa como parámetro de jqGrid y una parte de web.config que describe la interfaz de servicio de WCF.
ACTUALIZADO 3: Uso VS2010 no hace mucho tiempo. Entonces podría degradar esto rápidamente a VS2008. Entonces, casi el mismo código funciona en Visual Studio 2008, pero con ASP.NET MVC 2.0 puede descargarlo desde http://www.ok-soft-gmbh.com/jqGrid/VS2008jQueryMVC.zip. El código en ASP.NET MVC 1.0 debe ser el mismo, pero se debe parchear un GUID del archivo del proyecto y algunas cadenas de Web.config (consulte http://www.asp.net/learn/whitepapers/aspnet-mvc2-upgrade-notes).
.
¿Puedes mostrar tus rutas? Probablemente necesite un IgnoreRoute para MovieService.svc
– Joel
14 mayo, 2010 a las 16:50
He editado mi respuesta para mostrar las rutas. Agregué IgnoreRoute para MovieService, pero no cambió nada, el servicio web aún no se llama.
– dcp
14 mayo, 2010 a las 18:05
¿Podría publicar un prototipo (interfaz) de la función GetMovies dentro de MovieService.svc? El problema en tu código es claro. Reescribiría un poco su código y lo haría más fácil (y solucionaría su principal problema de causa).
– Oleg
14 mayo, 2010 a las 18:37
Interesante. Acabo de probar esto con un nuevo proyecto de prueba y puedo confirmar el comportamiento. No parece ser un problema de enrutamiento ya que puede navegar directamente al servicio web. Tendré que jugar un poco cuando tenga más tiempo y ver si puedo resolver esto.
–Bradley Mountford
14 mayo, 2010 a las 18:58
Espero que mi código funcione en su entorno. No tuve problemas con el enrutamiento, pero
routes.IgnoreRoute("*MovieService.svc*")
me parece una buena idea. Si algo no funciona con MVC/WFC, podemos comparar más partes de sus proyectos y los míos. Una observación más: cómo puede ver que mi inglés no es bueno, por lo que si encuentra algunos errores claros, edite mi respuesta y simplemente corríjalos.– Oleg
15 mayo 2010 en 0:42