AngularJS : injecting data from factory to controller - javascript

I trying to get some data and then pass it to the controller for further processing. I have setup a factory to get the data but having issues passing it to the controller and getting it working. Below is the case I'm working with.
var app = angular.module("contactApp",[]);
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
$http.get("/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID).success(onSuccess).error(onFailure);
}
};
});
app.controller('ContactController', function(listData,$scope){
//setTimeout(function(){console.log(Data)},2000);
console.log("Controller called. Hello");
listData.getData(successFunction,failFunction,curItemId);
successFunction = function(data){
$scope.resData = data;
console.log("Success - ", data);
}
failFunction - function(data){
console.log("Didn't work - ", data);
}
});
This gives me below error.
successFunction is not defined
Not sure what I'm doing wrong, any input will be greatly appreciated!
EDIT:
Moving the functions down works really well but the async call is failing. I switched to using $ajax and it works just fine but $http doesn't work for some reason!
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
//$http.get("/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20156").success(onSuccess).error(onFailure);
$.ajax({
url:"/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID,
headers: {"accept": "application/json; odata=verbose"},
success: onSuccess,
error: onFailure
});
}
};
});
Just fyi the data is coming from a SharePoint list but that shouldn't matter. I'll keep digging and please do let me know if I'm making any syntax error that I can't locate.
I really appreciate the help guys!
EDIT 2:
Ok this issue was unrelated. I found the problem, SharePoint uses odata so I had to pass a header:
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
$http(
{
method: "GET",
headers: {"accept": "application/json; odata=verbose"},
url: "/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID
}
).success(onSuccess).error(onFailure);
}
};
});
You guys ROCK!!!!!!!

As Daniel A. White said, declare your functions before you call them.
var app = angular.module("contactApp",[]);
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
$http.get("/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID).success(onSuccess).error(onFailure);
}
};
});
app.controller('ContactController', function(listData,$scope){
//setTimeout(function(){console.log(Data)},2000);
console.log("Controller called. Hello");
var successFunction = function(data){
$scope.resData = data;
console.log("Success - ", data);
}
var failFunction - function(data){
console.log("Didn't work - ", data);
}
//Now your functions are actually defined.
listData.getData(successFunction,failFunction,curItemId);
});
You don't have to use var to declare functions, because JavaScript will still understand but using var makes it a local variable, while not using var will define it as global.
I'm sure this will help.

Related

Share data between controllers AngularJs with Services

