This blog post explains one way to let CMS users control shortcut/favorite icons with SaaS headless content management systems and provides an ASP.NET Core razor pages view component implementation.
This prototype is almost exactly like the prototype for google analytics that I described in a previous blog post, so I will not repeat that content.
To summarize, any entry should be able to specify a favicon. If an entry does not specify a favicon, then the view renders the favicon associated with the nearest ancestor that specifies a favicon.
The implementation depends on how you let the CMS user specify the favicon. If they need to type or paste in a file system path, then maybe a single line text field will suffice. If they need to be able to select from a media library, then a more complex field type may be required, as well as logic to map that structure back to a flat list. In my case, I store page metadata like this in a nested structure, which also affects value retrieval. Optimally, views are completely unaware of these implementation details.
This prototype solution uses the following.
- A field for the favicon in the collection of metadata fields available in all page entries in the CMS.
- Depends on CMS and implementation.
- A property in the class that models that structure to expose this new value.
- The FavIcon property I had already added to the class in that previous post.
- A view component class that determines the favicon.
using System; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Deliverystack.DeliveryApi.Models; using Deliverystack.Models; public class FavIcon : ViewComponent { public class FavIconModel { public string Path { get; } public FavIconModel(PathApiResultModel data) { foreach (var entry in data.Entries.Reverse()) { if (!string.IsNullOrEmpty(entry.PageData?.FavIcon)) { Path = entry.PageData.FavIcon; break; } } } } private PathApiClient _client; public FavIcon(PathApiClient client) { _client = client; } // May 18 2021 - logic is synchronous and lightweight; ignore warnings about async #pragma warning disable CS1998 public async Task<IViewComponentResult> InvokeAsync() { var favIconModel = new FavIconModel( _client.Get(new PathApiBindingModel() { Path = this.HttpContext.Request.Path, Ancestors = Int32.MaxValue })); if (String.IsNullOrEmpty(favIconModel.Path)) { return Content(String.Empty); } return View(favIconModel); } #pragma warning restore CS1998 // Rethrow to preserve stack details }
- A view component template (razor .cshtml file) to render the favicon.
@model HeadlessArchitect.Website.Pages.Shared.Components.Breadcrumb.FavIcon.FavIconModel @{ if (!string.IsNullOrEmpty(Model.Path)) { <link rel="icon" href="@Model.Path" type="image/x-icon"> } }
- Logic to invoke the view component.
- @await Component.InvokeAsync(“FavIcon”) in that previous post.