Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    Jul 22, 2016 @ 13:30
    Morten Bock
    0

    Adding querystring to RoutPath on custom tree

    I'm building a custom section with a custom tree.

    The tree is sort of a filter of some underlying data. So my tree looks something like this:

    MyTree

    • Tag Group 1
      • Tag 1
      • Tag 2
    • Tag Group 2
      • Tag 3
      • Tag 4

    Now, when I click the "MyTree" root node, I go to a search page like this:

    /umbraco#/mysection/mytree/search/-1

    When I click the "Tag 3" node, I would like it to pass a parameter to that route, like this:

    /umbraco#/mysection/mytree/search/-1?tag=3

    But if I make me tree node like this:

    CreateTreeNode("3", -1, queryStrings, "Tag 3", "icon-tag", false,"mysection/mytree/search/-1?tag=3")
    

    Then the url I get is

    /umbraco#/mysection/mytree/search/-1%3Ftag=3

    Is there any way, from my tree controller, to add a parameter to the routePath, without encoding the "?", so that I can use $routParams.tag in my angular controller?

  • David Brendel 792 posts 2970 karma points MVP 3x c-trib
    Jul 22, 2016 @ 14:42
    David Brendel
    0

    Hi,

    would also be interested in a solution. Had the problem myself and in the end refactored my code as I didn't found a solution.

  • Ian 178 posts 752 karma points
    Jul 22, 2016 @ 20:25
    Ian
    0

    I don't know if this the 'correct' way and I was interested in modifying a key already present but you could try the same method I used recently.

    if (FormDataCollectionExtensions.HasKey(queryStrings, "tree") && string.IsNullOrEmpty(FormDataCollectionExtensions.GetValue<string>(queryStrings, "tree")))
                {
                    Dictionary<string, string> queryStringsDictionary = queryStrings.ToDictionary(x => x.Key, x => (string)x.Value);
                    queryStringsDictionary["tree"] = "topics";
                    queryStrings = new FormDataCollection(queryStringsDictionary);
                }
                ResourcesSectionApiController controller = new ResourcesSectionApiController();
                var topics = controller.GetTopics();
                foreach (var topic in topics)
                {
                    TreeNode node = this.CreateTreeNode(topic.Id.ToString(), "topics", queryStrings, topic.Name, "icon-book-alt-2", false, FormDataCollectionExtensions.GetValue<string>(queryStrings, "application") + StringExtensions.EnsureStartsWith(this.TreeAlias, '/') + "/edit-topic/" + topic.Id);
                    node.NodeType = "topic";
                    nodes.Add(node);
    
                }
    
  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    Jul 26, 2016 @ 07:42
    Morten Bock
    0

    Unfortunately, the query string collection modified here is the one sent to the server when asking for the childnodes of that node. It is not the client side angular route of the node.

  • Ian 178 posts 752 karma points
    Jul 26, 2016 @ 20:57
    Ian
    0

    Sorry for the duff reply. I did a little research and what i found seemed to suggest angular doesn't (or isn't in this case going to) support query strings.

    If you fancy doing a bit of route hijacking for angular (and copying can route out of umbraco route.js which i don't like) then a test of this worked to pass the tag value into routeparams.

    'use strict';
    var app = angular.module("umbraco");
    
    
    app.config(function ($routeProvider) {
        var canRoute = function (isRequired) {
    
            return {
                /** Checks that the user is authenticated, then ensures that are requires assets are loaded */
                isAuthenticatedAndReady: function ($q, userService, $route, assetsService, appState) {
                    var deferred = $q.defer();
    
                    //don't need to check if we've redirected to login and we've already checked auth
                    if (!$route.current.params.section
                        && ($route.current.params.check === false || $route.current.params.check === "false")) {
                        deferred.resolve(true);
                        return deferred.promise;
                    }
    
                    userService.isAuthenticated()
                        .then(function () {
    
                            assetsService._loadInitAssets().then(function () {
    
                                //This could be the first time has loaded after the user has logged in, in this case
                                // we need to broadcast the authenticated event - this will be handled by the startup (init)
                                // handler to set/broadcast the ready state
                                var broadcast = appState.getGlobalState("isReady") !== true;
    
                                userService.getCurrentUser({ broadcastEvent: broadcast }).then(function (user) {
                                    //is auth, check if we allow or reject
                                    if (isRequired) {
                                        // U4-5430, Benjamin Howarth
                                        // We need to change the current route params if the user only has access to a single section
                                        // To do this we need to grab the current user's allowed sections, then reject the promise with the correct path.
                                        if (user.allowedSections.indexOf($route.current.params.section) > -1) {
                                            //this will resolve successfully so the route will continue
                                            deferred.resolve(true);
                                        } else {
                                            deferred.reject({ path: "/" + user.allowedSections[0] });
                                        }
                                    }
                                    else {
                                        deferred.reject({ path: "/" });
                                    }
                                });
    
                            });
    
                        }, function () {
                            //not auth, check if we allow or reject
                            if (isRequired) {
                                //the check=false is checked above so that we don't have to make another http call to check
                                //if they are logged in since we already know they are not.
                                deferred.reject({ path: "/login/false" });
                            }
                            else {
                                //this will resolve successfully so the route will continue
                                deferred.resolve(true);
                            }
                        });
                    return deferred.promise;
                }
            };
        };
    
        $routeProvider.when('/mypackagesection/:tree/:method/:id/tag/:tag', {
            //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
            template: "<div ng-include='templateUrl'></div>",
            //This controller will execute for this route, then we replace the template dynamnically based on the current tree.
            controller: function ($scope, $route, $routeParams, treeService) {
    
                if (!$routeParams.tree || !$routeParams.method) {
                    $scope.templateUrl = "views/common/dashboard.html";
                }
    
                // Here we need to figure out if this route is for a package tree and if so then we need
                // to change it's convention view path to:
                // /App_Plugins/{mypackage}/backoffice/{treetype}/{method}.html
    
                // otherwise if it is a core tree we use the core paths:
                // views/{treetype}/{method}.html
    
                var packageTreeFolder = treeService.getTreePackageFolder($routeParams.tree);
    
                if (packageTreeFolder) {
                    $scope.templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
                        "/" + packageTreeFolder +
                        "/backoffice/" + $routeParams.tree + "/" + $routeParams.method + ".html";
                }
                else {
                    $scope.templateUrl = 'views/' + $routeParams.tree + '/' + $routeParams.method + '.html';
                }
    
            },            
            resolve: canRoute(true)
        })        
    })
    
    app.controller(
        "MyPackage.EditItemController",
        function ($scope, $routeParams)
        }
    );
    
  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    Jul 28, 2016 @ 07:34
    Morten Bock
    100

    I've "solved" it for now by simply adding a redirecting controller:

    angular.module("umbraco").controller("Foo.FoorByTagController",
        function($scope, $routeParams, $location, notificationsService, navigationService) {
    
            // This controller is basically just a way to generate the correct search url, and redirect to it.
            $location.search("tag", $routeParams.id);
            $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + "search" + "/" + "-1");
            $location.replace();
        });
    

    Angular does support this, so it looks more like the Umbraco API was not built for allowing to add them to routePaths from the server.

    Thanks for the example. but I think route hijacking seems a bit much just for this relatively simple task.

  • Ian 178 posts 752 karma points
    Jul 28, 2016 @ 08:04
    Ian
    1

    I saw location.search but thought you needed a query string for it to work, didnt know you could search within a route parameter. Glad you found your workaround

  • Markus Johansson 1909 posts 5733 karma points MVP c-trib
    Aug 22, 2020 @ 17:34
    Markus Johansson
    0

    Hi guys!

    I had the same issue, wanting to add a querystring to the route for TreeNode.

    In AngularJS one need to pass any qyerystring to the $location-service by calling the search()-method,

    $location.path('/foo').search({'queryParam1':'val'});
    

    Looking at the source for the navigation.controller.js-file we can see that Umbraco Core only calls $location.Path which will encode any query-parameters.

    But... there is a way to pass custom javascript to be executed on click: https://github.com/umbraco/Umbraco-CMS/blob/55c37e0a4efacfce48c3d776b2893dd076625049/src/Umbraco.Web.UI.Client/src/navigation.controller.js#L71

    By adding an item to the node.AdditionalData-dictionary we can have our own js-code executing on click. The nice thing is that the code is executed using js-eval() and thanks to closures we have access to all the variables in the scope.

    So in the TreeController-code (C#) when creating a TreeNode we can add this javascript function to set the search-parameters on the $location-service, like this:

    var dictionaryParameters = new Dictionary<string,string>();
    dictionaryParameters.Add("queryParam1","value");
    var queryParamsJsObj = JsonConvert.SerializeObject(dictionaryParameters).Replace("\"", "'");
    
    // Code in the js-function is copied from: https://github.com/umbraco/Umbraco-CMS/blob/55c37e0a4efacfce48c3d776b2893dd076625049/src/Umbraco.Web.UI.Client/src/navigation.controller.js#L93
    var js = @"(function(){
    
        //add action to the history service
        historyService.add({ name: n.name, link: n.routePath, icon: n.icon });
    
        //put this node into the tree state
        appState.setTreeState('selectedNode', args.node);
    
        //not legacy, lets just set the route value and clear the query string if there is one.
        $location.path(n.routePath);
        navigationService.clearSearch();
        $location.search(" + queryParamsJsObj + @");
    })()";
    
    node.AdditionalData.Add("jsClickCallback",js);
    

    Hope this helps if anyone has the same issue =D

    Cheers!

Please Sign in or register to post replies

Write your reply to:

Draft