Five New Optimizely Certifications are Here! Validate your expertise and advance your career with our latest certification exams. Click here to find out more

Trying to add custom links to menu

Vote:
0

I have a ContentArea called CustomNavigationItems.

This contains the following:

        [Required]
        [CultureSpecific]
        [MinItemCount(1)]
        [MaxItemCount(1)]
        [Display(GroupName = SystemTabNames.Content, Order = 10)]
        public virtual LinkItemCollection Link { get; set; }

        [CultureSpecific]
        [UIHint(UIHint.Textarea)]
        [Display(GroupName = SystemTabNames.Content, Order = 20)]
        public virtual string Text { get; set; }

        [UIHint(UIHint.Image)]
        [Display(GroupName = SystemTabNames.Content, Order = 30)]
        public virtual ContentReference Icon { get; set; }

        [Required]
        [AppSettingsSelection("navigation.custom.buttontype")]
        [Display(GroupName = SystemTabNames.Content, Order = 40)]
        public virtual string Type { get; set; }

        [CultureSpecific]
        [Display(GroupName = SystemTabNames.Content, Order = 50)]
        public virtual string ButtonText { get; set; }

        [CultureSpecific]
        [MinItemCount(0)]
        [MaxItemCount(3)]
        [Display(GroupName = SystemTabNames.Content, Order = 60)]
        public virtual LinkItemCollection SubLink { get; set; }

In my Navigation.cshtml I have this:

@helper ComplexLayoutSubItemTemplate(HtmlHelpers.MyMenuItem item)
{
    <li @((item.HasChildren || item.CustomMenuItems.FilteredItems.Any()) ? "class=has-level-3" : "")>
        <a tabindex="-1" href="@Url.ContentUrl(item.Page.ContentLink)" @(item.HasChildren ? "data-toggle=show-children role=button" : "")>
            @Html.PropertyFor(_ => item.Page.NavigationPageImage, new { ImgCssClass = "icon", Width = "165", Height = "200" })
            <span>@item.Page.NavigationTeaserTitle</span>
        </a>

        @if (item.HasChildren || item.CustomMenuItems.FilteredItems.Any())
        {
            <ul class="level-3 nav">
                <li>
                    <a href="@Url.ContentUrl(item.Page.ContentLink)">
                        <img class="icon" src="@Url.ContentUrl(item.Page.NavigationPageImage)" alt="">
                        <span>
                            <strong>@item.Page.NavigationTeaserTitle</strong>
                            <span class="visible-lg">@((!string.IsNullOrEmpty(item.Page.NavigationTeaserText)) ? item.Page.NavigationTeaserText : item.Page.TeaserText)</span>
                            <span class="hidden-lg">@((!string.IsNullOrEmpty(item.Page.NavigationShortTeaserText)) ? item.Page.NavigationShortTeaserText : item.Page.NavigationTeaserText)</span>
                        </span>
                        <div class="btn btn-primary btn-lg" href="@Url.ContentUrl(item.Page.ContentLink)">@Html.Translate("/navigation/overviewPage")</div>
                    </a>
                </li>

                @Html.MenuList(item.Page.ContentLink, ComplexLayoutLevel3SubItemTemplate)

                @foreach (var customItem in (item.CustomMenuItems ?? new ContentArea()).LoadContent<CustomNavigationBlock>())
                {
                    LinkItem link = customItem.Link.First();
                    if (link != null)
                    {
                <li>
                    <a tabindex="-1" href="@Url.PageUrl(link.Href)" target="@link.Target" title="@link.Title">
                        @GetCostumIcon(customItem)
                        <span>
                            <strong>@link.Text</strong>
                            <span>@customItem.Text</span>
                            <div class="btn btn-link btn-cta pb-0 mehrDataLayer">@Html.Translate("/navigation/more") <span class="arrow"></span></div>
                        </span>
                    </a>

                </li>
                    }
                }

                <li>
                    <a href="@Url.ContentUrl(item.Page.ContentLink)" class="btn-link btn-cta">@Html.Translate("/navigation/showAll") <span class="arrow"></span></a>
                </li>
            </ul>
        }
    </li>
}

Right after the 

</span>

</a>

I would like to access the link items within SubLink, but I am having a hell of a hard time grasping it. 

I would like to do something like this:

			<ul class="level-4 list-inline">
                        <li>
                            <a href="SubLinkItem1.Url" target="SubLinkItem1.Target" title="SubLinkItem1.Title">SubLinkItem1.Text <span class="arrow"></span></a>
                        </li>
            </ul>

I have tried accessing: @customItem.SubLink but the ouput is:

<ul><li><a href="/link/d76209b3121e4033ac5b80dec57cd592.aspx">Test Link</a></li></ul>

I have also tried this:

                    <ul class="level-4 list-inline">
                        @foreach (var subLink in customItem.SubLink)
                        {
                            <li>
                                <a href="@Url.PageUrl(subLink.Href)" target="@subLink.Target" title="@subLink.Title">@subLink.Text <span class="arrow"></span></a>
                            </li>
                        }
                    </ul>

