Buidling menu from a list - need how to group/distinct

Sep 13, 2010 at 3:25 AM

Hi All,
      I'm trying to build a menu from a sharepoint list as you can see below but i need to group the main menu's and then add links to the
appropiate main menus.I have seperated the links using the LinkType column if is a header or a header with a link.
But what i need help with is to kind of get the distinct of the Group columns or how to group the groups columns


My data  looks like this:

Title   URL             LinkType        Groups
Test    http://Test	Header Link     Comms
Test2d	http://Test2	Header          Marketing
Test3   http;//Test3	Header		Comms
Test4   http://test4	Header		Plans
Test5   http://Test5	Header Link	Marketing

 

Any help would be appreciated.Thanks

 

<script  type="text/javascript">
$(document).ready(function() {
 $().SPServices({
    operation: "GetListItems",
    async: false,
    listName: "Menu",
    CAMLViewFields: "<ViewFields></ViewFields>",
    completefunc: function (xData, Status) {
      $(xData.responseXML).find("[nodeName=z:row]").each(function() {
        var title= $(this).attr("ows_Title");
        var groups = $(this).attr("ows_Groups");
        var LinkType = $(this).attr("ows_LinkType");



//For testing
$("#print").append(title);


var HTMLmarkup = '';
$(".sf-menu").each(function(){

if (LinkType == 'Header') {

   HTMLmarkup += '<li class="current"><a href=#>' + groups + '</a>';
   HTMLmarkup += '<ul>';
   HTMLmarkup += '<li><a href="#">' + title+ '</a></li>';
   HTMLmarkup += '</ul>'
   HTMLmarkup += '</li>';
   $(".sf-menu").append(HTMLmarkup);
}else if (LinkType == 'Header Link'){
  //HTMLmarkup += '<li class="current"><a href="#a">' + groups + '</a>';
  HTMLmarkup += '<li class="current"><a href=' + title + '>' + groups + '</a>';
   HTMLmarkup += '<ul>';
   HTMLmarkup += '<li><a href="#">' + title+ '</a></li>';
   HTMLmarkup += '</ul>'
   HTMLmarkup += '</li>';
$(".sf-menu").append(HTMLmarkup);
}

});




      });
    }
    
  });
  
});

</script>

 

 

Coordinator
Sep 13, 2010 at 5:10 PM

I think (assuming I understand the question) that the issue is more with your data model. Are the headers "linked" to header links? If so, you should have a column for that relationship. It may even make sense to have two separate lists.

Usually I'd do something like this with a DVWP rather than jQuery.

M.

Coordinator
Sep 13, 2010 at 10:12 PM
Edited Sep 13, 2010 at 10:15 PM

Like Marc, I generally do this type of stuff with a DVWP, but I have had a couple scenarios where I had to use SPServices to write it with jQuery.

There is no "Distinct" query ability with the CAML, so what I've done in the past is query the source list and iterate through all of the data. For each row, push the group name into an array with a key defined to that group name (this basically overwrites the previous entry if it's already there). Once that query has completed, you're left with a JavaScript array containing just the group names (the count of that array will also match how many unique groups you have).

So the completefunc looks something like this:

 

completefunc: function (xData, Status) {
  var groupNames = new array();
  $(xData.responseXML).find("[nodeName=z:row]").each(function() {
    groupNames[$(this).attr("ows_Groups")] = $(this).attr("ows_Groups");
  });
}

 

You can then iterate through your groupNames array and rerun the query with filters to pull out that specific group. This changes a bit if you're allowing people to select multiple groups for a header to be displayed.

I wouldn't recommend this for large scale use as you're essentially double pulling the data because of the query to figure out the group names. In a small application the additional overhead is negligible but the better, best practices approach here would be a DVWP.

If a DVWP is still not an option, you could clean it up a bit by making the groups a list of their own and have the group selection for the link be a lookup. Then you can iterate through the groups list and then call the nav list for each record. Still an extra call for the groups, but at least you're only pulling all of the groups instead of all of the data in the nav list.

Sep 14, 2010 at 1:20 AM

Thanks Marc and webses03.
Nice idea though but as you can see i'm building the html for a menu.
I have decided to put the the group in another list and them link it to the child list to get my submenus.
One thing i'm not sure how to do is how to iterate through 2 lists using SPSerivces
How to iterate through the groups and then add the submenus

Thanks in advance

Coordinator
Sep 14, 2010 at 2:12 AM

So if your groups are in another list, you'll want to do something like this.

