49 votes

Comment réaliser des fils d'Ariane dynamiques avec ASP.net MVC ?

Comment chapelure dynamique être réalisé avec ASP.net MVC ?

Si vous êtes curieux de savoir ce que sont les miettes de pain :

Qu'est-ce que la chapelure ? Si vous avez déjà navigué sur une boutique en ligne ou lu des messages dans un forum, vous avez probablement rencontré des fils d'Ariane. Ils constituent un moyen facile de savoir où vous vous trouvez sur un site. Des sites comme Craigslist utilisent le fil d'Ariane pour décrire l'emplacement de l'utilisateur. Au-dessus des annonces de chaque page se trouve quelque chose qui ressemble à ceci :

s.f. bayarea craigslist > ville de san francisco > bicyclettes

EDIT

Je sais ce qui est possible avec le SiteMapProvider. Je suis également conscient des fournisseurs qui existent sur le net et qui vous permettent d'associer les sitenodes aux contrôleurs et aux actions.

Mais qu'en est-il lorsque vous souhaitez que le texte d'un fil d'Ariane corresponde à une valeur dynamique, comme ceci :

Accueil > Produits > Voitures > Toyota

Accueil > Produits > Voitures > Chevy

Accueil > Produits > Équipement d'exécution > Chaise électrique

Accueil > Produits > Équipement d'exécution > Potences

... où les catégories de produits et les produits sont des enregistrements d'une base de données. Certains liens doivent être définis de manière statique (Home pour sûr).

J'essaie de comprendre comment faire cela, mais je suis sûr que quelqu'un l'a déjà fait avec ASP.net MVC.

56voto

Sean Haddy Points 456

Les sitemaps sont certainement une solution... Vous pouvez aussi en écrire un vous-même ! (bien sûr, à condition de respecter les règles MVC standard)... Je viens d'en écrire un, je me suis dit que je le partagerais ici.

@Html.ActionLink("Home", "Index", "Home")
@if(ViewContext.RouteData.Values["controller"].ToString() != "Home") {
    @:> @Html.ActionLink(ViewContext.RouteData.Values["controller"].ToString(), "Index", ViewContext.RouteData.Values["controller"].ToString()) 
}
@if(ViewContext.RouteData.Values["action"].ToString() != "Index"){
    @:> @Html.ActionLink(ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["controller"].ToString()) 
}

J'espère que quelqu'un trouvera cela utile, c'est exactement ce que je cherchais lorsque j'ai cherché dans SO des breadcrumbs MVC.

33voto

vulcan raven Points 8312

ASP.NET 5 (alias ASP.NET Core), solution MVC Core

Dans ASP.NET Core, les choses sont encore plus optimisées car nous n'avons pas besoin de stringifier le balisage dans la méthode d'extension.

En ~/Extesions/HtmlExtensions.cs :

using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace YourProjectNamespace.Extensions
{
    public static class HtmlExtensions
    {
        private static readonly HtmlContentBuilder _emptyBuilder = new HtmlContentBuilder();

        public static IHtmlContent BuildBreadcrumbNavigation(this IHtmlHelper helper)
        {
            if (helper.ViewContext.RouteData.Values["controller"].ToString() == "Home" ||
                helper.ViewContext.RouteData.Values["controller"].ToString() == "Account")
            {
                return _emptyBuilder;
            }

            string controllerName = helper.ViewContext.RouteData.Values["controller"].ToString();
            string actionName = helper.ViewContext.RouteData.Values["action"].ToString();

            var breadcrumb = new HtmlContentBuilder()
                                .AppendHtml("<ol class='breadcrumb'><li>")
                                .AppendHtml(helper.ActionLink("Home", "Index", "Home"))
                                .AppendHtml("</li><li>")
                                .AppendHtml(helper.ActionLink(controllerName.Titleize(),
                                                          "Index", controllerName))
                                .AppendHtml("</li>");

            if (helper.ViewContext.RouteData.Values["action"].ToString() != "Index")
            {
                breadcrumb.AppendHtml("<li>")
                          .AppendHtml(helper.ActionLink(actionName.Titleize(), actionName, controllerName))
                          .AppendHtml("</li>");
            }

            return breadcrumb.AppendHtml("</ol>");
        }
    }
}

~/Extensions/StringExtensions.cs reste le même que ci-dessous (faites défiler la page pour voir la version MVC5).

Selon le point de vue de Razor, nous n'avons pas besoin Html.Raw comme Razor s'occupe de l'échappement lorsqu'il s'agit de IHtmlContent :

....
....
<div class="container body-content">

    <!-- #region Breadcrumb -->
    @Html.BuildBreadcrumbNavigation()
    <!-- #endregion -->

    @RenderBody()
    <hr />
...
...

Solution ASP.NET 4, MVC 5

\=== RÉPONSE ORIGINALE / ANCIENNE RÉPONSE CI-DESSOUS ===

(Développant la réponse de Sean Haddy ci-dessus)

Si vous voulez le rendre orienté vers les extensions (en gardant Views propre), vous pouvez faire quelque chose comme :

En ~/Extesions/HtmlExtensions.cs :

(compatible avec MVC5 / bootstrap)

