cloudscribe/cloudscribe.Web.Navigation

CurrentNode with same Controller and action

Closed this issue · 14 comments

Hi,
I've a menu like the sample below.
In menu I generate an url with this structure: /Page/Details?id=1, /Page/Details?id=2, ....

My problem is the CurrentNode value. It takes always the first item (in this case "Orientamento").
There is a way to do get the CurrentNode that consider the url parameter?

Thank you for your support.


<NavNode key="Orientamento" parentKey="ServiceStudents" controller="Page" action="Details" customData="1" text="Orientamento e carriera" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">
          <Children>
            <NavNode key="OrientamentoStudi" parentKey="Orientamento" controller="Page" action="Details" customData="2" text="Orientamento agli studi" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">
              <Children />
            </NavNode>
            <NavNode key="OrientamentoCarriera" parentKey="Orientamento" controller="Page" action="Details" customData="3" text="Orientamento alla carriera" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">
              <Children />
            </NavNode>
            <NavNode key="OrientamentoBigInterview" parentKey="Orientamento" controller="Page" action="Details" customData="4" text="Big Interview" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">
              <Children />
            </NavNode>
            <NavNode key="OrientamentoBlogLavoro" parentKey="Orientamento" controller="Page" action="Details" customData="5" text="Blog sul lavoro" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">
              <Children />
            </NavNode>
            <NavNode key="OrientamentoVisualizzazioneCandidature" parentKey="Orientamento" controller="Page" action="Details" customData="6" text="Visualizzazione candidature" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">
              <Children />
            </NavNode>
            <NavNode key="OrientamentoInoltroCandidature" parentKey="Orientamento" controller="Page" action="Details" customData="7" text="Invio candidature Jobgate" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">
              <Children />
            </NavNode>
          </Children>
        </NavNode>

@Netclick19 I've edited your post to make the XML visible!

Hi,
there is a method to achieve the goal?
Thank you for your support.

Hi,

any news?

Thank you for your support.

Hi @Netclick19 sorry we're a little pressured with summer holidays and some very busy client projects - will look into this next week.

Thank you for your answer.
No problem, it's just to know if there is a way to do that or I've to change my approach.

Hi @Netclick19

I've spent a while looking at your issue, and I can see what you are trying to achieve - using a customData attribute to point multiple navigation links to the same controller and same action - but with different Ids.

Having debugged into the code, then it does seem to me that the component as it currently stands is sadly not able to do what you want, that is to figure out the currentNode in a way that takes into account the different Ids on the query string.

If you want to look at the source code, the relevant code pathway is (NavigationViewModel.cs)
RootNode.FindByUrl(urlHelper, context.Request.Path, nodeSearchUrlPrefix);

which then calls into (TreeNodeExtensions.cs)
FindByUrlExact(currentNode, urlHelper, urlToMatch, urlPrefix);

That method declares a delegate function that attempts to discover the current node by matching the current URL against those for each node in the navigation tree... but it only makes use of the context.Request.Path in that code pathway – i.e. just the URL with no query parameters.

So it matches any available navigation nodes purely in controller and action... and so it always seems to find the first node in the tree that uses that same controller and action, regardless of any query parameters.

I experimented briefly to see whether you could perhaps put the id into the route, i.e. instead of
/Page/Details?id=1
you could link to
/Page/Details/1
...but even then, I was still hitting the same problem (you could experiment with that further)

I'm not easily able to debug into that delegate function to look at this more closely, but it seems to me that to achieve what you want, the delegate function might have to be re-worked to handle any additional query string parameters too.

I did notice that there is scope in NavigationViewModel.cs to inject a custom nodeFinder (line 127) but even with that - it's happening too late (after currentNode has already been figured out, to the wrong node), and again it is not getting passed the query string parameters.

So I'm not seeing a way to do that without a code change to the cloudscribe component. If we were doing that then I think we might need to consider whether this would be an enhancement, or if there might be scenarios where it would be unwanted, or risk other bugs. (e.g. I could imagine a scenario where you were using some kind of dynamic value, timestamp etc on the query string, and you wanted that to be ignored.)

