2012-04-04 9 views
6

Ho un ViewModel con una proprietà Filter che ha molte proprietà che uso per filtrare i miei datiCome creare un ActionLink con proprietà per il Model View

Esempio:

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

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

Questo funziona bene quando usando la mia vista. Posso impostare le proprietà di Model.Filter e vengono passati al controller. Quello che sto cercando di fare ora è creare un ActionLink con una stringa di query che funzioni con il formato sopra.

La stringa di query generata dal mio punto di vista dall'alto assomiglia a questo:

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

ho bisogno di generare un ActionLink in una diversa visualizzazione per ogni riga in una griglia che va alla vista sopra.

ho provato:

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

Ho anche provato a installare l'argomento routeValues a:

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

Ma questi non generare la stringa di query come sopra uno.

+0

+1 una buona domanda –

+0

grazie per la modifica; Ho rifattorizzato la risposta e nella sostituzione ho dimenticato di aggiungere il 'prefisso'! –

+0

@AndrasZoltan Nessun problema. – DaveShaw

risposta

1

Si può creare un RouteValueDictionary da un'istanza FilterViewModel e quindi utilizzare ToDictionary da quello di passare ad un altro RouteValues ​​con tutte le chiavi prefisso 'Filter.'.

Prendendo ulteriormente, si potrebbe costruire una sostituzione speciale di RouteValueDictionary che accetta un prefisso (rendendo quindi più utile per altri scenari):

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 che ora si può fare:

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

L'avvertenza, tuttavia, è che i metodi Add, Remove, TryGetValue e this[string key] non vengono modificati per tenere conto dello prefix. Ciò può essere ottenuto definendo le versioni new di tali metodi, ma poiché non sono virtuali, funzionano solo da chiamanti che sanno che stanno parlando con uno PrefixedRouteValueDictionary anziché uno RouteValueDictionary.

+0

Questo era simile al mio pensiero iniziale pure. Un ulteriore avvertimento è che si può avere un solo prefisso per ogni chiamata al metodo ActionLink e tutti gli elementi dovrebbero avere il prefisso. Ho aggiornato la mia risposta con un'altra possibile soluzione a seconda delle esatte esigenze e ho pensato che potresti essere interessato. Saluti. – Craig

2

Interessante domanda (+1). Suppongo che lo scopo sia quello di utilizzare il modello predefinito di associazione per associare i parametri di querystring ai parametri Action.

Fuori dalla scatola Non credo che il metodo ActionLink faccia questo per voi (ovviamente non c'è nulla che vi impedisca di farvi da soli). Guardando nel riflettore possiamo vedere che quando lo viene aggiunto allo RouteValueDictionary, vengono aggiunte solo coppie di valori chiave. Questo è il codice che aggiunge le coppie di valori chiave e come puoi vedere non c'è attraversamento delle proprietà dell'oggetto.

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

Così per l'oggetto

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

la chiave che viene aggiunto è Filter e il valore è il vostro Filter oggetto che valuterà il nome completo del tipo di oggetto.

Il risultato è Filter=Youre.Namespace.Filter.

Modificapossibile soluzione a seconda delle vostre esigenze Metodo


estensionefa il lavoro

Nota che utilizza i metodi quadro statici ExpressionHelper e ModelMetadata (che vengono utilizzati anche dagli helper esistenti) per determinare i nomi appropriati che il il raccoglitore modello predefinito comprenderà e il valore della proprietà, rispettivamente.

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)); 
    } 
} 

modelli di esempio

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

    public FilterViewModel Filter { get; set; } 
} 

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

azione

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

utilizzo

Qualsiasi numero delle proprietà del modello di visualizzazione può essere aggiunto alla querystring tramite l'ultimo parametrodel metodo.

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

Markup

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

Invece di utilizzare string.Format è possibile utilizzare TagBuilder, querystring dovrebbe essere codificato da passare in modo sicuro in un URL e questo metodo di estensione avrebbe bisogno di un po 'di un'ulteriore convalida, ma Penso che potrebbe essere utile. Si noti inoltre che, sebbene questo metodo di estensione sia stato creato per MVC 4, potrebbe essere facilmente modificato per le versioni precedenti. Non mi rendevo conto che uno dei tag MVC era per la versione 3 fino ad ora.

+0

* ovviamente non c'è nulla che ti impedisca di far ruotare il tuo * - e come potrei farlo? :) E hai ragione, stavo vedendo il "Filtro = Your.Nam ...". – DaveShaw

+0

@DaveShaw anche se non si può essere d'accordo, mi sento come se questa fosse una valida risposta alla tua domanda. Saluti. – Craig

+0

@DaveShaw ci stava pensando mentre tornavo a casa dal lavoro e ci ho provato. Questa soluzione potrebbe essere maturata mentre ci lavori, ma penso che potrebbe essere utile. – Craig

Problemi correlati