Passing an array from service to controller - javascript

I cannot seem to figure out how to pass an array from service to a controller.
I have a simple service
.service('test', function() {
var array = []
return array;
})
And a controller where I call this function when a button is pressed
$scope.testArray = function() {
$scope.test = test.array;
console.log("test: ", $scope.test);
};
I get an error test is undefined. Can anyone explain to me please why this doesn't work and how to fix it? I tried storing that array in a separate object but no luck either. THanks

(See also: this SO question about Angular providers)
A service should put properties directly on this. So instead of
.service('test', function() {
var array = [];
return array;
})
try
.service('test', function() {
this.array = [];
})
(code style notwithstanding; many would suggest preferring function access over direct object access)
.service('test', function() {
var array = [];
this.getArray = function(){
return array;
};
})

Just change test.arraywith test:
JSFiddle
.controller('youCtrl', ['$scope', 'test', function ($scope, test) {
$scope.testArray = function() {
$scope.test = test;
console.log("test: ", $scope.test);
};
});

Add the array variable to your service.
angular.module('moduleName').service('test', function() {
this.array = [];
});
Inject your service into your controller.
angular.module('moduleName').controller('controllerName', function(test) {
$scope.test = test.array;
console.log("test: ", $scope.test);
});

Related

How to access Array value from outside scope with Angular JS

Okay so I know this is a really stupid question. I looked everywhere before making this thread and I am just completely unable to figure it out. This is the code I had. My issue is that between passing the value from getJSON to the angular controller, my array has lost its values. What is the correct way to do what I'm trying to do here?
function getJSON(json) {
data = [];
$.each(json, function (index, value) {
data.push(value);
});
console.log(data); // Accurately logs array data
return data;
}
function passValue() {
return getJSON();
}
app.controller('ExampleController', function($scope) {
x = passValue()
$scope.title = x[0]; // Throws error
console.log(x); // Only returns an empty array
}
And this I have in a script on my html file. It works 100%.
$(document).ready(function() {
$.getJSON("{{ url_for('static', filename='movies.json') }}?id={{ movie_id }}", function(json) {
getJSON(json);
});
For example, this works.
function getJSON(json) {
data = [];
$.each(json, function (index, value) {
data.push(value);
});
console.log(data) // Acurrately logs array data
document.getElementById('exampleDiv').innerHTML = data[0] // Accurately appends array data (0 being title)
}
I found a solution that fixes my problem. If anyone has a similar issue, I hope this helps you as well.
function getJSON(json) {
data = [];
$.each(json, function (index, value) {
data.push(value);
});
console.log(data)
update(data)
}
function update(data) {
var $scope = angular.element(document.querySelector('#movie')).scope()
$scope.$apply(function(){
$scope.title = data[0];
});
}
app.controller('MovieController', function($scope) {
});
You can use the window to store and access data between angular and any component. Angular has a wrapper called $window to access it too.
In javascript:
function foo(value){
window.data = value;
}
In angular:
app.controller('ctrl', ['$scope', '$window', function($scope, $window) {
$scope.getData= function() {
//using $window
alert($window.data);
//Or
alert(window.data);
};
}]);

Can't save http result to scope

I have a json-file defined and I am trying to load in one of my controllers. I am using a factory to fetch the data:
.factory('myService', function($http) {
var all_data = [];
return {
getAllData: function(){
return $http.get('js/data/all_data.json').then(function(data) {
all_data = data;
return all_data ;
});
}
}
})
Later in my controller I call getAllData() in a loadData()-function:
.controller('QuizCtrl',['$scope','$state','$http','myService',function($scope,$state,$http,myService){
// $scope.myData = []; <-- this makes the app freeze and not respond anymore
$scope.loadData = function(){
myService.getAllData().then(function(all_data){
$scope.myData = all_data.data.all_data;
alert($scope.myData);
});
}
$scope.loadData();
$scope.another_var = $scope.myData;
}])
As you can see first of all I am also calling loadData(). While debugging inside the function (see alert()) I can clearly see how the json has been loaded and applied to the $scope.myData variable.
Once I try to assign the variable to another variable (see $scope.another_var) myData is 'undefined'.
What I tried was defining $scope.myData before the $scope.loadData() call (see comment in code). Unfortunately, this simple variable declaration makes my app freeze completely. I have not found the reason for this yet. Also, I am not sure if it is related to my overall problem.
So what have I missed? Why am I not able to store my "http get" result in my controller's $scope?
EDIT: So in my case, I need the data to be there before the current Controller is even used. Would it be a legit option to put all the code which is executed within the controller into the .then-chain of the promise?
It's because your HTTP request is an asyncronous function while the assignment $scope.another_var = $scope.myData; is syncronous.
Basically what's going on is that when your QuizCtrl controller is loaded, it finishes the statement $scope.another_var = $scope.myData; before it finishes the http request of getAllData(). What you've got is a race condition.
If you want to change the value of another_var move it within your async callback:
$scope.loadData = function(){
myService.getAllData().then(function(all_data){
$scope.myData = all_data.data.all_data;
alert($scope.myData);
// because now $scope.myData is available this assignment will work:
$scope.another_var = $scope.myData;
});
}
$scope.loadData();
Hope this helps.
If you need to udpate a different value based on the value that is already on scope, you could observe the value for changes and update accordingly.
here is what you could do.
var app = angular.module("sampleApp", []);
app.controller("sampleController", ["$scope", "sampleService",
function($scope, sampleService) {
sampleService.sampleMethod(1).then(function(value) {
$scope.value = value;
$scope.$digest();
}, function(error) {});
$scope.$watch(function() {
return $scope.value;
}, function(newValue, oldValue) {
//Default Value when the dependant value is not available
newValue = newValue || 0;
$scope.myNewValue = newValue * 10;
});
}
]);
app.service("sampleService", function() {
this.sampleMethod = function(value) {
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
value = value * 2;
resolve(value);
}, 1000);
});
return promise;
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<div ng-app="sampleApp">
<div ng-controller="sampleController">
<div>Value: {{value}}</div>
<div>Cloned Value : {{myNewValue}}
</div>
</div>
</div>
You are missing a promiss $q
take this method for instance:
.factory('myService', function($http,$q) {
var all_data = [];
return {
getAllData: function () {
var d = $q.defer();
$http.get('js/data/all_data.json')
.success(function (response) {
d.resolve(response);
});
return d.promise;
}
}
})

Callback from Angularjs factory

I want to get a callback from a factory. If I understand correctly, the callback should be in the deepest nested function (i.e. under var myResult = $filter('filter')(myData, {id:exnum})[0];, but I get "TypeError: callback is not a function".
My factory calls another factory, gets a value and injects it into a third one for the final result. This final result logs correctly to console, but I cannot callback to the controller.
Any feedback would be appreciated.
angular.module('resourceFetch', [])
.factory('ResourceFetch', ['JsonService', 'UserProgress', '$filter', function(JsonService, UserProgress, $filter) {
var resourceResult = {};
resourceResult.getResource = function(callback){
UserProgress.getProgress(function(exnum, callback) {
JsonService.get(function(data){
var myData = [];
var myData = data.exercises;
var myResult = [];
var myResult = $filter('filter')(myData, {id:exnum})[0];
console.log(myResult) // <- this displays correctly
callback(myResult); // <- "TypeError: callback is not a function"
});
});
//callback(myResult); <- here "myResult is not defined"
};
return resourceResult;
}]);
This is the controller:
myApp.controller('ResourceFetchTest', function($scope, ResourceFetch) {
$scope.myresults = ResourceFetch.getResource(function(obj1){
console.log('obj1 is ' + obj1);
$scope.MyData = obj1;
$scope.MySelectedData = obj1.string1;
});
});
You could use a promise to return the object
Something like:
angular.module('resourceFetch', [])
.factory('ResourceFetch', ['JsonService', 'UserProgress', '$filter','$q', function(JsonService, UserProgress, $filter,$q) {
var resourceResult = {};
resourceResult.getResource = function(){
var defer = $q.defer();
UserProgress.getProgress(function(exnum) {
JsonService.get(function(data){
var myData = [];
var myData = data.exercises;
var myResult = [];
var myResult = $filter('filter')(myData, {id:exnum})[0];
console.log(myResult) // <- this displays correctly
defer.resolve(myResult);
});
});
return defer.promise;
};
return resourceResult;
}]);
and in the controller:
myApp.controller('ResourceFetchTest', function($scope, ResourceFetch) {
$scope.myresults = ResourceFetch.getResource().then(function(obj1){
console.log('obj1 is ' + obj1);
$scope.MyData = obj1;
$scope.MySelectedData = obj1.string1;
});
});
here's the documentation on promises:https://docs.angularjs.org/api/ng/service/$q
Let me know if you have questions I had the simular problem today and fixed it this way
Sorry, my previous answer was not looking at your code properly, your biggest issue here is that you are trying to pass services within services and it makes your code hard to follow.
What you should do is inject all of your services to your controller module and then you can do something along the lines of this.
myApp.controller('ResourceFetchTest', function($scope, ResourceFetch) {
$scope.dataForProgress = "Bind whatever data in needed to get your progress";
$scope.dataForJson = UserProgress.getProgress(dataForProgress);
$scope.myResults = JsonService.get(dataForJson);
});
Depending on what each service does and what it calls it is possible you are also making Async calls in which case I would recommend looking into the $q directive angular provides.

How to fix the unit test issue in my case?

I am trying to unit test two functions codes and keep getting error of undefined object.
my controller
vm = this;
//always fire first in the app
vm.getCompany = function() {
api.getCompany(function(res){
//do stuff
})
}
//always fire second in the app
vm.getEmployee = function() {
api.getEmployee(function(res){
//do stuff
})
}
api service
var company;
function getCompany() {
var company;
var q = $q.defer();
var url = ‘something.com’;
anotherApi.getCompany(url).then(function(comp){
company = comp;
q.resolve(company)
})
}
function getEmployee = function() {
var name = company.name
var url = ‘something.com/’ + name;
var q = $q.defer();
anotherApi.getEmployee(url).then(function(employee){
q.resolve(employee)
})
}
unit test.
beforeEach(function(){
module(‘myApp);
inject(function ($injector) {
$controller = $injector.get('$controller');
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
$httpBackend = $injector.get('$httpBackend');
api = $injector.get('api');
});
vm = $controller'myCtrl', {
$scope : $scope
});
})
describe (‘test’, function(){
it(‘should get company’, function(){
vm.getCompany();
$httpBackend.flush();
// stuff and works
})
it(‘should get Employee’, function(){
vm.getEmployee()
$httpBackend.flush();
//getting error says
//'undefined' is not an object (evaluating 'company.name’)
})
})
I am getting 'undefined' is not an object (evaluating 'company.name’)
under getEmployee function in service.
I have tried many different ways but still not sure how to fix it, can someone help me about it? Thanks!
What is the expected behavior of the service if getEmployee is called before getCompany is called? You should at least check for company being null before attempting to use it. Also, you may want to consider storing the company in a property that you can access in your service. NOTE: I'm prefixing the property name with an underscore just to make a distinction between the public api and this pseudo-private property:
{
_company: null,
getCompany: function() {
var self = this;
var url = '...';
return $http.get(url).then(function(comp){
self._company = comp;
return self._company;
});
},
getEmployee: function() {
var self = this;
if (!self._company) {
return null; //or throw error or return rejected promise: $q.reject('company is null')
} else {
var url = '...';
var name = self._company.name;
return http.get(url);
}
}
}
Lastly, you can (and should) test your service separately from your controller now. In your controller test, you can just spyOn your service methods without it calling through to the server. And when you test your service, you can just set the service._company to a mock value when testing the getEmployee method.
Issue is in your Service. "company" should be the object literal since you access .name over it else it will through an error which you have specified.
Try below code:
Service
var company = {};
function getCompany() {
$http.get(url).then(function(comp){
company = comp;
return company;
})
}
function getEmployee = function() {
var name = company.name
$http.get(url).then(function(employee){
// do stuff
}
}
It should work.

angular - I have an element in the view bound to a variable in a service, but the view is not getting updated when the service is updated

I have a variable appList = [1,2,3] that is stored in a service. I import appList into the controller and bind it to the view in a list. I have a function modifyAppList in the controller that calls a function of the same name in the service. This function is called with a button click in the view, and doing so definitely modifies the value in the service, as you can see with the console logs. I am very confused, because the view never updates even though the value in the service changes. If someone could take a look at the code and offer an explanation that would be much appreciated.
NOTE If you just change the contents of modifyAppList() to appList[0] = 5; appList[1]= 6; appList[2]=7; it will work as I expect. Something funny is happening when I assign appList to a new array inside of modifyAppList()
angular.module('app', []);
angular.module('app')
.factory('appService', appService)
function appService(){
var appList = [1,2,3];
var modifyList = [5,6,7];
var modifyAppList = function(){
console.log("At the beginning of modifyAppList: ", appList)
appList = [];
console.log("After appList=[] : ", appList)
for(var i = 0; i < 3; i++){
appList.push(modifyList[i]);
}
console.log("After modification: ", appList)
}
return {
getAppList : function(){
return appList;
},
modifyAppList : modifyAppList
}
}
angular
.module('app')
.controller('appCtrl', appCtrl)
appCtrl.$inject = ['appService'];
function appCtrl(appService){
this.appList = appService.getAppList();
this.modifyAppList = function(){
appService.modifyAppList();
var testList = appService.getAppList();
console.log("After modification, and calling getList() : ", testList)
}
}
<body ng-controller="appCtrl as vm">
<ul>
<li ng-repeat="num in vm.appList" ng-bind="num"></li>
</ul><br>
<button ng-click="vm.modifyAppList()">modifyAppList()</button>
<script src="controller.js"></script>
<script src="service.js"></script>
</body>
When the code in the service did appList = [];, it replaced the object reference with a new object reference. The object reference in the controller still points to the old array. (appList = []; literally means appList = new Array();)
To preserve the old reference, use angular.copy:
function appService(){
var appList = [1,2,3];
var modifyList = [5,6,7];
var modifyAppList = function(){
angular.copy(modifyList, appList);
}
return {
getAppList : function(){
return appList;
},
modifyAppList : modifyAppList
}
}
From the Docs:
angular.copy
Usage
angular.copy(source, [destination]);
Creates a deep copy of source, which should be an object or an array.
If a destination is provided, all of its elements (for arrays) or properties (for objects) are deleted and then all elements/properties from the source are copied to it.
--AngularJS angular.copy API Reference
Instead of replacing the array with a new array, angular.copy modifies the of the contents of the array. Changes to the array by the service will be seen by the controller and vice versa.
Hi once the service gets updated u haveent assign the value please check the link
JS
app.controller('MainCtrl', function($scope , appService) {
$scope.name = 'World';
$scope.appList = appService.getAppList();
$scope.modifyAppList = function(){
console.log("After modification: ")
appService.modifyAppList();
var testList = appService.getAppList();
$scope.appList = appService.getAppList();
console.log("After modification, and calling getList() : ", testList)
}
});
for ref https://plnkr.co/edit/XRAEwtiV9vdv0beiS5yU?p=preview
Why not keep things simple. Just ensure your modifyAppList function in service returns appList.
var modifyAppList = function() {
console.log("At the beginning of modifyAppList: ", appList)
appList = [];
console.log("After appList=[] : ", appList)
for (var i = 0; i < 3; i++) {
appList.push(modifyList[i]);
}
console.log("After modification: ", appList)
return appList;
}
Then in your Controller :
this.modifyAppList = function(){
this.appList = appService.modifyAppList();
}
appService.getAppList();
is not enough.
You have to apply it to the controller's scope:
this.appList = appService.getAppList();
As the variable appList inside your service has nothing to do with the one in the controller.
--update--
Well, the actual cause is the line in the service:
appList = [];
You indeed apply the appList to this.appList in the first line in the controller, but when you call the method modifyAppList another time, you apply a new empty array to it. Now the appList in the service is indeed has nothing to do with the old one.
So if you remove this line:
appList = [];
and change the follow line:
appList.push(modifyList[i]);
to something like this:
appList[i] = modifyList[i];
You will see it works well too. As you do not create a new reference this way.

Categories