Trying to add custom links to menu

Vote:
 

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:
 

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:
 

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:
 

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:
 

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:
 

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.