2013-08-14 3 views
1

Je passe un modèle à une vue avec une propriété qui est une collection de livres, par exemple. À mon avis j'utilise une boucle foreach pour créer une table des livres dans ma collection, chacun avec nom, auteur, etc.Renvoyer le modèle au contrôleur avec la propriété de liste

Je veux que l'utilisateur puisse ajouter/modifier/supprimer des livres sur le côté client. Ensuite, je veux être en mesure de renvoyer au contrôleur le modèle, avec la collection de livres reflétant les changements effectués.

Est-ce possible?

+0

vous voulez dire que vous ne voulez pas enregistrer cha nges jusqu'à ce que toute la liste soit affichée? Pourquoi est ce que tu veux faire ça? – ataravati

+0

Je prévois d'utiliser les valeurs pour enregistrer dans la base de données sur la publication, donc je ne veux pas sauvegarder les valeurs jusqu'à ce qu'elles soient sauvegardées au cas où elles feraient une erreur et fermeraient le navigateur. Ou ai-je oublié quelque chose? – heilch

+0

Eh bien, vous leur fournissez la liste, et ils peuvent modifier/supprimer chaque élément de la liste. Vous n'avez pas à renvoyer toute la collection au serveur, seulement celle qui est modifiée. – ataravati

Répondre

0

a résolu le problème sans l'utilisation de ajax/jquery/knock-out:

Fondamentalement, je besoin pour envelopper la page cshtml dans un @using (Html.BeginForm (~)) {}, puis utilisez une boucle for (NOT a foreach) pour afficher chacun des éléments de la liste. Ensuite, je devais créer un @ Html.HiddenFor pour chaque élément de la liste. Quand je soumets le formulaire je prends le modèle comme paramètre et les articles peuplent la liste.Je ne peux pas montrer mon code réel, donc je hâte réétiqueté certaines variables clés, donc j'espère que vous pouvez lui donner un sens, mais c'est essentiellement comment je l'ai eu à travailler

Voici le contrôleur

[HttpGet] 
public ActionResult BookStore(int storeId) 
{ 
    //model contains a list property like the following: 
    //public List<Books> BooksList { get; set; } 
    //pass the model to the view 
    var model = new BookStoreModel(); 

    return View(model); 
} 

est la vue

@model BookStore.Models.BookStoreModel 
@using (Html.BeginForm("BookStoreSummary", "BookStore", FormMethod.Post)) 
{ 
<fieldset> 

@Html.HiddenFor(model => model.Id) 
@Html.HiddenFor(model => model.BookId) 
@Html.HiddenFor(model => model.LastModified) 
//some html stuff here 
<table id="users" class="ui-widget ui-widget-content"> 
<thead> 
    <tr class="ui-widget-header BookTable"> 
    <th>@Html.DisplayNameFor(model => model.BookList.FirstOrDefault().Title)  </th> 
    <th>@Html.DisplayNameFor(model => model.BookList.FirstOrDefault().Author)</th> 

    </tr> 
</thead> 
<tbody> 
@for (int i = 0; i < Model.BookList.Count; i++) 
{ 
     <tr> 
      <td> 
       @Html.HiddenFor(model => model.BookList[i].Author) 
       @Html.DisplayFor(model => model.BookList[i].Author) 
      </td> 
      <td> 
       @Html.HiddenFor(model => model.BookList[i].BookId) 
       @Html.HiddenFor(model => model.BookList[i].Title) 
       @Html.DisplayFor(model => model.BookList[i].Title) 
      </td> 
     </tr> 
    } 
</tbody> 
</table> 

</fieldset> 
} 

et le contrôleur après retour:

[HttpPost] 
//[AcceptVerbs("POST")] 
public ActionResult BookStoreSummary(BookStoreModel model) 
{ 
    //do stuff with model, return 
    return View(model); 
} 
+0

Je suis heureux que cela ait résolu votre problème, mais je ne crois pas que cela fonctionne comme décrit dans votre question. Cela ne stockera pas un ensemble de nouveaux livres créés sur le client à enregistrer avec une action de serveur. Je suppose que cela dépend de votre interaction spécifique avec l'interface utilisateur, n'autorisez-vous qu'une seule nouvelle entrée? –

+0

En fait, je suis capable d'ajouter plusieurs livres à la grille et de les renvoyer tous au contrôleur comme plusieurs nouveaux éléments de la liste. Tant que je m'assure d'incrémenter l'ID généré de 1 et d'inclure toutes les informations pour cet "item" dans la liste, il l'ajoute sans problème. Autant que je sache, il recherche l'identifiant généré pour l'objet liste et commence à compter. un ID d'exemple est donc BookList_1__Author_Text. L'objet liste est BookList, premier objet dans la liste, nom de la propriété et type de propriété. – heilch

0

Oui, il est possible

que vous avez une forme et vous avez la collection nommée « Livres ». Si vous souhaitez ajouter un nouveau livre par programme, vous pouvez utiliser jQuery et ajax. Fist vous avez besoin de cours d'aide.

Les classes suivantes vous aident à créer un élément de livre unique à ajouter à la collection de livres dans votre vue. Comme vous le savez tous les domaines doit avoir le préfixe unique, donc van liant modèle distinction entre les éléments de formulaire

public static class HtmlClientSideValidationExtensions 
{ 
     public static IDisposable BeginAjaxContentValidation(this HtmlHelper html, string formId) 
     { 
      MvcForm mvcForm = null; 

      if (html.ViewContext.FormContext == null) 
      { 
       html.EnableClientValidation(); 
       mvcForm = new MvcForm(html.ViewContext); 
       html.ViewContext.FormContext.FormId = formId; 
      } 

      return new AjaxContentValidation(html.ViewContext, mvcForm); 
     } 

