I'm doing the CA course for angular. Here is the code for the controller, in controller.js:
app.controller('MainController', ['$scope', 'forecast', function($scope, forecast) {
forecast.success(function(data) {
$scope.fiveDay = data;
});
}]);
Here is the code for the service, in service.js:
app.factory('forecast', ['$http', function($http) {
return $http.get('http://s3.amazonaws.com/codecademy-content/courses/ltp4/forecast-api/forecast.json')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}]);
So I guess factories are services? What exactly is a service? I saw this explanation but it isn't the most clear to me.
Services
Syntax: module.service( 'serviceName', function ); Result: When
declaring serviceName as an injectable argument you will be provided
with an instance of the function. In other words new
FunctionYouPassedToService().
In the two code snippets, when is the service called? What is the forecast.service doing in the controller? What is forecast = in the controller? Is it an object?
An Angular factory is a function which when called will return the service. The service can be pretty much anything. In your case, you are returning the result of $http(...).success(..).error(...) in your return statement which is a promise.
So when you instantiate the controller, the factory gets called, calls inside the function body the HTTP call and returns a promise.
In the controller you receive a promise object which you can call success or error on.
This way, you cannot repeat the HTTP call, since once factories are called, all other controller receive the same instance (in this case the HTTP call is made only when the first controller is instantiated with the forecast dependency, all other controllers with that dependency get the same promise for the already called HTTP request)
try this
$http.get('//s3.amazonaws.com/codecademy-content/courses/ltp4/forecast-api/forecast.json') instead of $http.get('http://s3.amazonaws.com/codecademy-content/courses/ltp4/forecast-api/forecast.json')
helped to me.
Related
I'm facing a problem with angular JS Controller function. I have a controller and in that controller I have bunch of functions which are getting called from a service which contains HTTP get and post requests.
To be specific my question is inside a controller, when I try to write:
$scope.functionName = function()
{
//some code
}
My function doesn't get called at all. And when I do the following my function gets called but the scope goes undefined for rest of the functions.
app.controller("ControllerName", function ($scope, ServiceName) {
getAll();
getAppKey();
getAppDefaults();
function getAll(){//some code}
function getAppKey(){//some code}
function AppDefaults(){//some code
})
I'm not sure where I'm going wrong. I would really appreciate anyone's help who can help me out with this issue.
I think you are defining function with $scope and calling function without $scope.
This code will work:
app.controller("ControllerName", function ($scope, ServiceName) {
$scope.getAll = function{
console.log("function called");
}
$scope.getAll();
})
And this code will not work as expected:
app.controller("ControllerName", function ($scope, ServiceName) {
$scope.getAll = function{
console.log("function called");
}
getAll();
})
'...bunch of functions which are getting called from a service...' ?
You can't call function from controller in service. What you can do is to use functions from service in controller.
If you want to use methods from service inside your controller, first inject service(you already did that),
then call it:
ServiceName.getAll();
ServiceName.getAppKey();
...
etc.
If you want to use this functions from controller, make another service/factory put this functions inside that service, inject service2 in service and again you can use them like:
ServiceName2.getAll();
ServiceName2.getAppKey();
I am not sure what are you trying to acomplish, maybie this?
this code works:
app.controller('MainCtrl', function($scope) {
$scope.submit = function() {
alert("called.........");
}
defaultFunction = function() {
alert("defaultFunction called.........");
}
defaultFunction();
});
$scope is added when you want to use in view, if you need to use it in controller, no need for $scope
you can change http://jsfiddle.net/B9RsQ/45/ to test it
I'm having trouble on figuring out how to pass parameters from my angular controller to
service
#my controller
'use strict';
angular.module('recipeapp')
.controller('recipeCtrl', ['$scope', 'recipeService',
function($scope, recipeService){
$scope.recipeFormData={};
$scope.recipeSave = function(){
recipeService.saveRecipe();
}
}]);
#my service
'use strict';
angular.module('recipeapp').service('recipeService',['$http', function($http){
this.saveRecipe = save;
function save(callback){
//calling external http api
}
}]);
What I'm trying to do here is , getting the $scope.formData from my form and controller should pass that to service, As per my understanding, I cannot use $scope inside the service so I need to find a way of passing $scope.formData to the service
tough Idea would be, in the controller, recipeService.saveRecipe($scope.formData); but I'm not sure how to collect that from the service,
when I changed the service this.saveRecipe(val) = save; it doesnt work :(
any help would be appriciated
This example demonstrates the proper structure of an angular app:
Model initialization inside your controller
Implementation of a service singleton, and injection into your controller
Use of $http promises to invoke web API calls asynchronously and allowing callers of your service to handle their success/failure.
Use of "controller as" syntax way of exposing functions from your controller rather than exposing functions directly from scope.
Two-way data model binding (textbox-to-recipe and recipe-to-textbox)
Initialize your model within your controller:
angular.module('recipeapp')
.controller('recipeCtrl', ['$scope', 'recipeService',
function($scope, recipeService){
// initialize your model in you controller
$scope.recipe={};
// declare a controller function that delegates to your service to save the recipe
this.saveRecipe = function(recipe) {
// call the service, handle success/failure from within your controller
recipeService.saveRecipe(recipe).success(function() {
alert('saved successfully!!!');
}).error(function(){
alert('something went wrong!!!');
});
}
}]);
In your recipe service, define the saveRecipe function:
angular.module('recipeapp').service('recipeService',['$http', function($http){
// expose a saveRecipe function from your service
// that takes a recipe object
this.saveRecipe = function(recipe){
// return a Promise object so that the caller can handle success/failure
return $http({ method: 'POST', url: '/api/recipe/add', data: recipe});
}
}]);
Bind your recipe object to your view; add a button to invoke the saveRecipe controller function and save the recipe (passing in the model recipe object):
<div ng-app="recipeapp" ng-controller="recipeCtrl as ctrl">
<form name="recipeForm">
Recipe Name: <input type="text" ng-model="recipe.name" />
<button ng-click="ctrl.saveRecipe(recipe)">Save Recipe</button>
</form>
</div>
var module = angular.module('example.service', []);
module.services('ExampleServices', ['$http', '$q', function ($http,
$q) {
var resourceUrl;
return {
setResourceUrl: function(resourceUrl) {
this.resourceUrl = resourceUrl;
},
create: function(params) {
//access params here sent from controller
//make call to server using $http
//return back the promise or response
},
remove: function(id) {
//access id here sent from controller
//make call to server using $http
//return back the promise or response
}
}
Later in your controller inject the service ExampleServices
And then access:
ExampleServices.create(params)
params could be any object, most probably data captured using forms.
ExampleServices.remove(id)
id could be primary id of the record to be removed from database.
Hope that helps :)
I have a factory called "Server" which contains my methods for interaction with the server (get/put/post/delete..). I managed to login and get all data successfully when I had all my code in my controller. Now that I want to separate this code and restructure it a little bit I ran into problems. I can still login and I also get data - but data is just printed; I'm not sure how to access the data in controller? I saw some ".then" instead of ".success" used here and there across the web, but I don't know how exactly.
This is my factory: (included in services.js)
app.factory('Server', ['$http', function($http) {
return {
// this works as it should, login works correctly
login: function(email,pass) {
return $http.get('mywebapiurl/server.php?email='+email+'&password='+pass').success(function(data) {
console.log("\nLOGIN RESPONSE: "+JSON.stringify(data));
if(data.Status !== "OK")
// login fail
console.log("Login FAIL...");
else
// success
console.log("Login OK...");
});
},
// intentional blank data parameter below (server configured this way for testing purposes)
getAllData: function() {
return $http.get('mywebapiurl/server.php?data=').success(function(data) {
console.log("\nDATA FROM SERVER: \n"+data); // here correct data in JSON string format are printed
});
},
};
}]);
This is my controller:
app.controller("MainController", ['$scope', 'Server', function($scope, Server){
Server.login(); // this logins correctly
$scope.data = Server.getAllData(); // here I want to get data returned by the server, now I get http object with all the methods etc etc.
…. continues …
How do I get data that was retrieved with $http within a factory to be accessible in controller? I only have one controller.
Thanks for any help, I'm sure there must be an easy way of doing this. Or am I perhaps taking a wrong way working this out?
EDIT: I also need to be able to call factory functions from views with ng-click for instance. Now I can do this like this:
// this is a method in controller
$scope.updateContacts = function(){
$http.get('mywebapiURL/server.php?mycontacts=').success(function(data) {
$scope.contacts = data;
});
};
and make a call in a view with ng-click="updateContacts()". See how $scope.contacts gets new data in the above function. How am I supposed to do this with .then method?(assigning returned data to variable)
My question asked straight-forwardly:
Lets say I need parts of controller code separated from it (so it doesn't get all messy), like some functions that are available throughout all $scope. What is the best way to accomplish this in AngularJS? Maybe it's not services as I thought …
The trick is to use a promise in your service to proxy the results.
The $http service returns a promise that you can resolve using then with a list or success and error to handle those conditions respectively.
This block of code shows handling the result of the call:
var deferred = $q.defer();
$http.get(productsEndpoint).success(function(result) {
deferred.resolve(result);
}).error(function(result) { deferred.reject(result); });
return deferred.promise;
The code uses the Angular $q service to create a promise. When the $http call is resolved then the promise is used to return information to your controller. The controller handles it like this:
app.controller("myController", ["$scope", "myService", function($scope, myService) {
$scope.data = { status: "Not Loaded." };
myService.getData().then(function(data) { $scope.data = data; });
}]);
(Another function can be passed to then if you want to explicitly handle the rejection).
That closes the loop: a service that uses a promise to return the data, and a controller that calls the service and chains the promise for the result. I have a full fiddle online here: http://jsfiddle.net/HhFwL/
You can change the end point, right now it just points to a generic OData end point to fetch some products data.
More on $http: http://docs.angularjs.org/api/ng.%24http
More on $q: http://docs.angularjs.org/api/ng.%24q
$http.get retuns a HttpPromise Object
Server.getAllData().then(function(results){
$scope.data = results;
})
I have a controller like this:
function MyCtrl($scope) {
$scope.doSomething = function(){
alert("Do something!");
}
}
And I have multiple views which depend on this (ie multiple of the below):
<div ng-controller="MyCtrl">
...
</div>
The problem is, the data the controller depends on needs to be loaded in the background (the controller does not load that data), and a callback (dataIsReady()) will be called after the data is ready.
function dataIsReady(){
// TODO: call the doSomething() function
}
Now, I want to basically call the doSomething() function, which is inside MyCtrl, from the dataIsReady() function. How can I do that?
I think what you need is a data service, which you can then inject into your controller. You can call a function on your data service which will handle the retrieval of the data and return a "promise" which can then be used to trigger your callback function when the data has loaded.
Have a look at the following code which is a slightly modified version from egghead.io:
Plunker Demo (w/ local storage): http://plnkr.co/edit/9w2jTg?p=preview
var myApp = angular.module('myApp', []);
myApp.factory('AvengersService', function ($http) {
var AvengersService = {
getAsyncCast: function () {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get("avengers.json") // or some JSON service
.then(function (response) {
// The 'then' function here is an opportunity to modify the response
// The return value gets picked up by the 'then' in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return AvengersService;
});
myApp.controller('AvengersCtrl', function($scope, AvengersService) {
// Call the async method and then do something when the data is retrieved
AvengersService.getAsyncCast()
.then(function (asyncData) {
// Callback logic that depends on the data goes in here
console.info("Cast pulled async.");
$scope.avengers.cast = asyncData;
});
});
Hope that helps.
Notice: This approach in this answer is terribly wrong, one should not access to the scope of a controller outside of angular, or outside of controller at all. This would also be terribly slow if you try to call it several times. Other than that, it is fine. I am giving this answer because it is also the simplest way. I would never use that kind of code in production, though. The appropriate way is to write a service to communicate with the controller.
Given that you have defined $scope.doSomething in MyCtrl:
var scp = angular.element('[ng-controller="MyCtrl"]').scope();
scp.doSomething();
Will call doSomething method defined in the controller.
I'm still wrapping my brain around Angular.JS.
I have two independent $http calls that retrieve data from remote web services. I have an action that I want to fire off after both service calls have been completed.
The first service call will populate the $scope.model
The second service call, modifies data in the $scope.model (it adds some counter properties that are rendered in the view)
Another unique requirement is that eventually the second service call will be called and updated outside the controller with the $scope.model. It's a notification message pump.
I'm guessing I'm going to use promises $q and possibly $service, but I'm not really sure where to start for something like this following some best practices.
I know it doesn't sound like async calls are appropriate here, since my example it could be simplified by doing it syncronously. However, the second service call is a notification updater, so it'll get continually polled to the server (eventually a websocket will be used).
It's a common pattern I'll see in this application.
You'll want to use $q promises. Specifically $q.all(). All $http methods will return promises. $q.all([promise, promise, promise]).then(doSomething) will wait for all promises to resolve then call doSomething passing an array of the promises results to it.
app.service('myService', ['$http', '$q', function($http, $q) {
return {
waitForBoth: function() {
return $q.all([
$http.get('/One/Thing'),
$http.get('/Other/Thing')
]);
};
}
}]);
Then call it:
app.controller('MyCtrl', ['$scope', 'myService', function($scope, myService) {
myService.waitForBoth().then(function (returnValues){
var from1 = returnValues[0].data;
var from2 = returnValues[1].data;
//do something here.
});
}]);
Here's a demonstration Plunker for you.
Create a service for your first service call. Pete's answer will help with that: https://stackoverflow.com/a/12513509/215945
In your controller, in the then() callback, add a $watch for the appropriate $scope.model property:
app.controller('MainCtrl', function(myService, $scope) {
myService.async().then(function(myData) {
$scope.model = myData;
$scope.$watch('model.???', function(newVal, oldVal) {
if(newVal !== oldVal) {
// do something here, now that $scope.model.??? has changed
}
})
});
});