I am using SharePoint's JavaScript Object Model in an AngularJS app and need one of its functions to execute on page load so an array is populated and used by an ng-repeat.
Currently, it logs to the console the array push on page load, but does not appear to commit the values into the array for use on the page/$scope until I click in/then out of a input box, click OK on an alert box, or a dummy link that keeps me on the page. This is where I am confused as to why it can log the array properly on load, but not have the array ready in $scope for the page.
I have tried the following, but without success:
1) I have moved the call to execute the function to the bottom of the controller
2) I have tried angular.element(document).ready
3) I have tried wrapping it in a function such as:
$scope.initTaxonomy = function () {
$(function () {
// Function here
});
};
What I don't understand is why the following, which calls to a REST service, executes on page load/works perfectly by pushing into $scope for use in an ng-repeat, while my function doesn't:
// Array holding items for display on homepage
$scope.items = [];
appItems.query(function (result) {
// Data is within an object of "value", so this pushes the server side array into the $scope array
var result = result.value;
var userInfo;
// Foreach result, push data into items array
angular.forEach(result, function (resultvalue, resultkey) {
$scope.items.push({
title: resultvalue.Title,
status: resultvalue.Status
});
});
});
This is the function that will successfully log to the console on page load, but won't populate the array in my ng-repeat until I click around on the page:
var termsArray = [];
$scope.termsArray = termsArray;
execOperation();
function execOperation() {
//Current Context
var context = SP.ClientContext.get_current();
//Current Taxonomy Session
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
//Term Stores
var termStores = taxSession.get_termStores();
//Name of the Term Store from which to get the Terms.
var termStore = termStores.getByName("Taxonomy_1111");
//GUID of Term Set from which to get the Terms.
var termSet = termStore.getTermSet("1111111");
var terms = termSet.getAllTerms();
context.load(terms);
context.executeQueryAsync(function () {
var termEnumerator = terms.getEnumerator();
while (termEnumerator.moveNext()) {
var currentTerm = termEnumerator.get_current();
var guid = currentTerm.get_id();
var guidString = guid.toString();
termsArray.push({
termName: currentTerm.get_name(),
termGUID: guidString,
});
//getLabels(guid);
}
console.log($scope.termsArray)
}, function (sender, args) {
console.log(args.get_message());
});
};
My controller is loaded with the page via app.js as follows:
$routeProvider.
when('/', { templateUrl: '/views/home.html', controller: 'appHomeItemsCtrl' }).
when('/add-item', { templateUrl: '/views/add-item.html', controller: 'appItemPostCtrl' }).
otherwise({ redirectTo: '/' });
Is this an issue with SharePoint's JSOM needing something to execute properly, how I am trying to execute in AngularJS, or something else?
I was able to get this working. The problem was that Angular doesn't appear to respect non-Angular functions, so using $scope.apply around the push of my array to $scope was effective. Note that wrapping the entire function caused issues with the digest of Angular:
var termsArray = [];
execOperation();
function execOperation() {
//Current Context
var context = SP.ClientContext.get_current();
//Current Taxonomy Session
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
//Term Stores
var termStores = taxSession.get_termStores();
//Name of the Term Store from which to get the Terms.
var termStore = termStores.getByName("Taxonomy_1111");
//GUID of Term Set from which to get the Terms.
var termSet = termStore.getTermSet("1111");
var terms = termSet.getAllTerms();
context.load(terms);
context.executeQueryAsync(function () {
var termEnumerator = terms.getEnumerator();
while (termEnumerator.moveNext()) {
var currentTerm = termEnumerator.get_current();
var guid = currentTerm.get_id();
var guidString = guid.toString();
termsArray.push({
termName: currentTerm.get_name(),
termGUID: guidString,
});
}
console.log(termsArray)
$scope.$apply(function () {
$scope.termsArray = termsArray;
});
}, function (sender, args) {
console.log(args.get_message());
});
};
Related
Im trying to update the scope after I get the response from Firebase (I am doing it in the app controler part), here is a sample code.
PS I am using AngularFire ,Firebase and Angularjs
Thank You
var app = angular.module("sampleApp", ["firebase"]);
firebase.initializeApp(config);
app.factory("mealMacros", ["$firebaseArray",
function($firebaseArray) {
var dbRef = firebase.database();
var userId = sessionStorage.uid;
var ordersRef = firebase.database().ref('user_info/' + userId + '/orders');
return $firebaseArray(ordersRef);
}
]);
app.controller("MealCtrl", ["$scope", "mealMacros",
function($scope, mealMacros) {
// $scope.user = "Guest " + Math.round(Math.random() * 100);
//tried mealMacros.on.. didnt work
ordersRef.on('value', function(snapshot) {
var orders = snapshot.val();
// Loop and parse order ids here
for (var key in orders) {
var orderId = orders[key]['orderType'];
console.log(orderId);
$scope.products = orderId;
//code not reaching here
}
});
}
]);
ordersRef.on('value', function(snapshot) {
var orders = snapshot.val();
// Loop and parse order ids here
for (var key in orders) {
var orderId = orders[key]['orderType'];
console.log(orderId);
$scope.products = orderId;
//code not reaching here
}
});
First you need to make sure you get data in var orders
Then $scope.products = orderId; you set the same variable in a loop. Maybe $scope.products is an array and you want to push data in it? $scope.products.push(orderId);
Finally, setting a $scope variable inside a .on event, you will need to call $scope.$apply() before the function returns.
ordersRef.on('value', function(snapshot) {
var orders = snapshot.val();
console.log(orders); // make sure there's an array here
for (var key in orders) {
var orderId = orders[key]['orderType'];
$scope.products.push(orderId);
}
$scope.$apply();
});
One last thing, var orderId = orders[key]['orderType']; may not work as expected depending on the structure of your var orders array.
You can try var orderId = key.orderType; if that doesn't work.
myData is generated in myFactory, and the following code works if myData is generated from a single myHttp.GetGroupMember function.
However, it is not working with multiple GetGroupMember calls as they execute "at the same time", and dataContainer gets initialized from createMyData call.
How can I call second GetGroupMember after first GetGroupMember is "finished"?
var myFactory = function (myHttp) {
var myData = [];
var dataContainer = [];
var numProgress;
var onSuccess = function(data_member) {
myData.push(data_member);
numProgress++;
loadMember(dataContainer); // if member is succefully loaded, load next member
// if numProgress == data_member.length, call next GetGroupMember() here?
}
var loadMember = function(data_group) {
if (numProgress < data_group.length) {
myHttp.getMemberDetails(data_group.division, data_group.members[numProgress]).then(onSuccess, onError);
}
}
var createMyData = function(response) {
dataContainer = response; // coded as shown to call loadMember() iteratively
loadMember(dataContainer);
}
var getMyData = function(init) {
myHttp.getGroupMember("Division1", "Group_Foo").success(createMyData); // getGroupMember_1
// how to call getGroupMember_2 after getGroupMember "finished"?
myHttp.getGroupMember("Division2", "Group_Bar").success(createMyData); // getGroupMember_2
return myData;
}
}
var myControl = function($scope, myFactory) {
$scope.myData = myFactory.getMyData();
}
Easiest thing in the world:
myHttp.getGroupMember("Division1", "Group_Foo")
.success(createMyData)
.then(function() {
return myHttp.getGroupMember("Division2", "Group_Bar")
}).success(createMyData);
Read up on promises.
So I have a small angular app that takes in a search query, sends it to an elasticsearch node I've got set up, and then displays the result set on screen.
My problem is that when I make a new query, the results gets appended to the end of the current result set. What I would like it to do is to erase whatever is currently on the page, and reload it with only the new data, much like how searching for something on Google returns a completely new set of results.
Is there any way to do this? Code below for reference.
// this is the controller that displays the reuslts.
var displayController = function($scope, $rootScope, $window, notifyingService) {
var dataReady = function(event, data) {
$scope.resultSet = notifyingService.getData();
}
$rootScope.$on('data-ready', dataReady)
}
app.controller("displayController", ["$scope", "$rootScope", "$window", "notifyingService", displayController]);
// this is the service that's responsible for setting the data
var notifyingService = function($http, $rootScope) {
var svc = {
_data: [],
setData: setData,
getData: getData
};
function getData() {
return svc._data;
}
function setData(data) {
var base_obj = data.hits.hits
console.log("Setting data to passed in data.");
console.log('length of dataset: ' + base_obj.length);
for(var i = 0; i < base_obj.length; i++){
svc._data.push(base_obj[i]._source);
}
$rootScope.$broadcast('data-ready', svc._data);
}
return svc;
};
app.factory("notifyingService", ["$http", "$rootScope", notifyingService]);
In setData just before the loop re-initialize svc._data
svc._data = [];
Clear you svc._data before you start adding the new query.
function setData(data) {
var base_obj = data.hits.hits;
svc._data = [];//reset your array before you populate it again.
for(var i = 0; i < base_obj.length; i++){
svc._data.push(base_obj[i]._source);
}
$rootScope.$broadcast('data-ready', svc._data);
}
I have view page where am trying to display all organizations,which is
obtained by a server call..In order to feel the application responsive
, in between the server response I want to load all local store
items.. But server call is always executing first.. The code I
mentioned bellow..
initialize: function()
{
var me = this,
st = Ext.create("Ext.data.Store", {
fields : [ {......................}]});
me.callParent(arguments);
me.setStore(st);
me.on({
show : me.onShow,
scope: me
});
},
onShow:function()
{
var me = this;
Ext.create('Ext.util.DelayedTask',
//call back function ,purpose : delayed exicution
function () {
me.DelShow(function(){
_syncMgr.getOrgGroup(-1,0,5); // servercall
});
}).delay(500);
},
DelShow: function(callback)
{
orgStore = Ext.getStore('Organizations'),
orgStore.load(function(records)
{
var i=0,len = records.length,
for(;i<len;i++)
{
organization = records[i];
regId = organization.get('rg_id');
resStr = organization.Resources();
resStr.load({callback:function(resorces)
{
var i = 0,rlen =resorces.length,
obj = {},
obj.rg_id = str.boundTo.get('rg_id');
}
orgViStr.add([obj]);
});
}
});
me.lodorg(callback);
},
lodorg:function(callback)
{
callback();
console.log("I don't know why this call back works first....");
console.log("plz help me to work last....");
}
Call You callback method after You load data from local store .
What you want to do is add the content from your local store to the list that is currently set in the view then refresh the list.
var localStore = something.getStore(),
entries = [];
localStore.each(function(entry){
entries.push(entry.copy);
});
//me is the list that is set in the view
me.add(entries);
me.deselectAll();
me.refresh();
Hope that helps :)
What I have is simple CRUD operation. Items are listed on page, when user clicks button add, modal pops up, user enters data, and data is saved and should automatically (without refresh)be added to the list on page.
Service:
getAllIncluding: function(controllerAction, including) {
var query = breeze.EntityQuery.from(controllerAction).expand(including);
return manager.executeQuery(query).fail(getFailed);
},
addExerciseAndCategories: function(data, initialValues) {
var addedExercise = manager.createEntity("Exercise", initialValues);
_.forEach(data, function(item) {
manager.createEntity("ExerciseAndCategory", { ExerciseId: addedExercise._backingStore.ExerciseId, CategoryId: item.CategoryId });
});
saveChanges().fail(addFailed);
function addFailed() {
removeItem(items, item);
}
},
Controller:
$scope.getAllExercisesAndCategories = function() {
adminCrudService.getAllIncluding("ExercisesAndCategories", "Exercise,ExerciseCategory")
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
$scope.queryItems = adminCrudService.querySucceeded(data);
var exerciseIds = _($scope.queryItems).pluck('ExerciseId').uniq().valueOf();
$scope.exerciseAndCategories = [];
var createItem = function (id, exercise) {
return {
ExerciseId: id,
Exercise : exercise,
ExerciseCategories: []
};
};
// cycle through ids
_.forEach(exerciseIds, function (id) {
// get all the queryItems that match
var temp = _.where($scope.queryItems, {
'ExerciseId': id
});
// go to the next if nothing was found.
if (!temp.length) return;
// create a new (clean) item
var newItem = createItem(temp[0].ExerciseId, temp[0].Exercise);
// loop through the queryItems that matched
_.forEach(temp, function (i) {
// if the category has not been added , add it.
if (_.indexOf(newItem.ExerciseCategories, i.ExerciseCategory) < 0) {
newItem.ExerciseCategories.push(i.ExerciseCategory);
}
});
// Add the item to the collection
$scope.items.push(newItem);
});
$scope.$apply();
}
Here is how I add new data from controller:
adminCrudService.addExerciseAndCategories($scope.selectedCategories, { Name: $scope.NewName, Description: $scope.NewDesc });
So my question is, why list isn't updated in real time (when I hit save I must refresh page).
EDIT
Here is my querySuceeded
querySucceeded: function (data) {
items = [];
data.results.forEach(function(item) {
items.push(item);
});
return items;
}
EDIT 2
I believe I've narrowed my problem !
So PW Kad lost two hours with me trying to help me to fix this thing (ad I thank him very very very much for that), but unfortunately with no success. We mostly tried to fix my service, so when I returned to my PC, I've again tried to fix it. I believe my service is fine. (I've made some changes as Kad suggested in his answer).
I believe problem is in controller, I've logged $scope.items, and when I add new item they don't change, after that I've logged $scope.queryItems, and I've noticed that they change after adding new item (without refresh ofc.). So probably problem will be solved by somehow $watching $scope.queryItems after loading initial data, but at the moment I'm not quite sure how to do this.
Alright, I am going to post an answer that should guide you on how to tackle your issue. The issue does not appear to be with Breeze, nor with Angular, but the manner in which you have married the two up. I say this because it is important to understand what you are doing in order to understand the debug process.
Creating an entity adds it to the cache with an entityState of isAdded - that is a true statement, don't think otherwise.
Now for your code...
You don't have to chain your query execution with a promise, but in your case you are returning the data to your controller, and then passing it right back into some function in your service, which wasn't listed in your question. I added a function to replicate what yours probably looks like.
getAllIncluding: function(controllerAction, including) {
var query = breeze.EntityQuery.from(controllerAction).expand(including);
return manager.executeQuery(query).then(querySucceeded).fail(getFailed);
function querySucceeded(data) {
return data.results;
}
},
Now in your controller simply handle the results -
$scope.getAllExercisesAndCategories = function() {
adminCrudService.getAllIncluding("ExercisesAndCategories", "Exercise,ExerciseCategory")
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
// Set your object directly to the data.results, because that is what we are returning from the service
$scope.queryItems = data;
$scope.exerciseAndCategories = [];
Last, let's add the properties we create the entity and see if that gives Angular a chance to bind up properly -
_.forEach(data, function(item) {
var e = manager.createEntity("ExerciseAndCategory");
e.Exercise = addedExercise; e.Category: item.Category;
});
So I've managed to solve my problem ! Not sure if this is right solution but it works now.
I've moved everything to my service, which now looks like this:
function addCategoriesToExercise(tempdata) {
var dataToReturn = [];
var exerciseIds = _(tempdata).pluck('ExerciseId').uniq().valueOf();
var createItem = function (id, exercise) {
return {
ExerciseId: id,
Exercise: exercise,
ExerciseCategories: []
};
};
// cycle through ids
_.forEach(exerciseIds, function (id) {
// get all the queryItems that match
var temp = _.where(tempdata, {
'ExerciseId': id
});
// go to the next if nothing was found.
if (!temp.length) return;
// create a new (clean) item
var newItem = createItem(temp[0].ExerciseId, temp[0].Exercise);
// loop through the queryItems that matched
_.forEach(temp, function (i) {
// if the category has not been added , add it.
if (_.indexOf(newItem.ExerciseCategories, i.ExerciseCategory) < 0) {
newItem.ExerciseCategories.push(i.ExerciseCategory);
}
});
// Add the item to the collection
dataToReturn.push(newItem);
});
return dataToReturn;
}
addExerciseAndCategories: function (data, initialValues) {
newItems = [];
var addedExercise = manager.createEntity("Exercise", initialValues);
_.forEach(data, function (item) {
var entity = manager.createEntity("ExerciseAndCategory", { ExerciseId: addedExercise._backingStore.ExerciseId, CategoryId: item.CategoryId });
items.push(entity);
newItems.push(entity);
});
saveChanges().fail(addFailed);
var itemsToAdd = addCategoriesToExercise(newItems);
_.forEach(itemsToAdd, function (item) {
exerciseAndCategories.push(item);
});
function addFailed() {
removeItem(items, item);
}
}
getAllExercisesAndCategories: function () {
var query = breeze.EntityQuery.from("ExercisesAndCategories").expand("Exercise,ExerciseCategory");
return manager.executeQuery(query).then(getSuceeded).fail(getFailed);
},
function getSuceeded(data) {
items = [];
data.results.forEach(function (item) {
items.push(item);
});
exerciseAndCategories = addCategoriesToExercise(items);
return exerciseAndCategories;
}
And in controller I have only this:
$scope.getAllExercisesAndCategories = function () {
adminExerciseService.getAllExercisesAndCategories()
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
$scope.items = data;
$scope.$apply();
}