Adding a Child Node Link to Heading - SharePoint Navigation using JavaScript
-
20-10-2020 - |
Question
I'm currently trying to create a "Child Node" using Javascript by grabbing the users input and selection of their "Heading" where the Child node is going to go under.
function addNavigationNodesChild() {
var idOfParentNode = $('#selectChildParentNavNode').val();
var nameOfParentNode = $('#selectChildParentNavNode option:selected').text();
var clientContextChild = new SP.ClientContext(siteRelURL);
if (clientContextChild != undefined && clientContextChild != null) {
var webChild = clientContextChild.get_web();
var quickLaunchNodeCollectionGet = webChild.get_navigation().get_quickLaunch();
var quickLaunchNodeCollectionGetData = quickLaunchNodeCollectionGet.get_item(idOfParentNode).get_children();
for (var c = 0; c < navArrayChild.length; c++) {
var navObjectChild = navArrayChild[c];
var navTitleChild = navObjectChild.title;
var navUrlChild = navObjectChild.url;
var navIsExternalChild = navObjectChild.isExternal;
// Set properties for a new navigation node.
var nnciChild = new SP.NavigationNodeCreationInformation();
nnciChild.set_title(navTitleChild);
nnciChild.set_url(navUrlChild);
nnciChild.set_isExternal(navIsExternalChild);
// Create node as the last node in the collection.
nnciChild.set_asLastNode(true);
quickLaunchNodeCollectionGetData.add(nnciChild);
}
clientContextChild.load(quickLaunchNodeCollectionGetData);
clientContextChild.executeQueryAsync(onQueryNavSucceededChild, onQueryNavFailedChild);
console.log(quickLaunchNodeCollectionGetData);
}
}
The error I'm getting with this is: Cannot Read Property "get_children" of undefined. Any help is greatly appreciated.
Reference: idOfParentNode is being pulled from a different script which actually works and pulls the correct id's of all the Headings. I've assigned the script that pulls these id's to populate a drop down that users can select from with values.
Example in my HTML:
<option value="5">Five</option>
Where 5 is the idOfParentNode - also my Header that I want a child node to go into.
EDIT: I have also tried this, with error.
function addNavigationNodesChild() {
var idOfParentNode = $('#selectChildParentNavNode').val();
var nameOfParentNode = $('#selectChildParentNavNode option:selected').text();
var clientContextChild = new SP.ClientContext(siteRelURL);
if (clientContextChild != undefined && clientContextChild != null) {
var webChild = clientContextChild.get_web();
var quickLaunchNodeCollection = webChild.get_navigation().getNodeById(idOfParentNode);
console.log(quickLaunchNodeCollection);
for (var c = 0; c < navArrayChild.length; c++) {
var navObjectChild = navArrayChild[c];
var navTitleChild = navObjectChild.title;
var navUrlChild = navObjectChild.url;
var navIsExternalChild = navObjectChild.isExternal;
// Set properties for a new navigation node.
var nnciChild = new SP.NavigationNodeCreationInformation();
nnciChild.set_title(navTitleChild);
nnciChild.set_url(navUrlChild);
nnciChild.set_isExternal(navIsExternalChild);
// Create node as the last node in the collection.
nnciChild.set_asLastNode(true);
quickLaunchNodeCollection.get_children.add(nnciChild);
quickLaunchNodeCollection.update();
}
clientContextChild.load(quickLaunchNodeCollection);
clientContextChild.executeQueryAsync(onQueryNavSucceededChild, onQueryNavFailedChild);
console.log(quickLaunchNodeCollection);
}
}
Here, I'm using getNodeById method but I get an error stating webChild.get_navigation(...).getNodeById is not a function
Solution
Solved with this solution.
function addNavigationNodesChild() {
var idOfParentNode = $('#selectChildParentNavNode').val();
var nameOfParentNode = $('#selectChildParentNavNode option:selected').text();
var titleOfNavNodeChild = document.getElementById('addNav1Child').value;
var urlOfNavNodeChild = document.getElementById('addNav2Child').value;
var clientContextChild = new SP.ClientContext(siteRelURL);
if (clientContextChild != undefined && clientContextChild != null) {
var webChild = clientContextChild.get_web();
var quickLaunchNodeCollection = webChild.get_navigation().get_quickLaunch();
clientContextChild.load(quickLaunchNodeCollection);
clientContextChild.executeQueryAsync(function () {
var e = quickLaunchNodeCollection.getEnumerator();
var notFound = true;
while (notFound && e.moveNext()) {
var parentNode = e.get_current();
if (parentNode.get_title() === nameOfParentNode) {
var childrenNode = parentNode.get_children();
notFound = false;
console.log("found it ")
var nnciChild = new SP.NavigationNodeCreationInformation();
nnciChild.set_title(titleOfNavNodeChild);
nnciChild.set_url(urlOfNavNodeChild);
nnciChild.set_isExternal(true);
// Create node as the last node in the collection.
nnciChild.set_asLastNode(true);
childrenNode.add(nnciChild);
clientContextChild.executeQueryAsync(onQueryNavSucceededChild, onQueryNavFailedChild);
}
}
});
}
}
OTHER TIPS
Here is a more modern example (change get_topNavigationBar
for get_quickLaunch
as needed) using typescript and async/await:
async addNavigationNode(title, url, parentTitle, external = true) {
// Helper fn to wrap JSOM call into awaitable promise
const executeQuery = async (context: SP.ClientContext) =>
new Promise((resolve, reject) => context.executeQueryAsync(resolve, reject));
// Get ctx, web and top navigation items
const ctx = new SP.ClientContext();
const web = ctx.get_web();
const nav = web.get_navigation().get_topNavigationBar();
// Load and execute so we can iterate the collection
ctx.load(nav);
await executeQuery(ctx);
// Create the new node
const newNode = new SP.NavigationNodeCreationInformation();
newNode.set_title(title);
newNode.set_url(url);
newNode.set_isExternal(external);
newNode.set_asLastNode(true);
// Figure out where to put it: under a parent if specified, otherwise just add to the collection
let parentNode: SP.NavigationNode = null;
if (parentTitle) {
// Look for the parent. Iterate through the top-level items
const e = nav.getEnumerator();
while (e.moveNext()) {
const node = e.get_current();
// Found it, set the parent node and stop iterating
if (node.get_title() === parentTitle) {
parentNode = node;
break;
}
}
// Add the new node as a child of the parent we just found
if (parentNode) {
const children = parentNode.get_children();
children.add(newNode);
ctx.load(children);
}
} else {
// No parent: add the new node as a child of the top-level collection
nav.add(newNode);
}
// Execute again to perform the operation
ctx.load(nav);
return await executeQuery(ctx);
}
I find it easier to just add using this function, then go into the site settings navigation area to re-order items.