Service Portal Header Menu Issue

paku
Kilo Expert

Hi,

I'm new to the Service Portal and tried to customize the Header Menu.

After some research I found out that there is currently a problem with cloning the Header Menu.

I'm aware of the issue with the spDropdownTree directive and tried to create my own angular provider directive in the service portal as mentioned on https://github.com/service-portal/documentation/issues/50.

So far I created the following angular provider directive:

Type: Directive

Name: spDropdownCustom

/*! RESOURCE: /scripts/app.$sp/directive.spDropdownTree.js */

/*   */

angular.module('sn.$sp').directive('spDropdownCustom', function () {

return {

restrict: 'E',

scope: {items: '='},

replace: true,

template: '<ul class="dropdown-menu">' +

'<li ng-repeat="mi in items" style="min-width: 20em;" ng-class="{\'dropdown-submenu\': mi.type == \'menu\', \'dropdown-menu-line\':$index < items.length - 1}" ng-include="getURL()">' +

'</ul>',

link : function(scope, element, attrs, controller) {

scope.getURL = function() {

return 'spDropdownTreeTemplate_custom';

}

}

}

});

After that, I changed the directive call in the menuTemplate and spDropdownTreeTemplate_custom from <sp-dropdown-tree to <-sp-dropdown-custom.

The templates are available on your instance under https://yourservicenowinstance.service-now.com/sp_ng_template_list.do?sysparm_query=&sysparm_first_r...

But my scripted list (OOTB Request Menu Entry) wont want to show the dropdown menu as seen in the following picture:

no_dropdown.JPG

Does anyone know if my angular provider directive is correct or knows how I can get my dropdown to work again?

Maybe nathanfirth?

1 ACCEPTED SOLUTION

paku
Kilo Expert

Okay so apparently I'm also new to angularjs and just found the article from Nathan about how to create an angular directive in service portal.



I removed the code "angular.module('sn.$sp').directive('spDropdownCustom'," and the corresponding bracket from the angular provider directive and the dropdown works fine again.



The final code of my directive is now:



  1. /*! RESOURCE: /scripts/app.$sp/directive.spDropdownTree.js */  
  2. function () {  
  3. return {  
  4. restrict: 'E',  
  5. scope: {items: '='},  
  6. replace: true,  
  7. template: '<ul class="dropdown-menu">' +  
  8. '<li ng-repeat="mi in items" style="min-width: 20em;" ng-class="{\'dropdown-submenu\': mi.type == \'menu\', \'dropdown-menu-line\':$index < items.length - 1}" ng-include="getURL()">' +  
  9. '</ul>',  
  10. link : function(scope, element, attrs, controller) {  
  11. scope.getURL = function() {  
  12. return 'spDropdownTreeTemplate_custom';  
  13. }  
  14. }  
  15. }  
  16. };

View solution in original post

23 REPLIES 23

Shannon Burns
Kilo Sage

I am having an issue with this as well.   This was a helpful post in trying to explain what is going on with this.   But I tried copying the text above for the provider, and I think I have done what I needed with redirecting the templates as well.   But my menu just looks like it's loading for a few seconds and then changes the color of the menu, and rendors all links on the page inoperable.   Can anyone see something I am missing?   I really don't understand angular.   The intent is for me to change the shopping cart, but I even took that part out for now just to try to get this to work.   But I am obviously not understanding something.



I highlighted the parts that I changed to point to the custom angular.   This is the code from the various parts of this attempted change.



ITSC Header Menu (HMTL)


<ul class="nav navbar-nav">


  <div class="header-loader" ng-show="loadingIndicator">


      <div class="hidden-xs sp-loading-indicator la-sm">


          <div></div>


          <div></div>


          <div></div>


      </div>


  </div>


  <li ng-repeat="item in data.menu.items" ng-class="{dropdown: item.items.length > 0}" ng-include="'ITSCmenuTemplate'"></li>


  <!-- Shopping cart stuff -->


  <li ng-if="options.enable_cart && data.isLoggedIn" class="dropdown hidden-xs">


  <a href="javascript:void(0)"


          data-toggle="dropdown"


          id="cart-dropdown"


          uib-tooltip-template="'item-added-tooltip.html'"


          tooltip-placement="bottom"


          tooltip-trigger="'none'"


          tooltip-is-open="$parent.itemAddedTooltipOpen"


          title="${Cart}">


      <i class="fa fa-shopping-cart" aria-hidden="true"></i>


          <span ng-bind-html="'${Cart}'"></span>


          <span ng-if="cartItemCount > 0" class="label label-as-badge label-primary sp-navbar-badge-count">{{cartItemCount}}</span>


</a>


      <div class="dropdown-menu cart-dropdown">


          <sp-widget widget="data.cartWidget"></sp-widget>


      </div>


  </li>


  <li ng-if="options.enable_cart && data.isLoggedIn" class="dropdown visible-xs">


  <a href="?id=sc_cart"


          title="${Cart}">


      <i class="fa fa-shopping-cart" aria-hidden="true"></i>


          <span ng-bind-html="'${Cart}'"></span>


          <span ng-if="cartItemCount > 0" class="label label-as-badge label-primary sp-navbar-badge-count">{{cartItemCount}}</span>


</a>


  </li>


  <script>


  <div ng-click="toggleCart()" class="item-added-tooltip">${Item has been added to your cart.}</div>


  </script>


</ul>



Angular template - itscMenuTemplate


