I am querying the contents of the Managed Metadata using the code below. I am encountering an error
The collection has not been initialized
when I am in the var level2TermsEnum = level2Terms.getEnumerator();
I have read that this is because of the deferred and promise of JavaScript and I can't seem to understand it. Maybe you could help me shed some light on here.
$(document).ready(function () {
var scriptbase = _spPageContextInfo.webServerRelativeUrl + "/_layouts/15/";
$.getScript(scriptbase + "SP.Runtime.js",
function () {
$.getScript(scriptbase + "SP.js", function () {
$.getScript(scriptbase + "SP.Taxonomy.js", function () {
context = SP.ClientContext.get_current();
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
//Term Stores
var termStores = taxSession.get_termStores();
//Term Store under which to create the group.
var termStore = termStores.getByName("Managed Metadata Service");
var termSet = termStore.getTermSet("7b6ee52v-3709-4181-a14d-b953f2ad0aad");
//Call your code here.
GetTermsFromTaxonomyStore();
});
});
});
var json = "";
function GetTermsFromTaxonomyStore() {
//Current Context
var context = SP.ClientContext.get_current();
//Current Taxonomy Session
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
var termStores = taxSession.get_termStores();
//Term Store under which to create the term.
//var termStore = taxSession.getDefaultSiteCollectionTermStore();
var termStore = termStores.getByName("Managed Metadata Service");
//Pass ID of the Meetings Term Set
var termSet = termStore.getTermSet("7b6ee52v-3709-4181-a14d-b953f2ad0aad");
var terms = termSet.get_terms();
context.load(terms);
context.executeQueryAsync(function () {
var level1Terms = terms.getEnumerator();
while (level1Terms.moveNext()) { //iterate thru Level1
var level1 = level1Terms.get_current();
if (level1.get_termsCount() > 0) { //check if Level1 has child
var level2Terms = level1.get_terms(); //get level2 terms of level 1 term
**var level2TermsEnum = level2Terms.getEnumerator();**
while (level2TermsEnum.moveNext())
{
var level2Term = level2TermsEnum.get_current();
var level2TermName = level2Term.get_name();
termsList += '"Level1":"' + level2Term.get_name() + '","Level2":"' + level2TermName + '"';
}
}
//console.log(currentTerm.get_name());
}
alert(termsList);
}, function (sender, args) {
console.log(args.get_message());
});
}
When the client object model returns a collection of objects for you, if each of those objects has its own sub-collections, they won't be initialized unless you explicitly ask for them to be loaded.
Similar to the answer provided here, you should be able to update your call to context.load() with an additional parameter telling it what to load.
I believe something like the below code will work, but I haven't tested it:
context.load(terms,"Include(Name,Terms,Terms.Include(Name))");
Related
I'm setting up a custom html form that uses javascript to add items into a SharePoint list. I need to get a list of terms from SharePoint 2016 managed metadata. Its a list of terms for office locations. I want to get these values and show them in a custom html drop down. Is this possible?
We can use the SharePoint web service to get the terms from managed metadata.
/_vti_bin/TaxonomyClientService.asmx
Or we can use JSOM in SharePoint to achieve it.
var termsList = "Terms: \n"
function showTerms(termSetId) {
//We need to load and populat the matching Term Set first.
var termSetEnum = termSets.getEnumerator();
while (termSetEnum.moveNext()) {
var currentTermSet = termSetEnum.get_current();
if (currentTermSet.get_id() == termSetId) {
//If termSet Matches, then get all terms.
context.load(currentTermSet);
context.executeQueryAsync(
function () {
//Load terms
var terms = currentTermSet.get_terms();
context.load(terms);
context.executeQueryAsync(
function () {
var termsEnum = terms.getEnumerator();
while (termsEnum.moveNext()) {
var currentTerm = termsEnum.get_current();
var termName = currentTerm.get_name();
var termId = currentTerm.get_id();
termsList += termName + ": " + termId;
//Check if term has child terms
if (currentTerm.get_termsCount() > 0) {
//Term has sub terms.
recursiveTerms(currentTerm, 1);
}
alert(termList);
}
},
function () {
//failure to load terms.
});
},
function () {
//failure to load current term set
});
break;
}
}
}
function recursiveTerms(currentTerm, nestedLoop) {
//Loop count for formatting purpose.
var loop = nestedLoop + 1;
//Get Term child terms
var terms = currentTerm.get_terms();
context.load(terms);
context.executeQueryAsync(
function () {
var termsEnum = terms.getEnumerator();
while (termsEnum.moveNext()) {
var newCurrentTerm = termsEnum.get_current();
var termName = newCurrentTerm.get_name();
termId = newCurrentTerm.get_id();
//Tab Out format.
for (var i = 0; i < loop; i++) {
termsList += "\t";
}
termsList += termName + ": " + termId;
//Check if term has child terms.
if (currentTerm.get_termsCount() > 0) {
//Term has sub terms.
recursiveTerms(newCurrentTerm, loop);
}
}
},
function () {
//failure to load terms
}
);
}
The following articles for your reference.
Using jQuery to return the Managed Metadata Term GUID
Accessing Taxonomy Term Store with JSOM
I am using the below JavaScript code to get the title of all lists in SharePoint 2013. How can i adapt this to return the URL of each list as well?
I've tired this but it doesn't work:
//listUrl = oList.get_url();
//console.log(listUrl);
Code:
function retrieveAllListProperties() {
var clientContext = new SP.ClientContext('/StrategicProjectOffice');
var oWebsite = clientContext.get_web();
this.collList = oWebsite.get_lists();
clientContext.load(collList);
clientContext.executeQueryAsync(
Function.createDelegate(this, this.onQuerySucceeded)
);
}
function onQuerySucceeded() {
var listTitle = '';
var listEnumerator = collList.getEnumerator();
while (listEnumerator.moveNext()) {
var oList = listEnumerator.get_current();
listTitle = oList.get_title();
//listUrl = oList.get_url();
//console.log(listUrl);
if (listTitle.indexOf("SPO") >= 0) {
getItemsFromView(listTitle, "All Tasks");
}
}
}
List Url could be retrieved via SPList.RootFolder property, in your example the line:
clientContext.load(collList);
needs to be replaced with
clientContext.load(collList,'Include(RootFolder.ServerRelativeUrl)');
which tells to construct a query to return RootFolder.ServerRelativeUrl property of List object.
Example
Here is my version which retrieves lists and prints its url:
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var lists = web.get_lists();
ctx.load(lists,'Include(RootFolder.ServerRelativeUrl)');
ctx.executeQueryAsync(
function () {
for(var i = 0; i < lists.get_count(); i++){
var list = lists.getItemAtIndex(i);
var listUrl = list.get_rootFolder().get_serverRelativeUrl();
console.log(listUrl);
}
},
function(sender,args){
console.log(args.get_message());
}
);
Like the title says I want to retrieve the contenttype name of list items with Javascript. to be concrete: User opens the "new form" in list a and with Javascript and CSR there should be an alert of the content type name of list items in list b. To do this I tried the following:
var collListItem = null;
var contentobject = null;
var ctx = null;
var oList = null;
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function() {
$(document).ready( function() {ExecuteOrDelayUntilScriptLoaded(loadConstants, "sp.js")});
}
});
function loadConstants() {
ctx = new SP.ClientContext.get_current();
var web = ctx.get_web();
ctx.load(web);
var listcol = web.get_lists();
ctx.load(listcol);
var oList = listcol.getByTitle('Aktionslisten');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where><Geq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>1</Value></Geq></Where></Query></View>');
collListItem = oList.getItems(camlQuery);
ctx.load(collListItem);
ctx.executeQueryAsync(Function.createDelegate(this, this.onSuccess), Function.createDelegate(this, this.onFail));
}
function onSuccess(sender, args) {
var listInfo = '';
var listEnumerator = collListItem.getEnumerator();
while (listEnumerator.moveNext()){
oList = listEnumerator.get_current();
var ID = oList.get_id();
contentobject = oList.get_contentType();
ctx.load(contentobject);
ctx.executeQueryAsync(function(){
var value = contentobject.get_name();
alert("VAL: "+value);
},function(){alert("No Success");});
}
}
function onFail(sender, args) {
console.log("Errorlog: "+ args.get_message());
}
But this code just gives me the content type of the last item a few times.
I think I'm maybe doing something wrong with the "executeQuery" function?
Best regards,
André
Update (see comments below)
The new try for the code, which is also not working:
var collListItem = null;
var ctx = null;
var oList = null;
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function() {
$(document).ready( function() {ExecuteOrDelayUntilScriptLoaded(loadConstants, "sp.js")});
}
});
function loadConstants() {
ctx = new SP.ClientContext.get_current();
var web = ctx.get_web();
ctx.load(web);
var listcol = web.get_lists();
ctx.load(listcol);
var oList = listcol.getByTitle('Aktionslisten');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where><Geq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>1</Value></Geq></Where></Query></View>');
collListItem = oList.getItems(camlQuery);
ctx.load(collListItem);
ctx.executeQueryAsync(Function.createDelegate(this, this.onSuccess), Function.createDelegate(this, this.onFail));
}
function onSuccess(sender, args) {
var listInfo = '';
var listEnumerator = collListItem.getEnumerator();
while (listEnumerator.moveNext()){
oList = listEnumerator.get_current();
getcontenttypetitle(oList.get_contentType(), ctx);
}
}
function onFail(sender, args) {
console.log("Errorlog: "+ args.get_message());
}
function getcontenttypetitle(contentobject,clientContext){
this.object = contentobject;
clientContext.load(object);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onSuccess2), Function.createDelegate(this,this.onFail2));
}
function onSuccess2 (sender,args){
alert("VAL: "+ this.object.get_name());
}
function onFail2(sender,args){
alert("fail");
}
//$(":input[title='Aktionsliste']").find('option:contains(Teammeetings)').remove();
The reason why it alerts the same thing multiple times is because when your callback to executeQueryAsync is executed, any variables in the closure refer to the actual variables in that scope. That is, a closure captures the variable contentobject, not its current value when you call executeQueryAsync. Since it refers to the variable, its callback value is the value at the end of the loop. To fix it, you need to create a separate closure for each contentobject. To see how that's done, read this answer on a question about this problem: https://stackoverflow.com/a/19324832/2407870.
You were closer with your earlier revision. Here is the part from your first version that you need to change. (Note the comment in the code.)
while (listEnumerator.moveNext()){
oList = listEnumerator.get_current();
var ID = oList.get_id();
contentobject = oList.get_contentType();
ctx.load(contentobject);
ctx.executeQueryAsync(
// !! Here we use an IIFE (immediately invoked function expression)
// to create a new callback each time through the loop.
// We pass in the contentobject into our IIFE,
// which causes the closure to be around the variable
// inside the scope of our IIFE,
// instead of inside the scope of the function containing the while loop.
(function (contentobject) {
return function () {
var value = contentobject.get_name();
alert("VAL: "+value);
};
}(contentobject)),
function () {
alert("No Success");
}
);
}
I have a JavaScript Win Pivot Application
Into the Hub I am retrieving some information:
function initPages(options) {
for (var i = 0; i < options.length ; i++) {
var menuItem = options[i];
menuItem.showBanner = (i == 0);
definePages(options);
}
}
and in a .Js file I have the definePages function created:
functions.js:
function definePages(item) {
var action = item[0];
var animation = item[1];
var scyfy = item[2];
var localmovies = item[3];
var clasic = item[4];
var comedy = item[5];
var biography = item[6];
var drama = item[7];
var kids = item[8];
var musical = item[9];
var romantic = item[10];
var suspense = item[11];
var horror = item[12];
var art = item[13];
var personalities = item[14];
var history = item[15];
var society = item[16];
}
Now, in my section 1 I initialize the page by calling another function there:
ready: function (element, options) {
// TODO: Inicializar la página aquí.
options = options || {};
initMovies();
},
function initMovies() {
var element = document.getElementById("movieContainer");
//var movies = ??????????????????????????
//console.log(movies);
//it keeps going
}
I need to be able to retrive, in that var movies, the var action, from the functions.Js or, which is the same, the items[0]...
However, if I call a function in functions.Js, which is defined in section1Page, it won´t work...
I can call functions and pass data from anywhere to functions.Js, but not the other way around...
Any ideas on what should I do? Thanks!!!
I fixed it... I created a global var in function.Js and I get the info from the array in each section later on:
function definePages(item) {
tooSleepyToThink = item;
}
section1Page:
function initMovies() {
var elemento = document.getElementById("movieContainer");
console.log(tooSleepyToThink[0].text);
}
I have a collaborative list in google's realtime api, which is displayed in a select tab.
Currently I have this.
var managment = (function($) {
var deadline1 = undefined;
// the name of the authenticated user - once authentication has succeeded
var username = "unkown";
var realtimeLoader = undefined;
var initializeModel = function(model) {
var deadline1 = model.createList();
model.getRoot().set('deadline1', deadline1);
}
var onProjectsLoaded = function(doc) {
deadline1 = doc.getModel().getRoot().get('deadline1');
deadline1.addEventListener(gapi.drive.realtime.EventType.VALUES_ADDED, updateUi);
deadline1.addEventListener(gapi.drive.realtime.EventType.VALUES_REMOVED, updateUi);
deadline1.addEventListener(gapi.drive.realtime.EventType.VALUES_SET, updateUi);
$("#inviteId").removeAttr("disabled");
var inviteButton = $("#invite");
inviteButton.removeAttr("disabled");
inviteButton.click(inviteUser);
var saveButton = $("#save");
saveButton.click(addProject);
var updateButton = $("#demoListSet");
updateButton.click(onSetItems);
var selector = $("#demoListInput");
selector.change(onSelect);
}
var addProject = function() {
var title = $("#title").val();
var date = $("#datepicker").val();
var priority = $("#priority").val();
var description = $("#description").val();
deadline1.push( {
title : title,
date : date,
description : description,
priority : priority,
} );
}
var updateUi = function(evt) {
/*$("#demoListInput").empty();*/
var projectList = $("#demoListInput");
for (var index in evt.values) {
var deadline1 = evt.values[index];
var newOption = $("<option>").val(deadline1.title).text("Title: " + deadline1.title + " Date:" + deadline1.date + " Priority: " + deadline1.priority)
}
projectList.append(newOption);
}
My problem is with the updateUI function.
The list is called deadline1. When an item is added to the list, it updates the list but the view of this list in the select tab, This is because I am only looping through the evt.values when displaying new items. What I would like to do it delete the current view of the list each time an event is fired (see the line currently in comment), however, every time I try and call deadline1 to loop through the length of it, it comes back saying deadline1 is undefined. Is there a way to get around this? Thank you.