none
Problema raro con un helper en MVC 3 RRS feed

  • Pregunta

  • Hola, estoy intentando hacer una prueba muy simple para familiarizarme con la nueva versión 3 de MVC y su nuevo engine Razor. El caso es que hice un proyecto muy simple, una agenda de contactos en MVC 2, y al intentar pasarla a MVC 3 no me muestra bien la zona que genero con un helper, pero en la versión anterior me la muestra correctamente.

    Este es el código que tengo en el archivo _Layout.cshtml:

    <ul id="menu">
      @Html.MenuItem("Contactos", "Index", "Contact")
      @Html.MenuItem("Grupos", "Index", "Group")
      @Html.MenuItem("About", "About", "Home")
    </ul>
    

    Y me debería generar esto:

    <ul id="menu">
       <li class="selected"><a href="/">Contactos</a></li>
       <li><a href="/Group">Grupos</a></li>
       <li><a href="/Contact/About">Sobre mi</a></li>
    </ul>
    

    Pero se genera esto:

    <ul id="menu">
      &lt;li class=&quot;selected&quot;&gt;&lt;a href=&quot;/&quot;&gt;Contactos&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/Group&quot;&gt;Grupos&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/Home/About&quot;&gt;About&lt;/a&gt;&lt;/li&gt;
    </ul>
    

    Y este es el código del helper que estoy utilizando:

    public static string MenuItem(this HtmlHelper helper, string linkText, string actionName, string controllerName) {
      string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
      string currentActionName = (string)helper.ViewContext.RouteData.Values["action"];
    
      var sb = new StringBuilder();
    
      if (currentControllerName.Equals(controllerName, StringComparison.CurrentCultureIgnoreCase) && currentActionName.Equals(actionName, StringComparison.CurrentCultureIgnoreCase))
        sb.Append("<li class=\"selected\">");
      else
        sb.Append("<li>");
    
      sb.Append(helper.ActionLink(linkText, actionName, controllerName));
      sb.Append("</li>");
      return sb.ToString();
    }
    

    ¿Cómo podría solucionarlo?

    Muchas gracias.

    lunes, 11 de octubre de 2010 19:03

Respuestas

  • Hola,

    los bloques de salida de Razor como los que usas (@Html.Algo()) codifican automáticamente la salida en HTML para evitar inyecciones de script, al igual que lo hace MVC 2 cuando utilizas la construcción <%: Html.Algo() %> de ASP.NET 4.

    Para evitar que esto se produzca, prueba a modificar tu helper de forma que no retorne un string como ahora, sino un MvcHtmlString:

    public static MvcHtmlString MenuItem(...)
    {
       ...
       return MvcHtmlString.Create(sb.ToString());
    }

    De esta forma evitarás que se codifique la salida, y volverá a funcionarte como antes.

    Espero que te sea de ayuda.

    Saludos!


    José M. Aguilar
    Variable not found
    lunes, 11 de octubre de 2010 19:27
  • Buenas,

    creo que ya he dado con el tema. El código de tu vista tiene un par de problemillas, lo que unido a fallos del parser de Razor generan el error. Te comento lo que he hecho para que funcione:

    • he eliminado los espacios sobrantes en las llamadas a LabelFor. Si te fijas, tienes un espacio entre el nombre del método y los paréntesis, que hace reventar el parser.  Por tanto, todos ellos quedarían como @Html.LabelFor(model => model.Algo).
    • el último label hace referencia a una propiedad del modelo inexistente, debería ser @Html.LabelFor(model => model.GrupoId).
    • el dropdownlist debes especificar el segundo parámetro (la lista) como un enumerable tipado, así:
      ViewData["Grupos"] as IEnumerable<MvcRazorContactManager.Models.Grupo>

    Con esto no tendrás problema para echarlo a andar... o eso espero ;-)

    Saludos.


    José M. Aguilar
    Variable not found
    miércoles, 13 de octubre de 2010 17:24

