2012-04-04 9 views
6

Tengo un modelo de vista con una propiedad de filtro que tiene muchas propiedades que utilizo para filtrar los datos de miCómo crear un ActionLink con propiedades en la vista del modelo

Ejemplo:

class MyViewModel : IHasFilter 
{ 
    public MyData[] Data { get; set; } 
    public FilterViewModel Filter { get; set; } 
} 

class FilterViewModel 
{ 
    public String MessageFilter { get; set; } 
    //etc. 
} 

Esto funciona bien cuando usando mi Vista. Puedo configurar las propiedades de Model.Filter y se pasan al controlador. Lo que intento hacer ahora es crear un ActionLink que tenga una cadena de consulta que funcione con el formato anterior.

La cadena de consulta generada por mi Vista desde arriba se ve así:

http://localhost:51050/?Filter.MessageFilter=Stuff&Filter.OtherProp=MoreStuff 

necesito para generar una ActionLink en una vista diferente para cada fila en una cuadrícula que se dirige a la opinión anterior.

que he intentado:

Html.ActionLink(
    item.Message, 
    "Index", 
    "Home", 
    new { Filter = new { MessageFilter = item.Message, }, }, 
    null); 

También probé establecer el argumento routeValues a:

new MyViewModel { Filter = new FilterViewModel { MessageFilter = item.Message, }, }, 

Pero éstas no generan la cadena de consulta como la de arriba.

+0

+1 una buena pregunta –

+0

gracias por la edición; Refactoré la respuesta y en el reemplazo olvidé agregar el 'prefijo' –

+0

@AndrasZoltan No hay problema. – DaveShaw

Respuesta

1

Se puede crear una instancia de un RouteValueDictionary FilterViewModel y luego usar ToDictionary en que para pasar a otro RouteValues ​​con todas las llaves con el prefijo 'Filter.'.

Tomando aún más, se podía construir un override especial de RouteValueDictionary que acepta un prefijo (por lo tanto haciendo que sea más útil para otros escenarios):

public class PrefixedRouteValueDictionary : RouteValueDictionary 
{ 
    public PrefixedRouteValueDictionary(string prefix, object o) 
    : this(prefix, new RouteValueDictionary(o)) 
    { } 

    public PrefixedRouteValueDictionary(string prefix, IDictionary<string, object> d) 
    : base(d.ToDictionary(kvp=>(prefix ?? "") + kvp.Key, kvp => kvp.Value)) 
    { } 
} 

Con que ahora se puede hacer:

Html.ActionLink( 
    item.Message, 
    "Index", 
    "Home", 
    new PrefixedRouteValueDictionary("Filter.", 
    new FilterViewModel() { MessageFilter = item.Message }), 
    null); 

La advertencia al respecto, sin embargo, es que los métodos Add, Remove, TryGetValue y this[string key] no se modifican para tener en cuenta el prefix. Esto se puede lograr definiendo las versiones new de esos métodos, pero dado que no son virtuales, solo funcionarían de las personas que llaman que saben que están hablando con un PrefixedRouteValueDictionary en lugar de un RouteValueDictionary.

+0

Esto fue similar a mi pensamiento inicial también. Una advertencia adicional es que solo puede tener un prefijo por llamada al método ActionLink y todos los elementos tendrían que tener el prefijo. Actualicé mi respuesta con otra posible solución según las necesidades exactas y el pensamiento que pueda interesarle. Aclamaciones. – Craig

2

Pregunta interesante (+1). Supongo que el objetivo es utilizar el enlace de modelo predeterminado para vincular los parámetros de la cadena de consulta a los parámetros Action.

Fuera de la caja no creo que el método ActionLink haga esto por usted (por supuesto, no hay nada que le impida rodar el suyo). Mirando en el reflector podemos ver que cuando se agrega el object al RouteValueDictionary, solo se agregan pares de valores clave. Este es el código que agrega los pares de valores clave y, como puede ver, no se cruzan las propiedades del objeto.

foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values)) 
{ 
    object obj2 = descriptor.GetValue(values); 
    this.Add(descriptor.Name, obj2); 
} 

Así que para su objeto

var values = new { Filter = new Filter { MessageFilter = item.Message } } 

la clave que se añade es Filter y el valor es su Filter objeto que evaluará al nombre completo de su tipo de objeto.

El resultado de esto es Filter=Youre.Namespace.Filter.

Editarsolución posible en función de sus necesidades exactas Método


Extensiónhace el trabajo

Note que usa los métodos marco estático ExpressionHelper y ModelMetadata (que también son utilizados por los ayudantes existentes) para determinar los nombres apropiados que el el encuadernador de modelo predeterminado comprenderá y valorará la propiedad, respectivamente.

public static class ExtentionMethods 
{ 
    public static MvcHtmlString ActionLink<TModel, TProperty>(
     this HtmlHelper<TModel> helper, 
     string linkText, 
     string actionName, 
     string controllerName, 
     params Expression<Func<TModel, TProperty>>[] expressions) 
    { 
     var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext); 

     var url = urlHelper.Action(actionName, controllerName); 

     if (expressions.Any()) 
     { 
      url += "?"; 

      foreach (var expression in expressions) 
      { 
       var result = ExpressionHelper.GetExpressionText(expression); 

       var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, helper.ViewData); 

       url = string.Concat(url, result, "=", metadata.SimpleDisplayText, "&"); 
      } 

      url = url.TrimEnd('&'); 
     } 

     return new MvcHtmlString(string.Format("<a href='{0}'>{1}</a>", url, linkText)); 
    } 
} 

modelos de ejemplo

public class MyViewModel 
{ 
    public string SomeProperty { get; set; } 

    public FilterViewModel Filter { get; set; } 
} 

public class FilterViewModel 
{ 
    public string MessageFilter { get; set; } 
} 

acción

public ActionResult YourAction(MyViewModel model) 
{ 
    return this.View(
     new MyViewModel 
     { 
      SomeProperty = "property value", 
      Filter = new FilterViewModel 
      { 
       MessageFilter = "stuff" 
      } 
     }); 
} 

de uso

Se puede agregar cualquier cantidad de propiedades de su modelo de vista a la cadena de consulta a través del último parámetro params del método.

@this.Html.ActionLink(
    "Your Link Text", 
    "YourAction", 
    "YourController", 
    x => x.SomeProperty, 
    x => x.Filter.MessageFilter) 

marcado

<a href='/YourAction/YourController?SomeProperty=some property value&Filter.MessageFilter=stuff'>Your Link Text</a> 

En lugar de utilizar string.Format podría utilizar TagBuilder, la cadena de consulta debe ser codificado para ser aprobado de forma segura en una URL y este método de extensión necesitaría una validación adicional, pero Creo que podría ser útil. Tenga en cuenta también que, aunque este método de extensión está diseñado para MVC 4, podría modificarse fácilmente para las versiones anteriores. No me di cuenta de que una de las etiquetas MVC era para la versión 3 hasta ahora.

+0

* por supuesto, no hay nada que te impida rodar tu propio * - y ¿cómo podría hacer eso? :) Y tienes razón, estaba viendo el "Filter = Your.Nam ...". – DaveShaw

+0

@DaveShaw, aunque no esté de acuerdo, siento que esta es una respuesta valiosa a su pregunta. aclamaciones. – Craig

+0

@DaveShaw estaba pensando en esto en mi camino a casa desde el trabajo y lo apuñaló. Esta solución podría madurar a medida que trabajas con ella, pero creo que podría ser útil. – Craig

Cuestiones relacionadas