--- Given this code in a file ---
angular.module('storyCtrl', ['storyService'])
.controller('StoryController', function(Story, socketio) {
var vm = this;
Story.getStory()
.success(function(data) {
vm.stories = data;
});
vm.createStory = function() {
vm.message = '';
var newMessage = vm.storyData.content;
var newStory = { str: newMessage , timeNow: new Date(), mess: "Hello" };
Story.createStory(newStory)
.success(function(data) {
vm.storyData = '';
vm.message = data.message;
});
};
socketio.on('story', function(data) {
vm.stories.push(data);
})
})
Where does "data" being initialize or where is it coming from as it is not even a global variable or from ['storyService'].
Thank you
The variable data represents what the function (getStory or createStory or the on function) is returning to you for use in the function. For example, the getStory function might be returning a json array. Within the success function, this data is assigned to the vm.stories variable.
Does that help?
The storyCtrl module references another module called storyService. Slightly confusing, the storyService module contains a service (or factory) called Story. The Story service provides a method called getStory. Internally getStory very likely makes a call using $http. You can tell because getStory does not use standard promises, but instead uses the success and error methods that $http provides.
Related
This is my controller:
app.controller("PlaceController", ['$http', function($http){
this.places = shops;
var hotels = this;
hotels.objects = [];
this.spots = new Array;
this.newPlace = {};
this.city = new String();
this.addPlace = function() {
this.places.push(this.newPlace);
this.newPlace = {};
var request = *some query syntax, sorry for hiding*
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
});
for (each in hotels.objects.elements) {
this.spots.push(each.tags.name);
};
console.log(this.spots);
}}] );
I get an empty array when I log this.spots to the console. The http request etc work perfectly because the console.log(hotels.objects.elements) statement works perfectly.
Because of this problem, I can't output it into my HTML either. What should I do?
You are issuing an asynchronous request to get the spots, but you're logging them before they complete.
Change this.addPlace to log / act on the spots array inside the promise callback:
this.addPlace = function() {
this.places.push(this.newPlace);
this.newPlace = {};
var request = *some query syntax, sorry for hiding*
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
for (each in hotels.objects.elements) {
this.spots.push(each.tags.name);
};
console.log(this.spots);
});
You're adding to the spots array before the ajax request is done, move your calls to push inside the callback:
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
angular.forEach(hotels.objects.elements, function(value) {
hotels.spots.push(value.tags.name);
});
});
Also, you should really be using $scope instead of references to this. This would simplify your code a bit, without needing to rename this to hotels
full controller code using $scope
app.controller("PlaceController", ['$scope', '$http', function($scope, $http){
$scope.places = shops;
$scope.objects = [];
$scope.spots = new Array;
$scope.newPlace = {};
$scope.city = new String();
$scope.addPlace = function() {
$scope.places.push($scope.newPlace);
$scope.newPlace = {};
var request = *some query syntax, sorry for hiding*
$http.get(request).success(function(data) {
$scope.objects = data;
console.log($scope.objects.elements);
angular.forEach($scope.objects.elements, function(value, key) {
$scope.spots.push(value.tags.name);
});
// spots is ready, log it, do whatever
console.log($scope.spots);
});
}}] );
NOTE: Using $scope means you won't need to call this from your html to reference the objects and functions defined in your controller.
An example:
<div ng-controller="PlaceController">
<!-- no need to call places.city, if you use $scope just write city -->
{{city}}
</div>
EDIT: You probably shouldn't use JavaScript's for-in, the problem with it is that it iterates on the names or indexes of your objects/arrays.
An example:
var someArray = ['a', 'b', 'c'];
for (i in someArray) {
console.log(i); // prints 0, 1, 2
console.log(someArray[i]); // prints 'a', 'b', 'c'
}
This is different from any for-in/for-each implementation in other popular languages.
Anyway, in this case I've edited the code above to use Angular's forEach, which is a more appropriate solution (many libraries implement custom for-each functions to fix JS's weird for-in)
You can read more in Angular's docs
Another option, in plain javascript is, if $scope.objects.elements is an array, using the map() function, like this:
$scope.spots = $scope.objects.elements.map(function(value) {
return value.tags.name; // value is an item in object.elements
});
try this ..
due to your async call you need to perform task inside success
$http.get(request).success(function(data) {
hotels.objects = data;
console.log(hotels.objects.elements);
for (each in hotels.objects.elements) {
hotels.spots.push(each.tags.name);
};
console.log(this.spots);
});
In one of my factories I need to set a variable when data is fetched (through $http) so I can access it in my controller (the intention is to display a spinner image until the data is loaded).
.factory('LoadData', function LoadData($http, $q){
return {
getMyData: function(){
return $http(
// implementation of the call
).then(
function(response){
var myData = response.data;
// this should be reference to the other method (getLoadStatus)
// where I want to change its value to "true"
// this doesn't work - "this" or "self" don't work either because we're in another function
LoadData.getLoadStatus.status = true;
}
);
},
// by calling the below method from my controller,
// I want to mark the completion of data fetching
getLoadStatus = function(){
var status = null;
return status;
}
}
}
I hope you got the idea - how could this be accomplished? Also, I'm open to any suggestions which are aimed towards a better approach (I want to pick up best practice whenever possible).
Status is essentially a private variable; use it as:
.factory('LoadData', function LoadData($http, $q){
var status = null; // ESSENTIALLY PRIVATE TO THE SERVICE
return {
getMyData: function(){
return $http(...).then(function(response){
...
status = true;
});
},
getLoadStatus = function(){
return status;
}
};
})
There are several ways.
Here's one which I prefer to use:
.factory('LoadData', function LoadData($http, $q){
var status = false;
var service = {
getMyData: getMyData,
status: status
};
return service;
function getMyData() {
return $http(
// implementation of the call
).then(
function(response){
var myData = response.data;
status = true;
}
);
}
}
This provides good encapsulation of your methods and gives you a clean interface to export. No need for the getter method if you don't want it.
Inspiration via John Papa's Angular style guide (found here).
You could simply store variable flag in closure:
.factory('LoadData', function LoadData($http, $q) {
var status = false;
return {
getMyData: function() {
status = false;
return $http(/* implementation of the call */).then(function(response) {
status = true;
return response.data;
});
},
getLoadStatus: function() {
return status;
}
}
});
Also if getMyData loads fresh data every time, it's important to reset status to false before each request.
I actually decide to use a promise in my controller after calling this service and when data is returned, I am simply making the status true. It seems that is best practice to have a promise returned when calling a service so I should be good.
I am picking up a project started by a peer, and having trouble with an API call.
This is my angular controller:
angular.module('oowli').factory('Note', ['$http', function($http) {
var Note = function(data) {
angular.extend(this, data);
};
Note.prototype.save = function() {
var note = this;
return $http.post('/notes', this).then(function(response) {
note = response.data;
}, function(error) {
return error;
});
};
return Note;
}]);
and the call is executed in this function:
var saveNote = function(Note, scope){
return function(span, phrase){
var noteObj = new Note({
user: scope.global.user._id,
content: span.innerText
});
noteObj.save().then(function(){
console.log(noteObj);
});
};
};
problem is, after saving the note, noteObj is the original, not the one that comes back from the server (with an _id field).
Debugging on the Note.prototype.save, response.data comes with the _id;
I need to know how to have access to the returned Note object, in the saveNote function.
You are assigning the new object to the local variable 'note' in the Note.prototype.save function, but then not doing anything with it.
The quickest solution is to copy the properties of the returned object to your note rather than assign to it, so instead of:
note = response.data;
This might work:
angular.copy(response.data, note)
In general though I don't like the approach of having the Note class responsible for saving itself. I would create a note service that saves the object.
I'm attempting to use Angularjs to gather data from the USGS Earthquake feed. Typically you would need to tack ?callback=JSON_CALLBACK on to the end of the URL for Angular to use it, however the USGS feed does not recognize this option.
The URL I'm using is http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp and adding ?callback=JSON_CALLBACK (eg. http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp?callback=JSON_CALLBACK) returns a dataset wrapped in a function called eqfeed_callback.
Is there any way to use this data? I've got an eqfeed_callback function but it's not in scope which makes using Angular pointless.
Here's the code that I've got as it stands:
function QuakeCtrl($scope, $http) {
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
$http.jsonp(url)
}
}
function eqfeed_callback(data) {
return data;
}
Is there any way to either get the data back into the scope, or get angular to use the eqfeed_callback function internally?
Another option would be defining the eqfeed_callback within the scope like this:
function QuakeCtrl($scope, $http) {
$scope.data = null;
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
$http.jsonp(url)
}
window.eqfeed_callback = function(data) {
$scope.data = data
}
}
The only idea that comes to mind is (blech) to use a global, and then to manually trigger an Angular update, e.g.:
var callback$scope;
function QuakeCtrl($scope, $http) {
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
callback$scope = $scope;
$http.jsonp(url)
}
}
function eqfeed_callback(data) {
if (callback$scope) {
// 1. Use callback$scope here
// 2. Set callback$scope to undefined or null
// 3. Trigger an Angular update (since it won't be automatic)
}
}
Not pretty, but...
Expanding on #MichaelVanRijn's answer:
In order to keep the "global peace", define the global function when you need it and nullify it right after.
.controller('QuakeCtrl', function($window, $scope, $http) {
$scope.get_quakes = function() {
$window.eqfeed_callback = function(data){
console.log("quake data", data)
};
// load your jsonp data
$http.jsonp('http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp')
.then(function(success) {
console.log(success);
$window.eqfeed_callback = null;
}, function(fail) {
console.log(fail);
$window.eqfeed_callback = null;
})
}
})
I am new to Angular. I have a simple web page, but the codes for controller are not working. It seems I do not call or inject service "ListLogsFactory" in the controller properly. Please help. Thank you.
My codes include a module, service, and controller that all declared/defined as follows:
var myApp = angular.module("ListLogsModule", []);
myApp.factory('ListLogsFactory', function ($http) {
var thisPageNumber = 1;
var thisPageSize = 10;
var baseUrl = '../Api/LogApi/GetLogsByPage';
var items = {};
$http({
method: 'GET',
url: baseUrl,
data: $.param({ pageNumber: thisPageNumber, pageSize: thisPageSize })
})
.success(function (data, status, headers, config) {
items = data;
console.log(items);
})
.error(function (data, status, headers, config) {
alert('error: ' + status);
});
function getData() {
return items;
}
});
// The error is seen in FireFox and happens in the controller code:
myApp.controllers.ListLogsController = function ($scope, ListLogsFactory) {
$scope.logs = ListLogsFactory.getData(); // NOTE: this line throws error on running with something like "ListLogsFactory" is undefined
}
When you use factory you have to return something. You're just defining a bunch of methods there but they aren't available to anybody.
It's also good to use a different naming convention. For example, instead of LogsController, use LogsCtrl. AngularJS appends "Controller" internally and you might end up, in exotic situations, handling names like "LogsControllerController".
A simplified approach of using factory and returning the service:
var ListLogsModule = angular.module("myApp", []);
ListLogsModule.factory('ListLogsSrv', function ($http) {
// first define the service (you're using a factory)
var getData = function() {
return "hey";//items;
};
// then return it.
// offer a public method "getData" that uses your internal getData()
return {
getData : getData
}
});
ListLogsModule.controller("ListLogsCtrl", function ($scope, ListLogsSrv) {
$scope.w = "world";
$scope.logs = ListLogsSrv.getData();
});
You also have an $http request in the factory. That means that you'll trigger the async request when you instantiate the service (when it is used for the first time), so nobody will wait for it to finish and you'll be getting undefined.
If you are using this service in a controller, you will probably need to resolve a promise.
An example of using a promise:
var promise = $q.defer();
var thisPageNumber = 1;
...
var baseUrl = '../Api/LogApi/GetLogsByPage';
...
promise = $http.get(...
Now you can use this promise in the controller, for example, or in the methods of your service.
I answered a related question two days ago Angular Service Definition: service or factory
myApp.controllers.ListLogsController = function ($scope, ListLogsFactory) {
$scope.logs = ListLogsFactory.getData();
}
should be
myApp.controller("ListLogsController", function ($scope, ListLogsFactory) {
$scope.logs = ListLogsFactory.getData();
});
The following is extended the suggestions from #Eduard Gamonal to make variables/methods of angular service or factory, so it is considered a trick to remember syntax of angular service or factory.
Trick to memorize syntax of "service" or "factory" in Angular
Service is tied with "service" key word; "this" key word to make function instance members public; and "=" to make assignments to "function instance members".
Factory is tied with "factory" key word; "return" keyword to make/return a public object; and ":" to all key/value pairs assignments.
Details.
Service deals with "variables" (or "instance members"), and to make them "public", I use the "this" keyword, and because a service deals with "variables" (or "instance members") to-be-public we use "=" after a "variable" name.
"getLogs" can be treated like a "public variable" or "instance member", and written (in "assignment" meaning) like this.getLogs = function() {...}.
And the whole service is defined with the "service" key word:
<script type="text/javascript">
var myApp = angular.module("ListLogsModule", []);
myApp.service('ListLogsService', function () {
this.getLogs = function () {
var logs = [
{"LogId":5405,"RecordedDate" : "2012-11-19T14:22:02.247", "Event" : "Log On"},
{"LogId":5416,"RecordedDate" : "2012-11-19T14:55:02.247", "Event" : "Log Out"}
];
return logs;
}
});
myApp.controller('ListLogsCtrl', function ($scope, ListLogsService) {
$scope.logs = ListLogsService.getLogs();
});
</script>
Factory deals with a returned "object" and to make them "public", I use the "return" keyword, and because a factory deals with "object" to-look-like-JSON-object I use ":" after each "property" name inside { } of the "return" statement.
"getLogs" can be treated like a property (or key) of the returned JSON object, and is written (in "key/value" pair) like getLogs : function() {...}.
And the whole factory is defined with the "factory" key word:
<script type="text/javascript">
var myApp = angular.module("ListLogsModule", []);
myApp.factory('ListLogsFactory', function () {
return {
getLogs: function () {
return[
{"LogId":5405,"RecordedDate" : "2012-11-19T14:22:02.247", "Event" : "Log On"},
{"LogId":5416,"RecordedDate" : "2012-11-19T14:55:02.247", "Event" : "Log Out"}
];
}
}
});
myApp.controller('ListLogsCtrl', function ($scope, ListLogsFactory) {
$scope.logs = ListLogsFactory.getLogs();
});
</script>
In summary: To memorize syntax of "service" or "factory" in Angular
Service is tied with "service" key word; "this" key word to make function instance members public; and "=" to make assignments to "function instance members".
Factory is tied with "factory" key word; "return" keyword to make/return a public object; and ":" to all key/value pairs assignments.