Todas las respuestas

  • Hola,

    los bloques de salida de Razor como los que usas (@Html.Algo()) codifican automáticamente la salida en HTML para evitar inyecciones de script, al igual que lo hace MVC 2 cuando utilizas la construcción <%: Html.Algo() %> de ASP.NET 4.

    Para evitar que esto se produzca, prueba a modificar tu helper de forma que no retorne un string como ahora, sino un MvcHtmlString:

    public static MvcHtmlString MenuItem(...)
    {
       ...
       return MvcHtmlString.Create(sb.ToString());
    }

    De esta forma evitarás que se codifique la salida, y volverá a funcionarte como antes.

    Espero que te sea de ayuda.

    Saludos!


    José M. Aguilar
    Variable not found
    lunes, 11 de octubre de 2010 19:27
  • Muchas gracias por la respuesta, eso era.
    martes, 12 de octubre de 2010 6:31
  • Hola, tengo otra duda acerca de los helpers y es que el mismo código no me funciona cuando paso de MVC2 a la versión 3. Tengo este código para generar un contacto y una lista de grupos para asignarle uno de ellos:

     @Html.EditorFor(model => model.Contacto,
    					new { Model.Grupos })
    

    Pero me da este fallo en tiempo de ejecución:

    CS1502: The best overloaded method match for 'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)' has some invalid arguments
    ¿De qué puede ser?

    miércoles, 13 de octubre de 2010 7:54
  • Hola Zpecter, Contacto es del mismo tipo que Grupos?
    Para el correcto funcionamiento, y que otros usuarios se puedan beneficiar de la solucion de esta pregunta por favor marca las respuestas que te hayan ayudado como "Respuesta".
    Si la respuesta te ha sido util Votala.
    Mi Blog: Jtorrecilla
    Enlace a Faq de Winforms en Ingles Muy bueno
    miércoles, 13 de octubre de 2010 8:39
  • No, tengo contactos y grupos, y cada contacto pertenece a un grupo. En ese ejemplo que he puesto, estoy usando un view model, que lleva un contacto y una lista de los grupos, y tengo creada la plantilla Contacto.cshtml con estos datos:

    @model MvcRazorContactManager.Models.Contacto
    
      <script src="@Url.Content("/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
      <script src="@Url.Content("/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
      <script src="@Url.Content("/Scripts/MicrosoftMvcValidation.js")" type="text/javascript"></script>
    
      <p>
        @Html.LabelFor (model => model.Nombre)
        @Html.TextBoxFor(model => model.Nombre)
        @Html.ValidationMessageFor(model => model.Nombre)
      </p>
      <p>
        @Html.LabelFor (model => model.Apellidos)
        @Html.TextBoxFor(model => model.Apellidos)
        @Html.ValidationMessageFor(model => model.Apellidos)
      </p>
      <p>
        @Html.LabelFor (model => model.Telefono)
        @Html.TextBoxFor (model => model.Telefono)
        @Html.ValidationMessageFor (model => model.Telefono)
      </p>
      <p>
        @Html.LabelFor (model => model.Email)
        @Html.TextBoxFor (model => model.Email)
        @Html.ValidationMessageFor (model => model.Email)
      </p>
      <p>
        @Html.LabelFor(model => model.Grupo)
        @Html.DropDownList("GrupoId", new SelectList(ViewData["Grupos"] as IEnumerable, "Id", "Nombre", Model.GrupoId))
      </p>
    

    miércoles, 13 de octubre de 2010 8:49
  • Buenas,

    uufff, ni idea. De todas formas, prueba una cosilla rápida: en la llamada a tu helper incluye después de la arroba un paréntesis, y ciérralo al final, así dejarás claro a Razor qué es lo que quieres hacer. Hay veces que al parsear se lía con los símbolos ">" y "<":

    @(Html.EditorFor(model => model.Contacto, new { Model.Grupos }))

    Ya nos comentas si hay algún cambio.

    Saluds.


    José M. Aguilar
    Variable not found
    miércoles, 13 de octubre de 2010 14:41
  • Pues nada, ocurre exactamente lo mismo con paréntesis que sin ellos.
    miércoles, 13 de octubre de 2010 16:33
  • Buenas,

    creo que ya he dado con el tema. El código de tu vista tiene un par de problemillas, lo que unido a fallos del parser de Razor generan el error. Te comento lo que he hecho para que funcione:

    • he eliminado los espacios sobrantes en las llamadas a LabelFor. Si te fijas, tienes un espacio entre el nombre del método y los paréntesis, que hace reventar el parser.  Por tanto, todos ellos quedarían como @Html.LabelFor(model => model.Algo).
    • el último label hace referencia a una propiedad del modelo inexistente, debería ser @Html.LabelFor(model => model.GrupoId).
    • el dropdownlist debes especificar el segundo parámetro (la lista) como un enumerable tipado, así:
      ViewData["Grupos"] as IEnumerable<MvcRazorContactManager.Models.Grupo>

    Con esto no tendrás problema para echarlo a andar... o eso espero ;-)

    Saludos.


    José M. Aguilar
    Variable not found
    miércoles, 13 de octubre de 2010 17:24
  • Muchísimas gracias, ya está arreglado. La verdad que este fallo me estaba volviendo loco, porque en la vista de creación de grupo tenía un espacio en blanco entre el nombre del método y el paréntesis y ese sí que funcionaba, y lo de poner el tipo del enumerable ¿es obligatorio ahora? Porque tengo lo mismo hecho con MVC 2 y funciona perfectamente.
    miércoles, 13 de octubre de 2010 17:41
  • Holas,

    pues no sé, es posible que todas o parte de estas cuestiones se deban al estado "beta" del producto. El tema del espacio tras el nombre del método extensor, en todos los sitios donde he probado, casca. Lo del IEnumerable<T>, la verdad, no le veo demasiado sentido, pero es así (observa la definición del método en MSDN).

    Saludos!


    José M. Aguilar
    Variable not found
    miércoles, 13 de octubre de 2010 18:01
  • Esperemos que sea eso, que está en fase beta.

    Un saludo y muchas gracias de nuevo.

    miércoles, 13 de octubre de 2010 18:44