2010-07-02 6 views
2

J'utilise ASP.NET MVC 2 & .Net 3.5 avec Visual Studio 2008.Comment implémenter la navigation de type de type Assistant dans une application ASP.NET MVC 2?

Ok, ce que je fais référence par « page de navigation de type Assistant », est un site où vous avez une liste d'étapes dans un processus donné ou flux de travail. Il y a une sorte de dénotation visuelle pour indiquer quelle partie de l'étape vous êtes. J'ai déjà implémenté cette partie (bien que ça sent comme un hack) via ce qui suit:

css classe actuelle indique la page active. css class notcurrent indique la page inactive (c'est-à-dire la page que vous n'utilisez pas)

J'ai déclaré la méthode suivante dans une classe appelée NavigationTracker.

public static String getCss(String val, String currView) 
{ 
    String result = String.Empty; 
    String trimmedViewName = currView.Substring(currView.LastIndexOf("/") + 1).Replace(".aspx", ""); 
    if (val.ToLower().Equals(trimmedViewName.ToLower())) 
     result = "current"; 
    else 
     result = "notcurrent"; 

    return result; 
} 

J'ai mes étapes dans un contrôle comme celui-ci:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> 
<%@ Import Namespace="TheProject.Models" %> 

<link href="../../Content/custom.css" rel="stylesheet" type="text/css" /> 
<% 
    String currentView = ((WebFormView)ViewContext.View).ViewPath; 
%> 
<table width="100%"> 
    <tr>  
     <td class="<%= NavigationTracker.getCss("LogIn",currentView)%>" style="width:18%;">Log In</td> 
     <td class="<%= NavigationTracker.getCss("YearSelect",currentView)%>" style="width:18%;">Year Section</td> 
     <td class="<%= NavigationTracker.getCss("GoalEntry",currentView)%>" style="width:18%;">Goals</td> 
     <td class="<%= NavigationTracker.getCss("AssessmentEntry",currentView)%>" style="width:18%;">Assessment</td> 
     <td class="<%= NavigationTracker.getCss("SummaryEntry",currentView)%>" style="width:18%;">Summary</td>     
    </tr> 
</table> 

******************* C'est là où je besoin d'aide ***********

Pour compléter ce processus, je voudrais créer un contrôle utilisateur qui a juste Précédent & Suivant les boutons à gérer en passant par ce processus. Jusqu'à présent, un problème que j'ai rencontré est que ce contrôle ne peut pas être placé dans la page maître, mais devrait être inclus dans chaque vue, avant la fin du formulaire. Cela ne me dérange pas tellement. Cliquez sur le bouton Précédent ou Suivant pour soumettre le formulaire contenant à l'action appropriée. cependant, je ne suis pas sûr sur la façon de faire ce qui suit:

1) détecter si le bouton Précédent ou Suivant a été cliqué 2) Afficher/Masquer la logique des précédents & boutons Suivant au début & fin du processus, respectivement.

Une autre bizarrerie que je remarque avec mon application en général est que, après avoir parcouru plusieurs pages du processus, si je clique sur le bouton précédent, certaines valeurs de mon modèle apparaissent sur la page et d'autres pas. Par exemple, le texte saisi pour une zone de texte s'affiche, mais la valeur qui a été choisie pour un bouton radio n'est pas sélectionnée, mais lors de l'inspection directe du modèle, l'objet approprié possède une valeur à associer au bouton radio.

J'ai peut-être besoin de mettre cette dernière partie dans une nouvelle question. Ma question principale ici est avec le contrôle de la navigation. Tout pointeurs ou astuces sur la manipulation de cette logique & détectant si vous avez cliqué sur Suivant ou Précédent serait très utile.

EDIT 1

J'ai eu une pensée pour mettre un champ caché dans le contrôle qui affiche les boutons Suivant Précédent &. Selon le bouton cliqué, j'utiliserais JavaScript pour mettre à jour la valeur des champs cachés. Le problème semble maintenant être que le champ caché n'est jamais créé ni soumis avec le formulaire. J'ai modifié les arguments post du contrôleur pour accepter le champ supplémentaire, mais il n'est jamais soumis, ni dans le FormCollection.