     private class AjaxContentValidation : IDisposable 
     { 
      private readonly MvcForm _mvcForm; 
      private readonly ViewContext _viewContext; 

      public AjaxContentValidation(ViewContext viewContext, MvcForm mvcForm) 
      { 
       _viewContext = viewContext; 
       _mvcForm = mvcForm; 
      } 

      public void Dispose() 
      { 
       if (_mvcForm != null) 
       { 
        _viewContext.OutputClientValidation(); 
        _viewContext.FormContext = null; 
       } 
      } 
     } 
    } 

    public static class CollectionValidation 
    { 

     private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_"; 

     public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName) 
     { 
      var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName); 
      string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString(); 

      // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync. 
      html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex))); 

      return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex)); 
     } 

     public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix) 
     { 
      return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix); 
     } 

     private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName) 
     { 
      // We need to use the same sequence of IDs following a server-side validation failure, 
      // otherwise the framework won't render the validation error messages next to each item. 
      string key = idsToReuseKey + collectionName; 
      var queue = (Queue<string>)httpContext.Items[key]; 
      if (queue == null) 
      { 
       httpContext.Items[key] = queue = new Queue<string>(); 
       var previouslyUsedIds = httpContext.Request[collectionName + ".index"]; 
       if (!string.IsNullOrEmpty(previouslyUsedIds)) 
        foreach (string previouslyUsedId in previouslyUsedIds.Split(',')) 
         queue.Enqueue(previouslyUsedId); 
      } 
      return queue; 
     } 

     private class HtmlFieldPrefixScope : IDisposable 
     { 
      private readonly TemplateInfo templateInfo; 
      private readonly string previousHtmlFieldPrefix; 

      public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) 
      { 
       this.templateInfo = templateInfo; 

       previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix; 
       templateInfo.HtmlFieldPrefix = htmlFieldPrefix; 
      } 

      public void Dispose() 
      { 
       templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix; 
      } 
     } 
    } 

Ensuite, nous supposons que vous avez une vue partielle pour ajouter des livres comme celui-ci:

@model Models.Book 
@using (Html.BeginAjaxContentValidation("form")) 
    { 
     using (Html.BeginCollectionItem("Books")) 
     { 



     <div class="fieldcontanier"> 
      @Html.LabelFor(model => model.Title) 

      @Html.EditorFor(model => model.Title) 
      @Html.ValidationMessageFor(model => model.Title) 
     </div> 
     <div class="fieldcontanier"> 
      @Html.LabelFor(model => model.Author) 

      @Html.EditorFor(model => model.Author) 
      @Html.ValidationMessageFor(model => model.Author) 
     </div> 

     ... 

     } 
    } 

et supposer qu'il est un lien « ajouter un nouveau livre » sous forme que vous avez défini événement suivant pour que jQuery:

$('a ').click(function (e) { 
       e.preventDefault(); 
       $.ajax({ 
        url: '@Url.Action("NewBook")', 
        type: 'GET', 
        success: function (context) { 
         $('#books').append(context); 


         $("form").removeData("validator"); 
         $("form").removeData("unobtrusiveValidation"); 
         $.validator.unobtrusive.parse("form"); 
        } 
       }); 
      }); 

dans le code ci-dessus, tout d'abord vous demandez à l'action appelée NewBook, qui renvoie une vue partielle, je l'ai mentionné plus tôt, puis chargé après d'autres livres dans la page, puis pour la validation discrète à appliquer, nous utilisons les dernières lignes d'arbres.

+0

Donc si cette collection est une propriété du modèle je passe à la vue, serai-je capable de renvoyer le modèle au contrôleur et référence cette mise à jour collection en tant que propriété du modèle? – heilch

+0

Oui, vous pourriez. Dites que vous avez la propriété liste appelée "Livres" et défini comme ceci: Liste Livres ... –

+0

Je vais essayer et marquer si cela fonctionne, merci pour la réponse par la façon – heilch

0

Oui, absolument possible. J'utilise actuellement KnockOut sur le client et il vous permettra de lier à une collection d'un objet Javascript, de rendre chaque élément comme un modèle, de l'ajouter, de le supprimer, puis de renvoyer toute la collection au serveur pour traitement. Vous devrez gérer des choses comme l'état des livres supprimés, et les cacher de la liaison, mais tout est faisable.

est ici la syntaxe KO vous besoin dans votre vue:

<table> 
    <!-- ko foreach: {data: books } --> 
    <tr> 
     <td data-bind="text: title" /> 
    </tr> 
    <!-- /ko --> 
</table> 

Cela va créer une table avec 1 ligne pour chaque élément dans les livres .. ces objets ont besoin d'une propriété « titre » qui sera le seul valeur dans le tableau. Knockout est une excellente bibliothèque et j'ai eu beaucoup de plaisir à apprendre et à développer avec elle récemment. Vous pouvez obtenir plus d'informations sur leur page de projet ici: http://knockoutjs.com/

+0

cela fait passer la collection en tant que propriété dans le modèle? – heilch

+0

Oui, avec KO, le workflow de base serait, appelez l'action du serveur, traiter les données renvoyées dans un objet ViewModel javascript (c'est ce qui contient votre collection Books). Ce ViewModel est ensuite lié à la page. Ces commentaires ko et les attributs de liaison de données indiquent à KO comment construire la dom. Lors de la sauvegarde, vous convertissez ce viewmodel en JSON et le publiez sur le serveur pour l'enregistrement. Ce viewmodel contient tous les livres. Info: http://knockoutjs.com/documentation/json-data.html –

+0

résolu sans l'utilisation de jquery/knockout/ajax. Depuis que j'ai moins de 10 points de rep, je dois attendre 4 heures supplémentaires pour poster la réponse – heilch

Questions connexes