<a ng-if="item.items.length == 0 && !item.scriptedItems" ng-href="{{item.href}}" target="{{item.url_target}}">{{ item.label }}</a>


<a ng-if="item.items.length > 0" href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ item.label }} <span class="caret"></span></a>


<ul ng-if="item.items.length > 0" class="dropdown-menu" role="menu">


      <li ng-repeat="item in item.items" ng-include="'itscMenuTemplate'" />


</ul>


<a ng-if="item.scriptedItems.count > 0" href="javascript:void(0)" data-toggle="dropdown" title="{{item.hint}}">


      <span ng-bind-html="item.label"></span>


      <span ng-if="!item.scriptedItems.omitBadge" class="label label-as-badge label-primary sp-navbar-badge-count">{{item.scriptedItems.count}}</span>


</a>


<itsc-sp-dropdown-tree ng-if="item.scriptedItems.count > 0" items="item.scriptedItems.items" />




Angular template - itscSpDropdownTreeTemplate


<a ng-if="mi.type == 'link'" title="{{mi.title}}" target="{{mi.target}}" href="{{mi.href}}">


  {{mi.title | characters:60}}


</a>


<a ng-if="mi.type == 'record' && !mi.__page" title="{{mi.short_description}}" href="?id=ticket&table={{mi.__table}}&sys_id={{mi.sys_id}}">


  <span>{{mi.short_description | characters:60}}</span>


  <span class="block color-primary text-muted">


      <span class="block" style="float: right">


          <sn-time-ago timestamp="mi.sys_updated_on" />


      </span>


      {{mi.number}}


  </span>


</a>


<a ng-if="mi.type == 'record' && mi.__page" title="{{mi.short_description}}" href="?id={{mi.__page}}&table={{mi.__table}}&sys_id={{mi.sys_id}}">


  <span>{{mi.short_description | characters:60}}</span>


  <span class="block color-primary text-muted">


      <span class="block" style="float: right">


          <sn-time-ago timestamp="mi.sys_updated_on" />


      </span>


      {{mi.number}}


  </span>


</a>


<a ng-if="mi.type == 'request'" title="{{mi.short_description}}" href="?id=sc_request&table={{mi.__table}}&sys_id={{mi.sys_id}}">


  <span>{{mi.short_description | characters:60}}</span>


  <span class="block color-primary text-muted">


      <span class="block" style="float: right">


          <sn-time-ago timestamp="mi.sys_updated_on" />


      </span>


      {{mi.number}}


  </span>


</a>


<a ng-if="mi.type == 'approval'" title="{{mi.short_description}}" href="?id=approval&table={{mi.__table}}&sys_id={{mi.sys_id}}">


  <span ng-if="mi.short_description">{{mi.short_description | characters:60}}</span>


  <span class="block color-primary text-muted">


      <span class="block" style="float: right">


          <sn-time-ago timestamp="mi.sys_updated_on" />


      </span>


      {{mi.number}}


  </span>


</a>


<a ng-if="mi.type == 'menu' && mi.items.length" title="{{mi.title}}" href="javascript:void(0)" class="menu_trigger right-caret">


  {{mi.title | characters:60}}


</a>


<itsc-sp-dropdown-tree ng-if="mi.type == 'menu' && mi.items.length" items="mi.items" />



Angular provider - type Directive - itscSpDropdownTree


/*! RESOURCE: /scripts/app.$sp/directive.spDropdownTree.js */


function () {


return {


restrict: 'E',


scope: {items: '='},


replace: true,


template: '<ul class="dropdown-menu">' +


'<li ng-repeat="mi in items" style="min-width: 20em;" ng-class="{\'dropdown-submenu\': mi.type == \'menu\', \'dropdown-menu-line\':$index < items.length - 1}" ng-include="getURL()">' +


'</ul>',


link : function(scope, element, attrs, controller) {


scope.getURL = function() {


return 'itscSpDropdownTreeTemplate';


}


}


}


};


(function($) {


$("body").on( "click", "a.menu_trigger", function(e) {


var current = $(this).next();


var grandparent = $(this).parent().parent();


if ($(this).hasClass('left-caret') || $(this).hasClass('right-caret'))


$(this).toggleClass('right-caret left-caret');


grandparent.find('.left-caret').not(this).toggleClass('right-caret left-caret');


current.toggle();


$(".dropdown-menu").each(function(i, elem) {


var elemClosest = $(elem).closest('.dropdown');


var currentClosest = current.closest('.dropdown');


if (!elem.contains(current[0]) && elem != current[0] && (!currentClosest.length || !elemClosest.length || elemClosest[0] == currentClosest[0]))


$(elem).hide();


})


e.stopPropagation();


});


$("body").on( "click", "a:not(.menu_trigger)", function() {


var root=$(this).closest('.dropdown');


root.find('.left-caret').toggleClass('right-caret left-caret');


});


})(jQuery);


;



Thanks,


Shannon


Hi Shannon,



Don't forget to add Angular Provider to the custom Header widget which you created like below-



find_real_file.png



It works for me.


Thanks!



Regards,


Surya Nair


Exact same issue here too... did everything correctly and when the menu loads, it instantly changes color and becomes unusable.

I notice though these console errors:

find_real_file.png

 

And here's the setup:

find_real_file.png

Gurpreet07
Mega Sage

I wasted my whole day to find out what i am doing wrong... And then found this thread. At last able to replicate the header.. Thanks!!!

Community Alums
Not applicable

This post literally saved my day. Thanks to paku, suryanair, Shannon Burns for your comments.
Awesome...and THANK YOU.