Voici le code du champ caché. Notez qu'il est généré dans un contrôle utilisateur appelé à l'intérieur du formulaire sur la vue parentale (espérons que cela a du sens).

<% Html.Hidden("navDirection", navDirection); %> 

Pensées?

EDIT 2

J'ai résolu tous ces problèmes. Je posterai le code en détail mardi au plus tard. La solution présentée ci-dessous sera probablement choisie comme réponse, car elle m'a permis de trouver le bon processus de réflexion. En bref, la solution était d'avoir une classe de navigation comme celle suggérée avec logique pour déterminer la page suivante ou précédente basée sur la vue actuelle & une liste de chaînes de toutes les vues. Une vue partielle/un contrôle utilisateur a été créé pour afficher les boutons Précédent/Suivant. Le contrôle utilisateur avait 2 champs cachés: 1) Un avec la valeur de la vue actuelle 2) un champ indiquant la direction de navigation (précédent ou suivant). JavaScript a été utilisé pour mettre à jour la valeur du champ de navigation cachée en fonction du bouton sur lequel on a cliqué. La logique dans le contrôle utilisateur a déterminé si les boutons "Précédent" ou "Suivant" s'affichaient ou non selon les premières et dernières vues de l'assistant par rapport à la vue actuelle. Tout a dit, je suis assez content des résultats. Je vais probablement trouver des problèmes d'odeurs de code quand je reviendrai à ça, mais, pour le moment, ça fonctionne.

Merci à tous pour votre aide!

EDIT 3 - SOLUTION

Voici le code pour le contrôle I construit pour afficher la 'Suivant' & boutons de navigation 'Précédent':

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> 
<%@ Import Namespace="Project.Models" %> 
<link href="../../Content/custom.css" rel="stylesheet" type="text/css" /> 
<script type="text/javascript" > 
    function setVal(val) { 
     var nav = document.getElementById("NavigationDirection"); 
     nav.value = val; 
    } 
</script> 

<% 
    String currentView = ((WebFormView)ViewContext.View).ViewPath;  
    String navDirection = "empty"; 
    currentView = NavigationTracker.getShortViewName(currentView); 
%> 

<input type="hidden" value="<%= currentView %>" name="CurrentView" id="CurrentView" /> 
<input type="hidden" value="<%= navDirection %>" name="NavigationDirection" id="NavigationDirection" /> 

<% if(currentView != NavigationTracker.FirstPage) { %> 
    <div style="float:left;">   
     <input type="submit" value="Previous" onclick="setVal('previous')" />  <!-- On click set navDirection = "previous" --> 
    </div> 
<% } %> 

<% if (currentView != NavigationTracker.LastPage) 
    { %> 
<div style="float:right;"> 
    <input type="submit" value="Next" onclick="setVal('next')" />    <!-- On click set navDirection = "next" --> 
</div>  
<% } %> 

De là, vous rendre le contrôle juste avant la balise de fermeture d'un formulaire sur les vues que vous voulez comme ça:

<% Html.RenderPartial("BottomNavControl"); %> 
    <% } %> 

Maintenant je ne peux pas vraiment publier tout le code NavigationTracker, mais la viande de comment il fonctionne peut être déduite de la réponse sélectionnée et l'extrait suivant qui renvoie le nom de la vue, en fonction de la vue actuelle et la direction (précédente ou suivante). Maintenant, faire tout cela a quelques effets secondaires qui pourraient facilement être considérés comme une odeur de code. Premièrement, je dois modifier toutes mes méthodes de contrôleur marquées [HTTPPOST] pour accepter un objet NavigationTracker en tant que paramètre. Cet objet contient des méthodes d'assistance et les propriétés CurrentDirection & NavigationDirection. Une fois cela fait, je peux obtenir la vue suivante de la même manière dans toutes mes actions:

return RedirectToAction(nav.NextView); 

où nav est de type NavigationTracker.

Une autre remarque est que les propriétés LastPage & LastPage de NavigationTracker sont statiques, donc j'utilise réellement NavigationTracker.FirstPage dans mon fichier global.asax.cs pour mon routage. Cela signifie que je peux accéder à ma classe NavigationTracker et modifier le flux au même endroit pour l'ensemble de l'application.

