Category

          Category

            Wheels & Tires

            Error executing template "Designs/Swift-v2/Custom/MouldshopCategorySiblings.cshtml"
            System.ArgumentNullException: Value cannot be null. (Parameter 'source')
               at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
               at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
               at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
               at CompiledRazorTemplates.Dynamic.RazorEngine_485c7bb87f574172b384f06363e8644b.ExecuteAsync()
               at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
               at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
               at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
               at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
               at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
               at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
               at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
               at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
               at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
               at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
               at Dynamicweb.Rendering.Template.RenderRazorTemplate()
            
            1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using System.Linq 3 @using Dynamicweb.Frontend 4 @using Dynamicweb.Frontend.Navigation 5 6 @* Mouldshop Category Siblings — the sibling nav at the top of the listing sidebar. TASK-0104. 7 8 WHERE IT RENDERS. From our shadow of `Swift-v2_ProductListFacets.cshtml`, one call above 9 both facet forms. There is no paragraph of ours in the sidebar column and adding one is an 10 admin write, so the facets paragraph is the only template already standing in that column. 11 12 DATA SOURCE — NOT A SECOND ONE. The same tree the mega menu (`MouldshopHeader.cshtml`) and 13 the category grid (`MouldshopCategoryGrid.cshtml`) walk: `GetNavigationViewModel` from the 14 root, `ShowInMenu` nodes, catalogue root found by the same `FindProductsNode`. The current 15 category is the node DW marks `IsActive` — the flag the vendor breadcrumb uses to draw its 16 `active` crumb. Menu, grid, breadcrumb and this list therefore cannot disagree about what 17 exists or which one is current. 18 19 WHAT IS LISTED — SUPERSEDED 2026-09-23 (operator). TASK-0104 listed the active category's 20 SIBLINGS. It now lists the CHILDREN of a context category, under that category's name as a 21 title, with "All" first. The rule for choosing the context is at the top of the code block 22 below. Anywhere with no active catalogue node (search, a page outside the shop) nothing 23 renders. 24 25 CURRENT IS A <span aria-current="page">, NOT A LINK, drawn as a light-blue pill. Every entry 26 carries the same pill box at rest, so selecting one changes its colour and never its size. 27 28 NEVER THROWS. DW compiles templates on first request, so an exception here is a listing 29 page down on first hit. Any failure degrades to no nav, facets intact. *@ 30 31 @functions { 32 NavigationTreeViewModel GetTree(int rootPageId) 33 { 34 return Navigation.GetNavigationViewModel(new NavigationSettings 35 { 36 StartLevel = 1, 37 StopLevel = 9, 38 RootPageId = rootPageId, 39 IncludeFoldersAndHidden = false, 40 ExpandMode = ExpandMode.All, 41 }); 42 } 43 44 // Identical to MouldshopHeader's and MouldshopCategoryGrid's, deliberately — one tree, 45 // three consumers, same entry point. 46 NavigationTreeNodeViewModel FindProductsNode(System.Collections.Generic.List<NavigationTreeNodeViewModel> topNodes) 47 { 48 var byName = topNodes.FirstOrDefault(n => 49 !string.IsNullOrEmpty(n.Name) && 50 (n.Name.IndexOf("product", System.StringComparison.OrdinalIgnoreCase) >= 0 || 51 n.Name.IndexOf("shop", System.StringComparison.OrdinalIgnoreCase) >= 0 || 52 n.Name.IndexOf("catalog", System.StringComparison.OrdinalIgnoreCase) >= 0)); 53 if (byName != null && (byName.Nodes?.Any(c => c.ShowInMenu) ?? false)) { return byName; } 54 return topNodes.Where(n => n.Nodes?.Any(c => c.ShowInMenu) ?? false) 55 .OrderByDescending(n => n.Nodes.Count(c => c.ShowInMenu)) 56 .FirstOrDefault(); 57 } 58 59 // The node whose menu children include the active node, searched depth-first under the 60 // catalogue root. The root itself when the root is what is active. 61 NavigationTreeNodeViewModel FindActiveParent(NavigationTreeNodeViewModel node) 62 { 63 var kids = node?.Nodes?.Where(n => n.ShowInMenu).ToList(); 64 if (kids == null || kids.Count == 0) { return null; } 65 if (kids.Any(n => n.IsActive)) { return node; } 66 foreach (var kid in kids) 67 { 68 var hit = FindActiveParent(kid); 69 if (hit != null) { return hit; } 70 } 71 return null; 72 } 73 74 // The active node itself, searched depth-first under the catalogue root. 75 NavigationTreeNodeViewModel FindActive(NavigationTreeNodeViewModel node) 76 { 77 if (node == null) { return null; } 78 if (node.IsActive) { return node; } 79 var kids = node.Nodes?.Where(n => n.ShowInMenu) ?? Enumerable.Empty<NavigationTreeNodeViewModel>(); 80 foreach (var kid in kids) 81 { 82 var hit = FindActive(kid); 83 if (hit != null) { return hit; } 84 } 85 return null; 86 } 87 88 System.Collections.Generic.List<NavigationTreeNodeViewModel> MenuKids(NavigationTreeNodeViewModel node) 89 { 90 return node?.Nodes?.Where(n => n.ShowInMenu && !string.IsNullOrWhiteSpace(n.Name)).ToList() 91 ?? new System.Collections.Generic.List<NavigationTreeNodeViewModel>(); 92 } 93 } 94 95 @{ 96 // Operator 2026-09-23: CHILDREN, not siblings; the category's title big on top; "All" 97 // first and selected by default; the selected entry drawn as a light-blue pill. 98 // 99 // THE CONTEXT NODE is the category whose children are listed and whose name is the title: 100 // - the active category, when it has children -> "All" is selected; 101 // - else its parent, when that parent is a category (not the catalogue root) 102 // -> the active child is selected; 103 // - else the active category itself (a top-level category with no children) 104 // -> the list is "All" alone, selected. 105 // So a shopper who picks a subcategory keeps the same list and title, with the pill 106 // moved, and "All" takes them back to the parent. On the catalogue root the root is the 107 // context. The file keeps its TASK-0104 name so the facets shadow's one call is unchanged. 108 NavigationTreeNodeViewModel context = null; 109 NavigationTreeNodeViewModel active = null; 110 System.Collections.Generic.List<NavigationTreeNodeViewModel> children = null; 111 try 112 { 113 var tree = GetTree(0); 114 var topNodes = tree?.Nodes?.Where(n => n.ShowInMenu).ToList() 115 ?? new System.Collections.Generic.List<NavigationTreeNodeViewModel>(); 116 var productsNode = FindProductsNode(topNodes); 117 active = FindActive(productsNode); 118 if (active != null) 119 { 120 if (MenuKids(active).Any() || active == productsNode) 121 { 122 context = active; 123 } 124 else 125 { 126 var parent = FindActiveParent(productsNode); 127 context = (parent != null && parent != productsNode) ? parent : active; 128 } 129 children = MenuKids(context); 130 } 131 } 132 catch 133 { 134 context = null; 135 } 136 bool allSelected = context != null && context == active; 137 // TASK-0114 (operator, /en-us/shop/sale): a context category with NO children renders its 138 // title only - no "All" pill, no list, no divider. Everything else is unchanged. 139 bool titleOnly = context != null && (children == null || children.Count == 0); 140 141 // THE CATEGORY'S OWN "ALL ..." CHILD REPLACES OUR SYNTHETIC "All" (operator, 2026-09-24: 142 // "there is proper button at the bottom of the categories list - but it should be on top; 143 // there is second, unwanted button All on top. Remove the unwanted and position the proper 144 // one on top"). Bikes has a real group "All bikes"; the synthetic pill duplicated it. 145 // Matched by NAME - a child whose name is "All" or starts with "All " - because that is 146 // the only thing that marks it; no field says "this is the catch-all group". 147 // When it exists: it is listed FIRST, the synthetic pill is not rendered, and on the 148 // context page itself (where "All" was selected by default) it carries the selected look. 149 // aria-current stays only on the page actually open. No such child: unchanged. 150 NavigationTreeNodeViewModel allChild = null; 151 if (!titleOnly) 152 { 153 allChild = children.FirstOrDefault(n => !string.IsNullOrWhiteSpace(n.Name) && 154 (n.Name.Trim().Equals("All", System.StringComparison.OrdinalIgnoreCase) || 155 n.Name.Trim().StartsWith("All ", System.StringComparison.OrdinalIgnoreCase))); 156 if (allChild != null) 157 { 158 children = new System.Collections.Generic.List<NavigationTreeNodeViewModel> { allChild } 159 .Concat(children.Where(n => n != allChild)).ToList(); 160 } 161 } 162 } 163 164 @if (context != null) 165 { 166 <nav class="ms-catnav@(titleOnly ? " ms-catnav--title-only" : "")" aria-labelledby="ms-catnav-title-@Model.ID"> 167 <h2 class="ms-catnav-title" id="ms-catnav-title-@Model.ID">@context.Name</h2> 168 @if (!titleOnly) 169 { 170 <ul class="ms-catnav-list"> 171 @if (allChild == null) 172 { 173 <li class="ms-catnav-item"> 174 @if (allSelected) 175 { 176 <span class="ms-catnav-pill is-current" aria-current="page">@Translate("All")</span> 177 } 178 else 179 { 180 <a class="ms-catnav-pill" href="@context.Link">@Translate("All")</a> 181 } 182 </li> 183 } 184 @foreach (var node in children) 185 { 186 <li class="ms-catnav-item"> 187 @if (node.IsActive) 188 { 189 <span class="ms-catnav-pill is-current" aria-current="page">@node.Name</span> 190 } 191 else if (node == allChild && allSelected) 192 { 193 <a class="ms-catnav-pill is-current" href="@node.Link">@node.Name</a> 194 } 195 else 196 { 197 <a class="ms-catnav-pill" href="@node.Link">@node.Name</a> 198 } 199 </li> 200 } 201 </ul> 202 } 203 </nav> 204 } 205

            Wheels & Tires

            Find the perfect wheels and tires for your bike, from lightweight carbon road bike wheels to versatile options for mountain, hybrid, and gravel bikes. Enhance your performance with reliable and durable components.