First, call GetListItems to the Groups list (probably a good practice to specify a CAML Query to sort by Title so they come back alphabetically.

Then, your .each() function that iterates through the returned rows will want to write the header for the group, then call GetListItems to the list containing the other entries (this one will have a where clause in the CAML Query to only bring back rows that match the group you're currently cycling through). So for each header it writes it'll also make another query, You're just going to nest another SPServices call within the loop that iterates through the groups. Again, I'd probably specify a sort here to keep your data returning consistently.

Now just to take this a step further. Just because it's a nav doesn't mean you can't use a DVWP. Are you in a situation where you cannot use SharePoint Designer at all? DVWP's aren't just for displaying tabular data. I've used them to build charts and KPI's, navigation menus and trees, output content, and I know Marc's got loads of other examples too.

Sep 14, 2010 at 5:49 AM

webdes03,
             Thanks.
I have actually got to the stage where i have list the groups and sorted by Title asc.
The problem i have as i said is how to nest another SPservices so that i can send the group to the child list(my submenus)

Do you have any samples anywhere.Can't find any..

Coordinator
Sep 14, 2010 at 2:38 PM

You would just nest it inside the each() function of the first call...

So it'd look something like this:

completefunc: function (xData, Status) {
  $(xData.responseXML).find("[nodeName=z:row]").each(function() {
    $("#container").append($(this).attr("ows_Title"));//output header information
    $().SPServices //second call here
  });
}
You just want it to make a filtered call to the web service for every header.

Coordinator
Sep 14, 2010 at 3:46 PM

I want to go back to the DVWP vs. script point. If you aren't doing angthing "live" on the client side, then it's really a misuse of script. You should do the work on the server with a DVWP if the menu construct is constant and doesn't vary over the life of the page. Shifting to load to the client only makes sense if you have a deployment obstacle (no SharePoint Designer access allowed, for instance) or if the menu items change dynamically over the life of the page (not hide/display stuff, but actual content changes based on commits to a list or something).

M.

Sep 15, 2010 at 6:42 AM

Thanks Webdes03 for the tip.
Marc i was actually thinking of using DVWP but i taught i needed to add the link to the top nav bar and not like a webpart.So when i tested it and added the Jquery i got the menu in the top nav bar nice anc clean thats why i decided to do it using Jquery
and using you wonderful SPServices :)

Webdes03 this is what i did quickly below according to what you proposed and it did work but 'm sure if you guys look into my code sure i  could do better.
I would like to have a multi level menu the below has only one level for now.The menu is using the superfish Jquery component

Till i get to improve my Jquery skills hope to read from you:)

<script  type="text/javascript">
$(document).ready(function() {
 $().SPServices({
    operation: "GetListItems",
    async: false,
    listName: "MenuCategories",
    CAMLQuery: "<Query><OrderBy><FieldRef Name='Title'  Ascending='True'/></OrderBy></Query>",
    CAMLViewFields: "<ViewFields><FieldRef Name='Title'/></ViewFields>",
   
      completefunc: function (xData, Status) {
      $(xData.responseXML).find("[nodeName=z:row]").each(function() {
      
        var header= $.trim($(this).attr("ows_Title"));
      
var Headermarkup = '';
$(".sf-menu").each(function(){


Headermarkup += '<li class="current"><a href="#" id='+ header +'>' + header + '</a>';
Headermarkup += '</li>';
 

$(".sf-menu").append(Headermarkup);


//start submenu
  $().SPServices({
    operation: "GetListItems",
    async: false,
    listName: "Menu",
    CAMLQuery: "<Query><Where><Eq><FieldRef Name='Groups' Ascending='True'/><Value Type='Lookup'>" + header + "</Value></Eq></Where></Query>",
    CAMLViewFields: "<ViewFields><FieldRef Name='Groups'/><FieldRef Name='Title'/></ViewFields>",
    completefunc: function (xData, Status) {
      $(xData.responseXML).find("[nodeName=z:row]").each(function() {
        var title = $(this).attr("ows_Title");
        var grp = $(this).attr("ows_Groups");
        var sgrp = grp.substring(3);
        
        $("#print").append(title);
       
         var submenu = '';
         $('#'+ header).each(function(){
         $('<ul class='+ header +'>').insertAfter('#'+ header);
         $('.'+ header).append('<li><a href="#">' + title  + '</a></li>');
       

         });

      });
    }
    
  });

//end

});


      });
    }
    
  });
  
  
  
});

</script>

<script type="text/javascript">

		// initialise plugins
		jQuery(function(){
			jQuery('ul.sf-menu').superfish();
			

		});



		</script>


 

Coordinator
Sep 15, 2010 at 2:23 PM
Edited Sep 15, 2010 at 2:24 PM

Because your groups field is a lookup, you want to define your header variable as the ID for that row.

Your menu list isn't going to store the group name, but rather the id of the applicable header you selected.

I think if you change: var header= $.trim($(this).attr("ows_Title")); to: var header= $.trim($(this).attr("ows_ID")); you may see better results.

 

Sep 16, 2010 at 7:26 AM

Thanks webdes03

I would try that out