But the result is the following error when trying to load the website:

[NullReferenceException: Object reference not set to an instance of an object.]
   at ASP._Page_Views_Shared_Navigation_cshtml.<>c__DisplayClass4_0.b__0(TextWriter __razor_helper_writer) in D:\home\site\wwwroot\Views\Shared\Navigation.cshtml:line 177
   at ProjectDigital.Site.Helpers.HtmlHelpers.MenuList(HtmlHelper helper, ContentReference rootLink, Func`2 itemTemplate, Boolean includeRoot, Boolean requireVisibleInMenu, Boolean requirePageTemplate)
   at ASP._Page_Views_Shared_Navigation_cshtml.<>c__DisplayClass2_0.b__0(TextWriter __razor_helper_writer) in D:\home\site\wwwroot\Views\Shared\Navigation.cshtml:line 98
   at ProjectDigital.Site.Helpers.HtmlHelpers.RootMenuList(HtmlHelper helper, ContentReference rootLink, Dictionary`2 itemTemplates, Boolean includeRoot, Boolean requireVisibleInMenu, Boolean requirePageTemplate)
   at ASP._Page_Views_Shared_Navigation_cshtml.Execute() in D:\home\site\wwwroot\Views\Shared\Navigation.cshtml:line 245
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
   at System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper htmlHelper, String partialViewName, Object model)
   at ASP._Page_Views_Shared_Header_cshtml.Execute() in D:\home\site\wwwroot\Views\Shared\Header.cshtml:line 107
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
   at System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper htmlHelper, String partialViewName, Object model)
   at ASP._Page_Views_Shared_Layouts__Base_cshtml.Execute() in D:\home\site\wwwroot\Views\Shared\Layouts\_Base.cshtml:line 90
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
   at System.Web.WebPages.WebPageBase.<>c__DisplayClass3.b__2(TextWriter writer)
   at System.Web.WebPages.WebPageBase.Write(HelperResult result)
   at System.Web.WebPages.WebPageBase.RenderSurrounding(String partialViewName, Action`1 body)
   at System.Web.WebPages.WebPageBase.PopContext()
   at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.b__1e(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)



I would like to be able to access the actual individual elements of the links contained within the 
LinkItemCollection group called SubLink.

And would really appreciate any help in this direction.

#255127
Edited, May 19, 2021 20:06
Vote:
0

For anyone reading this later on, my problem was that I was not taking care of null references. Especially in the foreach loop I added:

<ul class="level-4 list-inline">
                        @foreach (var subLink in customItem.SubLink)
                        {
                            <li>
                                <a href="@Url.PageUrl(subLink.Href)" target="@subLink.Target" title="@subLink.Title">@subLink.Text <span class="arrow"></span></a>
                            </li>
                        }
                    </ul>

So I modified like so:

@if (customItem.SubLink != null) {
	<ul class="level-4 list-inline">
		@foreach (var subLink in customItem.SubLink)
		{
			<li>
				<a href="@Url.PageUrl(subLink.Href)" target="@subLink.Target" title="@subLink.Title">@subLink.Text <span class="arrow"></span></a>
			</li>
		}
	</ul>
}

And it worked.

#255212
May 20, 2021 13:02
Vote:
0

Hi,

I think you're nearly there with that last approach. I suspect the issue is that you need some null checking on the attributes of the link like this:

<ul class="level-4 list-inline">
    @foreach (var subLink in customItem.SubLink)
    {
        <li>
            <a href="@Url.ContentUrl(new EPiServer.Url(subLink.Href))" target="@(subLink.Target ?? "")" title="@(subLink.Title ?? "")">@(subLink.Text ?? "") <span class="arrow"></span></a>
        </li>
    }
</ul>

In the example, I've also used ContentUrl rather than PageUrl as PageUrl is there for legacy backwards-compatibility reasons.

#255213
May 20, 2021 13:10
Vote:
0

Thank you very much for the suggestion Paul, that is an even more complete check for null references. I will incorporate that in my code.

But in your example, there does not appear to be a check for null references in ContentUrl? Am I correct?

#255228
Edited, May 20, 2021 19:23
Vote:
0

Yes, you're correct. I'd assumed the null check had been done before that point. Putting it all together it would be:

@if (customItem.SubLink != null) {
<ul class="level-4 list-inline">
    @foreach (var subLink in customItem.SubLink)
    {
        <li>
            <a href="@Url.ContentUrl(new EPiServer.Url(subLink.Href))" target="@(subLink.Target ?? "")" title="@(subLink.Title ?? "")">@(subLink.Text ?? "") <span class="arrow"></span></a>
        </li>
    }
</ul>
}
#255288
May 21, 2021 9:42
Vote:
0

Sorry I was not thinking, you are of course right episervers link item collection object will force the user to enter a URL, so this field will never be empty.

#255294
May 21, 2021 18:15
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.