AngularJS : passing params from controller to service - angularjs

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 :)

Related

how to pass object data from one controller to another controller in angularjs

Actually I am trying to fill a form in angularjs and its data is recieved in one of the controller...i need to pass the same data to another controller which is needed to be shown on the another web page
You should make use of angular service to do the same.
Create a service, that will hold model variables.
angular.service("dataService", function() {
this.value1 = "";
});
Then you can reference that service in your controllers,
angular.controller("myCntrl1", function($scope, dataService) {
$scope.value1 = dataService.value1;
});
angular.controller("myCntrl2", function($scope, dataService) {
$scope.value1 = dataService.value1;
});

$injector:unpr when trying to add service [duplicate]

I have a Service:
angular.module('cfd')
.service('StudentService', [ '$http',
function ($http) {
// get some data via the $http
var path = 'data/people/students.json';
var students = $http.get(path).then(function (resp) {
return resp.data;
});
//save method create a new student if not already exists
//else update the existing object
this.save = function (student) {
if (student.id == null) {
//if this is new student, add it in students array
$scope.students.push(student);
} else {
//for existing student, find this student using id
//and update it.
for (i in students) {
if (students[i].id == student.id) {
students[i] = student;
}
}
}
};
But when I call save(), I don't have access to the $scope, and get ReferenceError: $scope is not defined. So the logical step (for me), is to provide save() with the $scope, and thus I must also provide/inject it to the service. So if I do that like so:
.service('StudentService', [ '$http', '$scope',
function ($http, $scope) {
I get the following error:
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <-
StudentService
The link in the error (wow that is neat!) lets me know it is injector related, and might have to do with order of declaration of the js files. I have tried reordering them in the index.html, but I think it is something more simple, such as the way I am injecting them.
Using Angular-UI and Angular-UI-Router
The $scope that you see being injected into controllers is not some service (like the rest of the injectable stuff), but is a Scope object. Many scope objects can be created (usually prototypically inheriting from a parent scope). The root of all scopes is the $rootScope and you can create a new child-scope using the $new() method of any scope (including the $rootScope).
The purpose of a Scope is to "glue together" the presentation and the business logic of your app. It does not make much sense to pass a $scope into a service.
Services are singleton objects used (among other things) to share data (e.g. among several controllers) and generally encapsulate reusable pieces of code (since they can be injected and offer their "services" in any part of your app that needs them: controllers, directives, filters, other services etc).
I am sure, various approaches would work for you. One is this:
Since the StudentService is in charge of dealing with student data, you can have the StudentService keep an array of students and let it "share" it with whoever might be interested (e.g. your $scope). This makes even more sense, if there are other views/controllers/filters/services that need to have access to that info (if there aren't any right now, don't be surprised if they start popping up soon).
Every time a new student is added (using the service's save() method), the service's own array of students will be updated and every other object sharing that array will get automatically updated as well.
Based on the approach described above, your code could look like this:
angular.
module('cfd', []).
factory('StudentService', ['$http', '$q', function ($http, $q) {
var path = 'data/people/students.json';
var students = [];
// In the real app, instead of just updating the students array
// (which will be probably already done from the controller)
// this method should send the student data to the server and
// wait for a response.
// This method returns a promise to emulate what would happen
// when actually communicating with the server.
var save = function (student) {
if (student.id === null) {
students.push(student);
} else {
for (var i = 0; i < students.length; i++) {
if (students[i].id === student.id) {
students[i] = student;
break;
}
}
}
return $q.resolve(student);
};
// Populate the students array with students from the server.
$http.get(path).then(function (response) {
response.data.forEach(function (student) {
students.push(student);
});
});
return {
students: students,
save: save
};
}]).
controller('someCtrl', ['$scope', 'StudentService',
function ($scope, StudentService) {
$scope.students = StudentService.students;
$scope.saveStudent = function (student) {
// Do some $scope-specific stuff...
// Do the actual saving using the StudentService.
// Once the operation is completed, the $scope's `students`
// array will be automatically updated, since it references
// the StudentService's `students` array.
StudentService.save(student).then(function () {
// Do some more $scope-specific stuff,
// e.g. show a notification.
}, function (err) {
// Handle the error.
});
};
}
]);
One thing you should be careful about when using this approach is to never re-assign the service's array, because then any other components (e.g. scopes) will be still referencing the original array and your app will break.
E.g. to clear the array in StudentService:
/* DON'T DO THAT */
var clear = function () { students = []; }
/* DO THIS INSTEAD */
var clear = function () { students.splice(0, students.length); }
See, also, this short demo.
LITTLE UPDATE:
A few words to avoid the confusion that may arise while talking about using a service, but not creating it with the service() function.
Quoting the docs on $provide:
An Angular service is a singleton object created by a service factory. These service factories are functions which, in turn, are created by a service provider. The service providers are constructor functions. When instantiated they must contain a property called $get, which holds the service factory function.
[...]
...the $provide service has additional helper methods to register services without specifying a provider:
provider(provider) - registers a service provider with the $injector
constant(obj) - registers a value/object that can be accessed by providers and services.
value(obj) - registers a value/object that can only be accessed by services, not providers.
factory(fn) - registers a service factory function, fn, that will be wrapped in a service provider object, whose $get property will contain the given factory function.
service(class) - registers a constructor function, class that will be wrapped in a service provider object, whose $get property will instantiate a new object using the given constructor function.
Basically, what it says is that every Angular service is registered using $provide.provider(), but there are "shortcut" methods for simpler services (two of which are service() and factory()).
It all "boils down" to a service, so it doesn't make much difference which method you use (as long as the requirements for your service can be covered by that method).
BTW, provider vs service vs factory is one of the most confusing concepts for Angular new-comers, but fortunately there are plenty of resources (here on SO) to make things easier. (Just search around.)
(I hope that clears it up - let me know if it doesn't.)
Instead of trying to modify the $scope within the service, you can implement a $watch within your controller to watch a property on your service for changes and then update a property on the $scope. Here is an example you might try in a controller:
angular.module('cfd')
.controller('MyController', ['$scope', 'StudentService', function ($scope, StudentService) {
$scope.students = null;
(function () {
$scope.$watch(function () {
return StudentService.students;
}, function (newVal, oldVal) {
if ( newValue !== oldValue ) {
$scope.students = newVal;
}
});
}());
}]);
One thing to note is that within your service, in order for the students property to be visible, it needs to be on the Service object or this like so:
this.students = $http.get(path).then(function (resp) {
return resp.data;
});
Well (a long one) ... if you insist to have $scope access inside a service, you can:
Create a getter/setter service
ngapp.factory('Scopes', function (){
var mem = {};
return {
store: function (key, value) { mem[key] = value; },
get: function (key) { return mem[key]; }
};
});
Inject it and store the controller scope in it
ngapp.controller('myCtrl', ['$scope', 'Scopes', function($scope, Scopes) {
Scopes.store('myCtrl', $scope);
}]);
Now, get the scope inside another service
ngapp.factory('getRoute', ['Scopes', '$http', function(Scopes, $http){
// there you are
var $scope = Scopes.get('myCtrl');
}]);
Services are singletons, and it is not logical for a scope to be injected in service (which is case indeed, you cannot inject scope in service). You can pass scope as a parameter, but that is also a bad design choice, because you would have scope being edited in multiple places, making it hard for debugging. Code for dealing with scope variables should go in controller, and service calls go to the service.
You could make your service completely unaware of the scope, but in your controller allow the scope to be updated asynchronously.
The problem you're having is because you're unaware that http calls are made asynchronously, which means you don't get a value immediately as you might. For instance,
var students = $http.get(path).then(function (resp) {
return resp.data;
}); // then() returns a promise object, not resp.data
There's a simple way to get around this and it's to supply a callback function.
.service('StudentService', [ '$http',
function ($http) {
// get some data via the $http
var path = '/students';
//save method create a new student if not already exists
//else update the existing object
this.save = function (student, doneCallback) {
$http.post(
path,
{
params: {
student: student
}
}
)
.then(function (resp) {
doneCallback(resp.data); // when the async http call is done, execute the callback
});
}
.controller('StudentSaveController', ['$scope', 'StudentService', function ($scope, StudentService) {
$scope.saveUser = function (user) {
StudentService.save(user, function (data) {
$scope.message = data; // I'm assuming data is a string error returned from your REST API
})
}
}]);
The form:
<div class="form-message">{{message}}</div>
<div ng-controller="StudentSaveController">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<input type="button" ng-click="reset()" value="Reset" />
<input type="submit" ng-click="saveUser(user)" value="Save" />
</form>
</div>
This removed some of your business logic for brevity and I haven't actually tested the code, but something like this would work. The main concept is passing a callback from the controller to the service which gets called later in the future. If you're familiar with NodeJS this is the same concept.
Got into the same predicament. I ended up with the following. So here I am not injecting the scope object into the factory, but setting the $scope in the controller itself using the concept of promise returned by $http service.
(function () {
getDataFactory = function ($http)
{
return {
callWebApi: function (reqData)
{
var dataTemp = {
Page: 1, Take: 10,
PropName: 'Id', SortOrder: 'Asc'
};
return $http({
method: 'GET',
url: '/api/PatientCategoryApi/PatCat',
params: dataTemp, // Parameters to pass to external service
headers: { 'Content-Type': 'application/Json' }
})
}
}
}
patientCategoryController = function ($scope, getDataFactory) {
alert('Hare');
var promise = getDataFactory.callWebApi('someDataToPass');
promise.then(
function successCallback(response) {
alert(JSON.stringify(response.data));
// Set this response data to scope to use it in UI
$scope.gridOptions.data = response.data.Collection;
}, function errorCallback(response) {
alert('Some problem while fetching data!!');
});
}
patientCategoryController.$inject = ['$scope', 'getDataFactory'];
getDataFactory.$inject = ['$http'];
angular.module('demoApp', []);
angular.module('demoApp').controller('patientCategoryController', patientCategoryController);
angular.module('demoApp').factory('getDataFactory', getDataFactory);
}());
Code for dealing with scope variables should go in controller, and service calls go to the service.
You can inject $rootScope for the purpose of using $rootScope.$broadcast and $rootScope.$on.
Otherwise avoid injecting $rootScope. See
Common Pitfalls: $rootScope exists, but it can be used for evil.

Return service $http.get response to controller without using $scope

I've been searching around for hours, but I couldn't find a solution. I'm trying to retrieve data from a separate json-file in my AngularJS application. Using the $http I do get the data I want.
However, I want to get specific data from that file at in multiple places. In the service I want to define some functions that controllers can call to retrieve the data (that the service got using $http).
Now the problem is that when I return the all the data to the controller directly, or try to use it later in the service, the assigned variables are undefined.
I try to use controller-as syntax, so I do not want to use $scope. However every solution I've found suggests using $scope. This code now logs
f {$$state: {...}}.
Code of the service:
theApp.service('SettingsService', function($http) {
this.dataVar = $http.get('../settings.json')
.then(function(response) {
return response;
});
});
Code of the controller:
theApp.controller('SomeController', ['SettingsService', function(SettingsService) {
console.log(SettingsService.dataVar);
}]);
UPDATE:
https://jsfiddle.net/md954y0a/
what about calling your service at start-up module then passing to submodules through a service that loads the same instance
html:
<div ng-app='myApp' ng-controller="myctrl">
{{parent}}
<div ng-app='myApp1' ng-controller="myctrl1">
{{myApp1data}}
</div>
</div>
js:
angular.module('myApp', ['myApp1']).controller('myctrl', ['$scope', 'API', function($scope, API) {
$scope.parent = API.getData();
}]).service('API', function($q) {
var object = null;
this.getData = function() {
object = {
obj1: "DATA1",
obj2: "DATA2"
};
return object;
}
});
angular.module('myApp1', []).controller('myctrl1', ['$scope', 'API', function($scope, API) {
$scope.myApp1data = API.getData().obj1
}]);

Angular service call. When does it happen?

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.

AngularJS: make data received with $http inside factory to be accessible inside controller

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;
})

Resources