I need some help understanding how to structure an AngularJS controller.
The controller is to execute an Async REST call to SharePoint and put the JSON results into a $scope.SearchResults property.
The Async REST call is executed by the getDataSP() function and the $scope.SearchResults is assigned in the onQuerySuccess() callback. The onQuerySuccess() method has the statement
console.log('Search Results Returned.' + $scope.SearchResults);
which does display the expected "[object Object]" so I know it is returning something.
However the HTML page has the following tag to show the JSON object. I also expect it to display "[object Object]" but it never shows up!
<span >{{SearchResults}}<span>
Here is the code for the entire module.
(function () {
var app = angular.module('FormApp', []);
app.controller('FormController', ['$scope', function ($scope) {
var context = SP.ClientContext.get_current();
var user = context.get_web().get_currentUser();
function onQuerySuccess() {
if (results && results.m_value && results.m_value.ResultTables && results.m_value.ResultTables[0] && results.m_value.ResultTables[0]) {
$scope.SearchResults = results.m_value.ResultTables[0];
console.log('Search Results Returned.' + $scope.SearchResults);
} else {
results = {};
console.log('Search Results Failed');
}
}
function onQueryFail(sender, args) {
console.log('Query failed. Error:' + args.get_message());
}
function getDataSP() {
var keywordQuery = new Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
keywordQuery.set_queryText("*");
keywordQuery.set_sourceId("d0aa2c2e-7709-46f1-835a-244f483e4b0e");
var managedProperties = ['RefinableString00'];
var kwqProperties = keywordQuery.get_selectProperties();
for (var i = 0; i < managedProperties.length; i++) {
kwqProperties.add(managedProperties[i]);
}
var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
results = searchExecutor.executeQuery(keywordQuery);
context.executeQueryAsync(onQuerySuccess, onQueryFail)
};
getDataSP();
$scope.ProjectSelected = function (proj) {
$scope.SelectedProj = proj;
};
}
]);
angular.bootstrap(document.body, ['FormApp']);
})();
you need to notify Angular that you changed the model. Usually when your code is triggered or the callback handled by Angular, it checks automatically, but in this case, it doesn't know anything about your callback. You can do this simply by calling $scope.$apply() at the end of your callback.
some more info: https://github.com/angular/angular.js/wiki/When-to-use-$scope.$apply()
Related
So I have a question that i've been trying to solve for hours.
My problem is that I'm getting the data faster than my services to load.
gen-service
function genEmpId() {
settingsService.getSettings().then(function (data) {
var comId = data.data[0].companyId;
console.log(comId);
var test = comId + ' - ';
return test;
});}
controller
function genId() {
var data = genService.genEmpId();
console.log(data); // getting the data too fast how to put a callback ?
}
So when my controller load its calling the service but im getting an undefined return value.
try this, In your code you not returning anything and another thing is it's async call to you have to wait until it finishes.
// gen-service
function genEmpId() {
return settingsService.getSettings().then(function (data) {
var comId = data.data[0].companyId;
console.log(comId);
var test = comId + ' - ';
return test;
});
}
// controller
function genId() {
var data = genService.genEmpId().then(function (data) {
console.log(data);
})
}
Currently I developing cordova application by using Angularjs framework. I want to get value return $cordovaSQLite.execute() function. Usually I get the value of return by $cordovaSQLite.execute() by using .then(result()). However, the value result from .then(result()) function cannot used in outside .then(result()). Is it any solution for me to get value like result.rows.item(0).UserName; outside of my .then(result()) function. The following is my code.
var channelNameQuery = "SELECT * FROM chat_friend WHERE UserName=?"
var channelNamePromise = $cordovaSQLite.execute(db, channelNameQuery, [channelName]).then(function (result){
var abc = result.rows.item(0).UserName;
console.log(abc);
})
Next, I try with following code to get ChannelNamePromise value. It failed to get the value.
var abc = channelNamePromise.rows.item(0).UserName;
console.log(abc);
You can do it like this.
Let's just create a global method inside AppCtrl so that you can use it in multiple controllers.
.controller('AppCtrl', function ($scope,$rootScope) {
$rootScope.getFromDB = function (channelName) {
var deferred = $q.defer();
var items = [];
var query = "SELECT * FROM chat_friend WHERE UserName=?";
$cordovaSQLite.execute(db, query, [channelName]).then(function (res)
{
for (var index = 0; index < res.rows.length; index++) {
var item = res.rows.item(index);
items.push(item);
}
deferred.resolve(items);
}, function (err) {
deferred.reject(items);
console.error(err);
});
return deferred.promise;
};
})
Now in your controller
.controller('ExampleCtrl', function ($scope,$rootScope) {
$rootScope.getFromDB('yourChannelName').then(function (data) {
console.log('Result'+data); //success block, do whatever you want
});
})
You can also assign the result to any array, like this
.controller('ExampleCtrl', function ($scope,$rootScope) {
$scope.resultArr = $rootScope.getFromDB('yourChannelName').then(function (data) {
$scope.resultArr = result; //do whatever you want here
});
//outside then block you can use it.
console.log("Result"+ $scope.resultArr);
})
I would like to call a function in mg-repeat and this function call a http request with id for find list data but when i try this i have one error message this.
this a call of function :
<div ng-repeat="ListeReponse in reponsefonction(Listechamps.keyQuestion)" >
<label class="col-xs-4 control-label">
{{ListeReponse.reponse}}
</label>
</div>
this is a function :
$scope.reponsefonction = function(idQuestion)
{
var defer = $q.defer();
return RequestService.get('question/'+idQuestion+'/reponse').success(function(data)
{
defer.resolve(data);
})
return defer.promise;
}
My Service :
app.factory('RequestService', function ($http, WEB_SERVICE_URL)
{
var requestService = {};
requestService.get = function (type)
{
var response = $http.get(WEB_SERVICE_URL.url+type);
return response;
};
// ajout
requestService.post = function (type, data)
{
var response = $http.post(WEB_SERVICE_URL.url+type, data);
return response;
};
requestService.put = function (type, data)
{
var response = $http.put(WEB_SERVICE_URL.url+type, data);
return response;
};
return requestService;
})
error message :
docs.angularjs.org/error/$rootScope/infdig?p0=10&p1=%5B%5D
You created an infitie loop. Each call will trigger another call. Angular evaluates the responseFunction everytime the calls return. You will need to re-work your app a little:
Do not call a function in ng-repeat. Instead simply link it a to a variable. Fill the variable once on startup with a get query.
I don't think ng-repeat works with promises. In this case the ng-repeat is looping through "null/undefined" which causes this error.
Try this:
html:
<div ng-repeat="ListeReponse in getResponses(Listechamps.keyQuestion)" >
<label class="col-xs-4 control-label">{{ListeReponse.reponse}}</label></div>
controller:
var idQuestions = {}; // keep track of the entire list
$scope.getResponses = function(idQuestion) {
var notInitialized = idQuestions[idQuestion] == null;
idQuestions[idQuestion] = notInitialized ? [] : idQuestions[idQuestion]; // initialize to empty array
if (notInitialized) {
$scope.reponsefonction(idQuestion); //kicks off ajax request to get data
}
return idQuestions[idQuestion];
}
$scope.reponsefonction = function(idQuestion) {
RequestService.get('question/' + idQuestion + '/reponse').success(function(data) {
//assuming data is an []
Array.prototype.push.apply(idQuestions[idQuestion], data); //update the same array object to prevent multiple $digest cycles
}
}
}
I have a function with a return however in the function there is an async request which holds the value that is suppose to be returned by the function. I understand with the nature of async request the function will complete and not return a value while waiting on the async function to complete.
I attempted to use dojo deferred functions to have my function PostInformation() to return a value within the ajax request callback. I am having some issues and i am not sure where my issue is. Under is my code:
Dojo Deferred Function
function PostInformation(){
var hasErrors = false;
var containers = [dijit.byId("container1"), dijit.byId("container2")];
var Employee = {
//data
};
var def = new dojo.Deferred();
def = dojo.xhrPost({
url: 'hello',
content: Employee,
load: function (data) {
formErrors = {
"errors": true,
"fName": "123",
"surname": "456",
"oNames": "789",
"bSurname": "784585"
};
//formErrors = (JSON.parse(data)).formErrors;
$.each(formErrors, function (key, value) {
if (key == 'errors') {
hasErrors = value;
//console.log('hasErrors set to '+value);
}
});
if (hasErrors == true) {
for (var i = 0; i < containers.length; i++) {
var processingContainer = containers[i];
dojo.forEach(processingContainer.getChildren(), function (wid) {
var widgetName = wid.attr('id');
$.each(formErrors, function (key, value) {
if (key == widgetName && value.length > 0) {
var myWidget = dijit.byId(widgetName);
//var wdgName = dijit.byId(widgetName).attr("id");
var myWidgetValue = value;
myWidget.validator = function () {
//console.log('Attribute Name is :' + wdgName + ' Error Value is : ' + myWidgetValue);
//console.log(wdgName + " : "+myWidgetValue);
this.set("invalidMessage", myWidgetValue);
};
myWidget._hasBeenBlurred = true;
myWidget.validate();
}
});
});
}
}
console.log(hasErrors);
def.resolve(hasErrors);
},
error: function(err){
console.log(err);
def.reject(err);
}
});
def.then(function(data){
console.log('In the then function');
//alert('In the def.then and the results is : ' + data);
if(data == true){
return false;
}else{return true;}
},function(err){
return false;
alert('In the def.error and there has been an error ' + err);
});
//return the value of hasErrors here
};
Devdar, you are making heavy wether out of something quite simple. In particular, you don't need to loop through an object to access one of its properties, and the variable hasErrors is not really necessary.
Your code should simplify to something like this :
function PostInformation() {
var $containers = $("#container1, #container2");
var Employee = {
//data
};
return dojo.xhrPost({
url: 'hello',
content: Employee
}).then(function(data) {
data = JSON.parse(data);
var formErrors = data.formErrors;
if(formErrors.errors) {
$containers.each(function(i, c) {
$(c).children().each(function(wid) {
var val = formErrors[wid.id],
myWidget;
if(val) {
myWidget = dijit.byId(wid.id);
myWidget.validator = function() {
this.set("invalidMessage", val);
};
myWidget._hasBeenBlurred = true;
myWidget.validate();
}
});
});
//Send an enhanced error object down the "error" route
throw $.extend(formErrors, {
'message': 'PostInformation(): validation failure'
});
}
//Send the data object down the "success" route
return data;
});
};
PostInformation().then(function(data) {
console.log('PostInformation(): everything went OK');
//access/process `data` here if necessary
//and/or just display a nice "success" message to the user
}, function(err) {
console.error(err.message);
});
Barring mistakes on my part, this code should do everything you want and more. As with your own code, it processes the server's JSON response and returns a Promise, but that's where the similarity stops.
In your code, you seek to return a Promise which is eventually resolved with a boolean to indicate whether or not errors were detected. Whilst this will (if correctly written) meet your immediate needs, it is not the best Promise logic.
In my code, the Promise is resolved only if validation succeeds and rejected if validation fails for whatever reason. Not only is this logically correct behaviour for a Promise (success goes down the success route, and errors go down the error route) but as a bonus should (see note below) also allow you to pass more information to whetever function(s) eventually handle errors. I choose to pass the whole formErrors object enhanced with an error message, thus providing a great deal of freedom in the error handler to display/log/etc as much or as little as is appropriate, and with virtually no assumption inside PostInformation() as to what will happen subsequently. You currently believe that you will only read and act on the boolean formErrors.errors but it could be beneficial to pass as much error data as possible thus allowing yourself the freedom to change your mind at a later date without needing to change anything in PostInformation().
In this regard you can think of PostInformation() as an agent of the server-side service; and like that service, it can be written with incomplete knowledge (or maybe no knowledge at all) of how the (promise of) data/errors it delivers will be used by "consumer code".
NOTE: I have to admit that I'm not 100% familiar with Dojo's Promises, so I'm not sure that a JS plain object can be thrown in the way I indicate. I have found evidence but not proof that it can. For that reason, I am cautious above in saying "your code should simplify to something like this" Anyway, that issue aside, the principle of sending success down the success route and errors down the error route should still apply.
I'd suggest this where you create your own Deferred() object, return it from your PostInformation() function and then register .then() handlers on it so you can pick up the resolve or reject on your own Deferred object that happens inside the PostInformation() function.
The way you had it you were creating your own Deferred() object, but then immediately overwriting it with the xhrPost return result which meant def is now something else and you weren't returning your Deferred from PostInformation() so it can be used outside that function to track the progress.
function PostInformation() {
var hasErrors = false;
var containers = [dijit.byId("container1"), dijit.byId("container2")];
var Employee = {
//data
};
var def = new dojo.Deferred();
dojo.xhrPost({
url: 'hello',
content: Employee,
load: function (data) {
formErrors = {
"errors": true,
"fName": "123",
"surname": "456",
"oNames": "789",
"bSurname": "784585"
};
//formErrors = (JSON.parse(data)).formErrors;
$.each(formErrors, function (key, value) {
if (key == 'errors') {
hasErrors = value;
//console.log('hasErrors set to '+value);
}
});
if (hasErrors == true) {
for (var i = 0; i < containers.length; i++) {
var processingContainer = containers[i];
dojo.forEach(processingContainer.getChildren(), function (wid) {
var widgetName = wid.attr('id');
$.each(formErrors, function (key, value) {
if (key == widgetName && value.length > 0) {
var myWidget = dijit.byId(widgetName);
//var wdgName = dijit.byId(widgetName).attr("id");
var myWidgetValue = value;
myWidget.validator = function () {
//console.log('Attribute Name is :' + wdgName + ' Error Value is : ' + myWidgetValue);
//console.log(wdgName + " : "+myWidgetValue);
this.set("invalidMessage", myWidgetValue);
};
myWidget._hasBeenBlurred = true;
myWidget.validate();
}
});
});
}
}
console.log(hasErrors);
def.resolve(hasErrors);
},
error: function (err) {
console.log(err);
def.reject(err);
}
});
return def.promise;
};
PostInformation().then(function (data) {
console.log('In the then function');
// process data value here which will contain the value you resolved with
}, function(err)
// process an error in the ajax result here
});
I think this is more of an issue with design of the function then.
Since the xHR call is asynchronous, the postInformation shouldn't really return anything unless it's the Deferred object itself. An alternative option is to have postInformation do some sort of event publishing (dojo/topic), that other functions will subscribe to and know how to handle said events.
I have the following function:
getTasks: function()
{
var taskRequest = Titanium.Network.createHTTPClient();
var api_url = 'http://myawesomeapi.heroku.com/users/' + Ti.App.Properties.getString("userID") + '/tasks';
var tasks = [];
taskRequest.onload = function() {
var response = JSON.parse(this.responseText),
len = response.length,
i = 0,
t;
for(; i < len; i++)
{
task = response[i];
var newTask = {};
newTask.rowID = i;
newTask.title = task.title;
newTask.description = task.description;
newTask.id = task.id;
newTask.hasChild = true;
tasks.push(newTask);
}
alert(tasks);
}
taskRequest.open('GET', api_url, false);
taskRequest.setRequestHeader('Content-Type', 'application/json');
taskRequest.send();
alert(tasks);
// return tasks;
}
This function is in my controller; I call it in my view when I need to load the data in. However, I wish to return this data so I can assign it to a variable in the view.
Now what happens is that it returns emptiness. The last alert (bottom one) seems to be running too fast and it returns an empty array, while the one that only gets alerted after the onload function is done, contains what I need.
Now my obvious question, how can I get my function to return the array with the data, instead of without?
Putting a timer on it seems hardly the right decision.. Thanks!
"However, I wish to return this data so I can assign it to a variable in the view."
Aside from making the AJAX request synchronous (which you probably don't want), there isn't any way to return the data.
Whatever code relies on the response needs to be called from within the response handler.
Since functions can be passed around, you could have your getTasks method receive a callback function that is invoked and will receive the tasks Array.
getTasks: function( callback ) // receive a callback function
{
var taskRequest = Titanium.Network.createHTTPClient();
var api_url = 'http://myawesomeapi.heroku.com/users/' + Ti.App.Properties.getString("userID") + '/tasks';
taskRequest.onload = function() {
var tasks = [];
// code populating the tasks array
alert(tasks);
callback( tasks ); // invoke the callback
}
taskRequest.open('GET', api_url, false);
taskRequest.setRequestHeader('Content-Type', 'application/json');
taskRequest.send();
}
So you'd use it like this...
myObj.getTasks(function(tasks) {
alert('in the callback');
alert(tasks);
// Any and all code that relies on the response must be
// placed (or invoked from) inside here
some_other_function();
});
function some_other_function() {
// Some more logic that can't run until the tasks have been received.
// You could pass the tasks to this function if needed.
}
You are getting empty alert because when the bottom alert is executed the server response is not available and tasks array is empty.
When the server response comes the tasks array is populated by the code which you have in the onload handler so you see the tasks in the second alert.