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);
};
}]);
Related
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;
}
}
})
I have a AngularJS controller like below:
schedule.controller('schedule', ['$scope', '$http', function($scope, $http){
$http.get('/zt-api/business/admin/' + window.location.pathname.split('/')[2]).success(function(data){
$scope.admin_times = data;
$scope.admin_times_unix = data;
$scope.weekDays = {'Saturday': "شنبه", 'Sunday': "یکشنبه", 'Monday': "دوشنبه", 'Tuesday': "سه شنبه",
'Wednesday': "چهارشنبه", 'Thursday': "پنجشنبه", 'Friday': "جمعه"};
angular.forEach($scope.admin_times, function (value, key) {
angular.forEach(value, function (value2, key2) {
angular.forEach(value2, function (value3, key3) {
angular.forEach(value3, function (value4, key4) {
angular.forEach(value4, function (value5, key5) {
var info = $scope.admin_times[key]["week_"+ key][key3].times[key5];
if (!isNaN(info)){
var myObj = $.parseJSON('{"date_created":"'+ $scope.admin_times[key]["week_"+ key][key3].times[key5] +'"}'),
myDate = new Date(1000*myObj.date_created);
$scope.admin_times[key]["week_"+ key][key3].times[key5] = myDate.toLocaleString().split(", ")[1]
}
});
});
});
});
});
});
}]);
I change $scope.admin_times, but I don't know why $scope.admin_times_unix also changes!
Can you help me?
Because you are assigning same reference of data to admin_times as well as admin_times_unix object.
That's why changing in one object is, affecting other object.
You could solve this issue by using angular.copy which does create a new reference rather than assigning its actual reference.
$scope.admin_times_unix = angular.copy(data);
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);
});
I have the following service:
app.service('Library', ['$http', function($http) {
this.fonts = [];
this.families = [];
// ... some common CRUD functions here ...
// Returns the font list
this.getFonts = function() {
if(_.isEmpty(this.fonts)) this.updateFonts();
return this.fonts;
};
// Returns the family list
this.getFamilies = function() {
if(_.isEmpty(this.families)) this.updateFamilies();
return this.families;
};
// Update the font list
this.updateFonts = function() {
var self = this;
$http.get(BACKEND_URL+'/fonts').success(function(data) {
self.fonts = data;
console.log('Library:: fonts updated', self.fonts);
});
};
// Update the family
this.updateFamilies = function() {
var self = this;
$http.get(BACKEND_URL+'/families').success(function(data) {
var sorted = _.sortBy(data, function(item) { return item });
self.families = sorted;
console.log('Library:: families updated', self.families);
});
};
}]);
And the following main controller code:
app.controller('MainController', ['$scope', '$state', 'Cart', 'Library', function($scope, $state, Cart, Library) {
console.log('-> MainController');
// Serve the right font list depending on the page
$scope.fonts = $state.is('home.cart') ? Cart.getFonts() : Library.getFonts();
$scope.families = Library.getFamilies();
}]);
The problem is, that when the view requests the content of $scope.fonts, it's still empty.
How to update $scope.fonts and $scope.families when the loading is over?
I could use $scope.$watch but I'm sure there is a cleaner way to do it...
This really is what promises were made for. Your service should return a promise that is to be resolved. You could also simplify your service:
app.service('Library', ['$http', '$q', function($http, $q) {
var self = this;
self.families = [];
// Returns the family list
self.getFamilies = function() {
var deferred = $q.defer();
if(_.isEmpty(self.families)) {
$http.get(BACKEND_URL+'/families').success(function(data) {
var sorted = _.sortBy(data, function(item) { return item });
self.families = sorted;
deferred.resolve(self.families);
console.log('Library:: families updated', self.families);
});
} else {
deferred.resolve(self.families);
}
return deferred.promise;
};
}]);
And then in your controller, use the promises then method:
app.controller('MainController', ['$scope', '$state', 'Cart', 'Library', function($scope, $state, Cart, Library) {
console.log('-> MainController');
// Serve the right font list depending on the page
$scope.fonts = $state.is('home.cart') ? Cart.getFonts() : Library.getFonts();
Library.getFamilies().then(function(result) {
$scope.families = result;
});
}]);
This is untested because of the $http, but here is a demo using $timeout:
JSFiddle
Consider passing a callback function.
Service:
this.getFonts = function(callback) {
if(_.isEmpty(this.fonts)) this.updateFonts(callback);
return this.fonts;
};
this.updateFonts = function(callback) {
var self = this;
$http.get(BACKEND_URL+'/fonts').success(function(data) {
self.fonts = data;
console.log('Library:: fonts updated', self.fonts);
callback(data);
});
};
Controller:
Library.getFonts(function (data) { $scope.fonts = data; });
This could be tidied up a bit, since a callback eliminates the need for some of this code, but it'll serve as an example.
Thanks for all the answers! I ended up using a mix of callback and promise, as follow:
app.service('Library', function($http) {
// Returns the font list
this.getFonts = function(callback) {
if(_.isEmpty(self.fonts)) return self.updateFonts(callback);
else return callback(self.fonts);
};
// Update the font list
this.updateFonts = function(callback) {
return $http.get(BACKEND_URL+'/fonts').success(function(data) {
self.fonts = data;
callback(data);
});
};
});
And, in the controller:
app.controller('MainController', function(Library) {
Library.getFonts(function(fonts) { $scope.fonts = fonts });
});
I tried all your suggestions, but this is the best one working with the rest of my code.
In your this.getFonts function (and your other functions), you call the data from this, which points to the function instead of the controller scope you want. Try the following instead:
var self = this;
self.fonts = [];
self.families = [];
// ... some common CRUD functions here ...
// Returns the font list
self.getFonts = function() {
if(_.isEmpty(self.fonts)) self.updateFonts();
return self.fonts; // <-- self.fonts will point to the fonts you want
};
I would try wrapping your getScope and getFonts bodies that you are calling in a
$scope.$apply(function(){ ...body here... });
Make sure you declare self = this outside any functions.
Assign the call to the value you want to store the data in and then return it.
var self = this;
self.data = [];
this.updateFonts = function() {
self.fonts = $http.get(BACKEND_URL+'/fonts').success(function(data) {
return data.data
});
return self.fonts
};
Since you're using ui-router (i saw a $state). You can use a resolve in your state and return a promise.
Doc : https://github.com/angular-ui/ui-router/wiki
Exemple :
$stateProvider.state('myState', {
resolve:{
// Example using function with returned promise.
// This is the typical use case of resolve.
// You need to inject any services that you are
// using, e.g. $http in this example
promiseObj: function($http){
// $http returns a promise for the url data
return $http({method: 'GET', url: '/someUrl'});
}
},
controller: function($scope,promiseObj){
// You can be sure that promiseObj is ready to use!
$scope.items = promiseObj.data;
}
}
In your case you'll need to turn your this.getFonts and getFamilies into promises
this.getFonts = function(){
return $http.get(BACKEND_URL+'/fonts').success(function(data) {
self.fonts = data;
console.log('Library:: fonts updated', self.fonts);
});
}
There is many many way to do this, but in my opinion the resolve way is the best.
I've just started using AngularJS and I love it.
However - I have a need to save an item to my database using $resource and then get back and object containing the values of the newly created item in the database (especially the database-assigned ID).
I've found a few articles describing this - but none of them seems to work for me :(
I have a very simple setup:
var app = angular.module("todoApp", ['ngResource', 'ngAnimate']);
app.factory("TodoFactory", function ($resource) {
return $resource('.../api/todo/:id', { id: '#id' }, { update: { method: 'PUT' }});
});
var todoController = app.controller("TodoController", function ($scope, TodoFactory) {
$scope.todos = [];
init();
function init() {
$scope.todos = TodoFactory.query();
}
$scope.addTodo = function () {
TodoFactory.save($scope.item, function () {
// Success
console.log($scope.item); // <--- HERE'S MY PROBLEM
$scope.todos.push($scope.item);
$scope.item = {};
},
function () {
// Error
});
};
But when I call TodoFactory.save, the $scope.item does not contain the Id-property from the database - only the values it had upon calling save.
How can I get my setup to return the updated object with all the database-generated values?
If somebody could point me in the right direction it would be much appreciated :)
Update: I just went over the source for the API I've been supplied - the save-method doesn't update the object that's inserted.
After I fixed this "minor" issue, peaceman's example worked like a charm.
Sorry for the inconvenience everybody - but thank you very much for the responses! :)
The save method of the TodoFactory won't update the $scope.item, but instead calls the callback function with the saved object as a parameter, that contains the new id.
So you have to replace
$scope.addTodo = function () {
TodoFactory.save($scope.item, function () {
// Success
console.log($scope.item); // <--- HERE'S MY PROBLEM
$scope.todos.push($scope.item);
$scope.item = {};
},
function () {
// Error
});
};
with
$scope.addTodo = function () {
TodoFactory.save($scope.item, function (savedTodo) {
// Success
console.log(savedTodo);
$scope.todos.push(savedTodo);
$scope.item = {};
},
function () {
// Error
});
};
This behaviour is documented at ngResource.$resource
This should solve the problem.
$scope.addTodo = function () {
// ADD IN A VARIABLE FOR THE RESPONSE DATA AS THE PARAMETER TO YOUR SUCCESS CALLBACK FN
TodoFactory.save($scope.item, function (responseItem) {
// Success
console.dir(responseItem); // <--- AND THEN USE IT
$scope.todos.push(responseItem);
$scope.item = {};
},
function () {
// Error
});
};