Background
I am making Service which has a list to be shared between two controllers. To start I followed this tutorial on Services:
https://thinkster.io/a-better-way-to-learn-angularjs/services
And I managed to successfully create and execute the basic tutorial on Plunker:
https://plnkr.co/edit/niUlaHP54wWpjSoWURNX
Problem
The problem here, is that when I click the form button, I need to make an HTTP GET request to my server, and update the service list when I get the response.
To achieve this, I first tried using the following Plunker modification:
https://plnkr.co/edit/Z7K9CJbNP9LClycRHwBd?p=info
The jest of the code can be seen in the service:
// Create the factory that share the Fact
app.factory('ListService', function($http) {
var list = {};
list.data = [];
list.request = function(theHairColor) {
var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
console.log(theUrl);
$http({
method: 'GET',
url: theUrl,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(function successCallback(response) {
list.data = response.data.entries; //does not work
console.log(response.data.entries);
}, function errorCallback(response) {
console.log('Error: ' + response);
});
};
return list;
});
If you tried it out, you will see it simply doesn't work, and I don't really understand why. In a previous question I made, someone explained to me that it was related to references, and that I should replace list.data = response.data.entries; //does not work for the following:
//this works but is rather flimsy ....
list.data.length = 0;
Object.assign(list.data, response.data.entries);
Which in deed does work, but I find it rather counter intuitive:
https://plnkr.co/edit/ABdxPI4coNYlJ85EIOJl
Another suggestion was also given, in that I should change my gnomeList controller to :
app.controller("gnomeList", function(ListService) {
var self = this;
self.listService = ListService;
});
and then iterate over the service's list directly:
<div ng-controller="gnomeList as listCtrl">
<p ng-repeat="gnome in listCtrl.listService.data">{{ gnome.id }}: {{ gnome.name }}</p>
</div>
Which also works, but attaches the controller directly to the service:
https://plnkr.co/edit/h48CmupRjRFoxFT5ZSd8
Questions:
Are there any other ways to make my first code sample (that didn't work) work?
Which of these solutions would be preferable and why? (which one is more Angulary?)
Problem is you initially copy the data in your gnomeList and it is passed by value.
app.controller("gnomeList", function(ListService) {
var self = this;
self.list = ListService.data;
});
When your controller gets initialized here, it puts a copy of ListService.data into self.list. However, when updating the values in the services, this controller does not get initialized again and therefore the value is not updated.
Objects in javascript are passed by reference. Just like you said, you could directly put the service on scope to use its data or you simply set the properties on an object before you set them on your scope. (Plunkr)
Javascript
app.controller("gnomeList", function(ListService) {
var self = this;
self.list = ListService.value; // value is an object
});
// Create the factory that share the Fact
app.factory('ListService', function($http) {
var list = {};
list.value = {};
list.request = function(theHairColor) {
var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
console.log(theUrl);
$http({
method: 'GET',
url: theUrl,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(function successCallback(response) {
list.value.data = response.data.entries; // extend value object
}, function errorCallback(response) {
console.log('Error: ' + response);
});
};
return list;
});
HTML
<div ng-controller="gnomeList as listCtrl">
<p ng-repeat="gnome in listCtrl.list.data">{{ gnome.id }}: {{ gnome.name }}</p>
</div>
Moreover it is better to use built in angular.extend for extending objects.
So this doesn't work?
list.request = function(theHairColor) {
var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
console.log(theUrl);
$http({
method: 'GET',
url: theUrl,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(function success(data) {
list.data = data;
console.log(data);
return data;
});
};
That would be a structure we've used in the past (we use Restangular now), but this link is a good page to see about $http that would help you.

AngularJS scope.apply causes error

I have this function which fetches data from the DB and updates scope's values.
$scope.loadFromDB = function(){
console.log("loadFromDB function");
$http({
url: '/getData',
method: "GET",
})
.then(function(response) {
// success
var data = JSON.parse(response.data)
data = JSON.parse(data.data)
$scope.gridDataDayData = data.dayData;
$scope.gridDataKlasoveNames = data.klasoveNames;
$scope.gridDataNumOfRows = data.numOfRows;
$scope.$apply();
},
function(response) { // optional
// failed
console.log("get unsuccessful");
});
};
When I run this though I get
error: [$rootScope:inprog] http://errors.angularjs.org/1.4.3/$rootScope/inprog?p0=%24digest
I can not figure out how to fix this. Please help me.
Thank you
You don't need trigger the digest cycle because you are inside the angular life cycle... it is already running.
A manual triggering is needed when you are outside from the angular way: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
Simply remove
var data = JSON.parse(response.data)
data = JSON.parse(data.data) and $scope.$apply()
and
$scope.$apply();

Knockout : Async call not finished before ko.applyBindings(). Best, and easiest practice ?

i KNOW this question probably has been asked here before, but i just cannot seem to get any "best practice" which.. me as a newbie, can relate to in a easy understandable way...
Knockout Code
$(document).ready(function() {
function viewModel() {
var self = this;
self.contacts = ko.observableArray();
$.ajax({
type: "POST",
url: "ClubWebServices.asmx/GetContacts",
data: "{'abc':'" + 123 + "'}", // if ur method take parameters
contentType: "application/json; charset=utf-8",
success: function (data) {
var contactData = data.d;
console.log("Nr 1: " + contactData);
self.contacts(contactData);
console.table(self.contacts());
},
dataType: "json",
failure: function(error) {
console.log(error);
}
});
console.table("Nr 2: " + self.contacts());
ko.applyBindings(new viewModel());
}
Result is that the console.log Nr 2 is called before Console.log nr 1..
So.. how do i solve this without creating a hierarky structure where i have to implement DataService layer's which uses OnSuccess and onFailure events ?
Here are some thoughts.
You're trying to create an instance of viewModel from within the function's declaration, this could lead to issues with the ko bindings.
Another note, is your references to self in the success callback may not be what you think it is. Using the debugger would give you clarity on that.
I would try rearranging some things like this:
$(document).ready(function() {
var viewModel = function (contactData) {
var self = this;
self.contacts = ko.observableArray(contactData);
}
$.ajax({
type: "POST",
url: "ClubWebServices.asmx/GetContacts",
data: "{'abc':'" + 123 + "'}", // if ur method take parameters
contentType: "application/json; charset=utf-8",
success: function (data) {
var contactData = data.d;
console.log("Nr 1: " + contactData);
ko.applyBindings(new viewModel(contactData));
},
dataType: "json",
failure: function(error) {
console.log(error);
}
});
}
Changes: I close your viewModel declaration before the ajax request, and I'm passing in the contactData to the new viewModel constructor.
Also, creating a jsfiddle would greatly help with finding the cause of your error.
I don't think your code has any problem.
In ViewModel constructor,you bind the model to the DOM
And after the service callback,you add data to the observableArray,then ko will update the binding DOM.
If you want your "Nr 2" table to be called after self.contacts is populated, subscribe:
var initialized = self.contacts.subscribe(function (newValue) {
console.table("Nr 2: " + self.contacts()); // could use newValue instead
initialized.dispose();
});
The dispose call keeps it from happening again on future updates. Or maybe you want it to, I don't know.
Oh, and as Julian Soro notes, don't call applyBindings inside the viewmodel creation function.

Angular async http autocomplete

I want to create an autocomplete with Angular and since this is my first contact with angular, I`m pretty stucked.
Here is my code:
MenuService.getAutocompleteData(term).then(function (response) {
$scope.menuData = response.data;
});
This is how I call a service that is making the following http call:
return $http({
url : autocompletePath,
method : "POST",
data : {
term: term
}
}).success(function (response) {
return response;
});
The problem is that it seems that is syncronously and my browser freezes when I type fast letters. I saw that it is about that ".then" promise, but I`m not sure how to fix it. Any help would be appreciated.
You do a HTTP-request, which is correct, but you try to return the result on success. Instead you should try to do what needs to be done in your success-handler.
So in your service you would do something like this:
return $http({
url : autocompletePath,
method : "POST",
data : {
term: term
}
}); //note that there is no success handler in your service, because what's supposed to happen when the request completes, is the controller's business, not the service's. The service should only know where to get the data
And you would change your controller to this:
MenuService.getAutocompleteData(term).success(function (response) {
$scope.menuData = response.data;
}); //note the success-handler
Why dont you give this a try?
https://angular-ui.github.io/bootstrap/#/typeahead

jQuery AJAX issue? Or JS OOP scope issue?

I am trying to create a database handler class in javascript. I would like to call the class by simply using:
var databaseHandler = new DatabaseHandler();
result = databaseHandler.getResult("SELECT * FROM login");
I have created the class and used a callback for the ajax function (so as to wait for the ajax result to be returned). But all I am still receiving "undefined" as my result. If I use console.log(a) inside of the onComplete function, I get an array of the intended results.
(function(window){
//Database class
function DatabaseHandler(){
//Query
this.query = function(query, whenDone){
request = $.ajax({
url: "../optiMizeDashboards/php/DatabaseQuery.php",
type: "POST",
data: {query : query},
dataType: "JSON"
});
request.done(function(output) {
whenDone(output);
});
request.fail(function(jqXHR, textStatus) {
console.log(textStatus);
});
};
//Get result
this.getResult = function(query){
this.query(query, this.onComplete);
};
//Ajax callback
this.onComplete = function(a){
return a;
};
}
//Make available to global scope
window.DatabaseHandler = DatabaseHandler;
}(window))
My question is: Is this something to do with the variable scope, or the way that ajax works? I have read all the answers explaining that ajax is ASYNC and I thought I had handled that by using a callback function "onComplete"
Any help on this topic would be greatly appreciated!
You will not be able to return result immediately from calling getResult because underlying jQuery POST request is Asynchronous, instead you need to be passing a callback function which eventually will receive a result from server.
something like that:
(function(window){
//Database class
function DatabaseHandler(){
//Query
this.query = function(query, whenDone){
request = $.ajax({
url: "../optiMizeDashboards/php/DatabaseQuery.php",
type: "POST",
data: {query : query},
dataType: "JSON"
});
request.done(function(output) {
whenDone(output);
});
request.fail(function(jqXHR, textStatus) {
console.log(textStatus);
});
};
//Get result
this.getResult = function(query, callback){
this.query(query, callback);
};
}
//Make available to global scope
window.DatabaseHandler = DatabaseHandler;
}(window))
// then use it like so
var databaseHandler = new DatabaseHandler();
result = databaseHandler.getResult("SELECT * FROM login", function(data) {
//do something with data
});
PS: exposing direct SQL access to the databse on the client is very dangerous though, and I would not recommend doing that

Categories