J'aimerais des commentaires ou des critiques sur cette solution. J'admets que ce n'est peut-être pas une solution formidable, mais à première vue, je suis plutôt content.

Espérons que ça aide.

Répondre

3

Vous devez implémenter une vue ou un contrôle fortement typé. Dans ce type, définissez une propriété indiquant quelle étape vous êtes et quelle autre logique. EX:

public class WizardView 
{ 
    public List<string> Steps { get; set; } 
    public int CurrentStepNumber {get;set;} 

    public bool ShowNextButton 
    { 
     get 
     { 
      return CurrentStepNumber < this.Steps.Count-1; 
     } 
    } 

    public bool ShowPreviousButton 
    { 
     get 
     { 
      return CurrentStepNumber > 0; 
     } 
    } 
} 

Et votre contrôle:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<WizardView>" %> 


    <table width="100%"> 
     <tr>  

     <% 
      int index=0; 
      foreach(string step in Model.Steps) 
      { 
      %> 
       <td class='<%=Model.CurrentStep==index?"current":"notcurrent" %> style="width:18%;"> 
         <%= step %> 
       </td> 
      <% 
       index++; 
      } %> 
     </tr> 
</table> 

Dans votre contrôleur

public ActionResult MyAction(int step) 
{ 
    return View (new WizardControl { 
     Steps = Myrepository.getSteps(); 
     CurrentStep = step 
    }); 
} 

}

+0

Ceci est une très bonne solution. Je vous remercie! Maintenant, j'ai juste besoin de comprendre comment savoir si le bouton précédent ou le bouton suivant a été cliqué. – jason

+0

@baijajusav - Si la solution de Grégoire fonctionne pour vous, pourquoi ne pas l'augmenter? Cela semble devenir une norme où les réponses ne sont plus acceptées et votées. Fait vraiment difficile quand on trouve une question mais les réponses ne sont ni acceptées ni mises à jour Ahmad

+0

@Ahmad. C'est un très bon point. J'ai upvoted sa solution, mais je ne l'ai pas acceptée parce que le cœur de mon problème est de détecter si le bouton Précédent ou Suivant a été cliqué. Les boutons eux-mêmes ne font que soumettre le formulaire. Je pense que je devrais ajouter de la logique dans le contrôleur pour gérer la redirection de l'action en fonction du bouton sur lequel on a cliqué. – jason

1

cela peut ne pas être exactement ce que vous cherchez, mais que faire si vous utilisez jQuery pour masquer et afficher les balises DIV.Ensuite, l'utilisateur aura l'expérience d'un assistant alors que tout le contenu est dans une seule vue. Ensuite, il peut tout être manipulé bien avec un seul contrôleur

Une autre option pourrait être de créer une vue séparée « étape » pour chaque étape de l'assistant

http://example.com/page/step/1

Ensuite, vous pouvez créer une classe d'objets qui contient tous champs et ajouter à ce que vous naviguez l'assistant en ajoutant à un objet session

(Custom.Class.Obj)Session["myWizard"]

de cette façon, il vous permet de créer des vues standard et charger ce que je Informations que vous avez sur l'objet Session.

0

Voici une autre façon d'aller - l'élargissement de l'approche WizardView. Ce que vous décrivez est un moteur d'état - quelque chose de conscient des états, des transitions et des comportements et déclencheurs associés à chacun.

Si vous extrayez une implémentation comme stateless (http://code.google.com/p/stateless/), vous verrez que vous pouvez décrire les états (étapes de l'approche de l'assistant) et les déclencheurs associés à chacun. Sur la base de votre description, vous pouvez soit capturer cette information dans le fil moteur de votre moteur d'état - ou peut-être l'ignorer complètement par le fait que toutes les transitions sont traitées discrètement par le moteur.

Pour aller plus loin, votre fil de vue peut maintenant devenir assez générique. Il n'est pas nécessaire que la vue soit consciente de l'état d'avancement des opérations, en se basant uniquement sur le modèle de vue lui-même.

Questions connexes