One other possible strategy - (this might be useless to you - you might know this already) -
but if you are using Cloudscribe SimpleContent, and if all you want is a navigation menu that links to specific pages, you can always create those pages manually using the SimpleContent system (create a new page with the "standard content editor") and then use the Override Url property in the Settings tab of that page, to create a nav item that routes directly to the specific page with the required id value. Then under Administration > Page Management, you can manage your menu items that way.

Regards
Jim

I've tried to use the url property with /Page/Details/1 but nothing changed.

It's seem that this piece of code in TreeNodeExtensions.cs doesn't handle the segment /1

if (!string.IsNullOrEmpty(n.Value.Url))
{
      if (n.Value.Url.Equals(urlToMatch, StringComparison.OrdinalIgnoreCase))
            { return true; }
}

That was confusing me too when I tried the same thing.

Because it's a delegate function that gets called for every node (until it find one that matches) then I was failing to get a debug breakpoint into it -

but I was just adding stuff like
Console.WriteLine(urlToMatch);
...to try and make sense of it.

I failed to make a consistent set of links though -
I was trying for
/Page/Details/1
/Page/Details/2
/Page/Details/3

but after I visited /Page/Details/3 then the link targets were changing to
/Page/Details/3/1
/Page/Details/3/2
/Page/Details/3/3

...which didn't help!

I'm guessing you must have modified /customsed the views like Bootstrap4NavigationNodeChildDropdownPartial somehow, to make use of this customData value... it doesn't appear in the URLs automatically.

Hi @Netclick19 your question seems similar to this one #89 and that answer should apply here too (an action for each nav node, in turn calling another).

Also can I ask whether you are using SimpleContent in your solution? If not, have you considered it? We cannot tell from what you have said above what your Details action is doing, but if it is presenting html content (e.g. from a database) then using cloudscribe SimpleContent might give you an alternative solution?

@JimKerslake Yes, I modified the BootstrapNavigationNodeChildDropdownPartial to use the CustomData property.
I only did this because with the url property it didn't work. By itself I just need to add a parameter to the action and for CurrentNode and ParentChain to be able to figure it out and not take the first one in the list.

@CrispinF I can't use this solution, because I have 100 pages.... I just need an action that will read the page content based on the id and present it.

Sorry... I had written in italian....

@JimKerslake translations:

Yes, I modified the BootstrapNavigationNodeChildDropdownPartial to use the CustomData property.
I did this only because with the url property it didn't work. In itself I only need to add a parameter to the action and that CurrentNode can understand it and not take the first one on the list.

I can't use this solution, because I have 100 pages... I just need an action that reads the content of the page based on the id and presents it.

Hi @Netclick19

I think I found a method that seems to work for me.

If you can deliver your content pages from routing like this:
/Page/Details/1
/Page/Details/2
/Page/Details/3
(I guess you need to implement that in your Page controller)

and then
if you change your navigation.xml to not use customData, but instead use the Url property:

<NavNode key="OrientamentoStudi" url="/Page/Details/2" parentKey="Orientamento" text="Orientamento agli studi" iconCssClass="flaticon-line-graph" preservedRouteParameters="countryId" viewRoles="StudentFB,Administrator">

but you need to do that for every Page node in the xml... especially that parent Orientamento one

and also go back to the BootstrapNavigationNodeChildDropdownPartial view and revert any changes you made in there to use the customData property... just use cloudscribe's default version of that view.

When I do all of those things, then I find that this bit of code that you mentioned before will correctly match the full url with its /1 or /2 etc.

if (!string.IsNullOrEmpty(n.Value.Url))
{
      if (n.Value.Url.Equals(urlToMatch, StringComparison.OrdinalIgnoreCase))
            { return true; }
}

When you mentioned that bit of code yesterday, you were not using the url property in navigation.xml, so you would have skipped over that if condition, and gone further down into later code that matches purely on the controller name and action... and that falsely matches the first parent node.

So you need to make sure that every node in your navigation xml that is referencing a /Page/Details page is defined in that way above, using "Url" but removing the Controller and Action properties.

Hope that helps
Jim

Hi,
thank you. With this change all is working well!

Kind regards!