using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace YourProjectNamespace.Extensions
{
    public static class HtmlExtensions
    {
        public static string BuildBreadcrumbNavigation(this HtmlHelper helper)
        {
            // optional condition: I didn't wanted it to show on home and account controller
            if (helper.ViewContext.RouteData.Values["controller"].ToString() == "Home" ||
                helper.ViewContext.RouteData.Values["controller"].ToString() == "Account")
            {
                return string.Empty;
            }

            StringBuilder breadcrumb = new StringBuilder("<ol class='breadcrumb'><li>").Append(helper.ActionLink("Home", "Index", "Home").ToHtmlString()).Append("</li>");

            breadcrumb.Append("<li>");
            breadcrumb.Append(helper.ActionLink(helper.ViewContext.RouteData.Values["controller"].ToString().Titleize(),
                                               "Index",
                                               helper.ViewContext.RouteData.Values["controller"].ToString()));
            breadcrumb.Append("</li>");

            if (helper.ViewContext.RouteData.Values["action"].ToString() != "Index")
            {
                breadcrumb.Append("<li>");
                breadcrumb.Append(helper.ActionLink(helper.ViewContext.RouteData.Values["action"].ToString().Titleize(),
                                                    helper.ViewContext.RouteData.Values["action"].ToString(),
                                                    helper.ViewContext.RouteData.Values["controller"].ToString()));
                breadcrumb.Append("</li>");
            }

            return breadcrumb.Append("</ol>").ToString();
        }
    }
}

En ~/Extensions/StringExtensions.cs :

using System.Globalization;
using System.Text.RegularExpressions;

namespace YourProjectNamespace.Extensions
{
    public static class StringExtensions
    {
        public static string Titleize(this string text)
        {
            return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text).ToSentenceCase();
        }

        public static string ToSentenceCase(this string str)
        {
            return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
        }
    }
}

Ensuite, utilisez-le comme (dans _Layout.cshtml par exemple) :

....
....
<div class="container body-content">

    <!-- #region Breadcrumb -->
    @Html.Raw(Html.BuildBreadcrumbNavigation())
    <!-- #endregion -->

    @RenderBody()
    <hr />
...
...

23voto

ICodeForCoffee Points 1786

Il existe un outil pour faire cela sur codeplex : http://mvcsitemap.codeplex.com/ [projet [déplacé vers github]](https://github.com/maartenba/MvcSiteMapProvider)

Edit :

Il existe un moyen de dériver un SiteMapProvider à partir d'une base de données : http://www.asp.net/Learn/data-access/tutorial-62-cs.aspx

Vous pouvez peut-être modifier l'outil mvcsitemap pour l'utiliser et obtenir ce que vous voulez.

5voto

Larz Points 323

J'ai construit ce paquet nuget pour résoudre ce problème pour moi-même :

https://www.nuget.org/packages/MvcBreadCrumbs/

Vous pouvez contribuer ici si vous avez des idées à ce sujet :

https://github.com/thelarz/MvcBreadCrumbs

5voto

pimbrouwers Points 5499

Pour ceux qui utilisent ASP.NET Core 2.0 et qui recherchent une approche plus découplée que le HtmlHelper de Vulcan, je vous recommande d'utiliser une vue partielle avec injection de dépendances .

Vous trouverez ci-dessous une mise en œuvre simple qui peut facilement être adaptée à vos besoins.

Le service de fil d'Ariane ( ./Services/BreadcrumbService.cs ):

using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Collections.Generic;

namespace YourNamespace.YourProject
{  
  public class BreadcrumbService : IViewContextAware
  {
    IList<Breadcrumb> breadcrumbs;

    public void Contextualize(ViewContext viewContext)
    {
      breadcrumbs = new List<Breadcrumb>();

      string area = $"{viewContext.RouteData.Values["area"]}";
      string controller = $"{viewContext.RouteData.Values["controller"]}";
      string action = $"{viewContext.RouteData.Values["action"]}";
      object id = viewContext.RouteData.Values["id"];
      string title = $"{viewContext.ViewData["Title"]}";   

      breadcrumbs.Add(new Breadcrumb(area, controller, action, title, id));

      if(!string.Equals(action, "index", StringComparison.OrdinalIgnoreCase))
      {
        breadcrumbs.Insert(0, new Breadcrumb(area, controller, "index", title));
      }
    }

    public IList<Breadcrumb> GetBreadcrumbs()
    {
      return breadcrumbs;
    }
  }

  public class Breadcrumb
  {
    public Breadcrumb(string area, string controller, string action, string title, object id) : this(area, controller, action, title)
    {
      Id = id;
    }

    public Breadcrumb(string area, string controller, string action, string title)
    {
      Area = area;
      Controller = controller;
      Action = action;

      if (string.IsNullOrWhiteSpace(title))
      {
         Title = Regex.Replace(CultureInfo.CurrentCulture.TextInfo.ToTitleCase(string.Equals(action, "Index", StringComparison.OrdinalIgnoreCase) ? controller : action), "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
      }
      else
      {
         Title = title;
      } 
    }

    public string Area { get; set; }
    public string Controller { get; set; }
    public string Action { get; set; }
    public object Id { get; set; }
    public string Title { get; set; }
  }
}

Enregistrez le service dans startup.cs après AddMvc() :

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddTransient<BreadcrumbService>(); 

Créer un partiel pour rendre le fil d'Ariane ( ~/Views/Shared/Breadcrumbs.cshtml ):

@using YourNamespace.YourProject.Services
@inject BreadcrumbService BreadcrumbService

@foreach(var breadcrumb in BreadcrumbService.GetBreadcrumbs())
{
    <a asp-area="@breadcrumb.Area" asp-controller="@breadcrumb.Controller" asp-action="@breadcrumb.Action" asp-route-id="@breadcrumb.Id">@breadcrumb.Title</a>
}

À ce stade, pour rendre les miettes de pain, il suffit d'appeler Html.Partial("Breadcrumbs") ou Html.PartialAsync("Breadcrumbs") .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X