I am new to angularjs and so i was going through the basic examples i found online initially just to understand the working and concepts used. When i encountered the concept of "factory service creation" (which is a way to expose data from server to views in and angularjs), i found it difficult to understand the flow between the service's function arguments and the calls made to it .
`<html ng-app="countryApp">
<head>
<meta charset="utf-8">
<title>Angular.js Example</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular-route.min.js"></script>
<script>
var countryApp = angular.module('countryApp', ['ngRoute']);
countryApp.config(function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'country-list.html',
controller: 'CountryListCtrl'
}).
when('/:countryName', {
templateUrl: 'country-detail.html',
controller: 'CountryDetailCtrl'
}).
otherwise({
redirectTo: '/'
});
});
countryApp.factory('countries', function($http){
return {
list: function(callback){
$http.get('countries.json').success(callback);
},
find: function(name, callback){
$http.get('countries.json').success(function(data) {
var country = data.filter(function(entry){
return entry.name === name;
})[0];
callback(country);
});
}
};
});
countryApp.controller('CountryListCtrl', function ($scope, countries){
countries.list(function(countries) {
$scope.countries = countries;
});
});
countryApp.controller('CountryDetailCtrl', function ($scope, $routeParams, countries){
countries.find($routeParams.countryName, function(country) {
$scope.country = country;
});
});
</script>
</head>
<body>
<div ng-view></div>
</body>
</html>`
so in the code i ve posted, can anyone let me know or explain the flow between the "factory's list and find methods( keeping the callback argument particularly in mind)?
i am not able to understand why the same factory method is called again by itself( callback argument)
please help me out..
Regarding the list function
When the CountryListCtrl controller is instantiated, the countries service (which is an object) is being passed as an argument.
Then the countries.list function (defined within countries service obviously) is being called and passed a callback function.
The countries.list function makes a GET request, and if successful (i.e. the $http promise is resolved with success), the anonymous callback function, that was passed in when the function was called in the CountryListController controller is called and the $http service passes the returned data as an argument - Which the anonymous function then assigns to the $scope.countries property.
The countries.find function is the same basic pattern, with the difference that $routeParams is picking up the /:countryName from the route, and passing it into the countries.find function as an argument for the purpose (it seems) of picking out a specific country from the response data returned by the server and then assigning it to the $scope.country property.
the portion of code i am commenting on is
countryApp.factory('countries', function($http){
return {
list: function(callback){
$http.get('countries.json').success(callback);
},
find: function(name, callback){
$http.get('countries.json').success(function(data) {
var country = data.filter(function(entry){
return entry.name === name;
})[0];
callback(country);
});
}
};
});
Here the factory is returning an object with two functions namely list and find.
both the function has a parameter called callback. callback is basically the function that you want to call when the service is executed successfully. As both list and find are going to make Asynchronous calls to server, you would want to be notified when the call is done.
Angular however has a neater way of doing this, called promise. and if we implement promise api the code becomes
countryApp.factory('countries', function($http, $q){
return {
list: function(){
var defered = $q.defer();
$http.get('countries.json').success(function(result){
defered.resolve(result);
})
.error(function(error){
defered.reject(error)
})
return defer.promise
},
find: function(name){
var defered = $q.defer();
$http.get('countries.json').success(function(data) {
var country = data.filter(function(entry){
return entry.name === name;
})[0];
defered.resolve(country);
})
.error(function(error){
defered.reject(error)
})
return defer.promise;
}
};
});
Angulars promise api is very well documented here
https://docs.angularjs.org/api/ng/service/$q
in short what it says is promise object is a contract that when an asynchronous work is completed then it would either be resolved() (completed successfully) or rejected (completed unsuccessfully) and promise objects then function would be called.
then(success(), error())
your controller would become.
countryApp.controller('CountryListCtrl', function ($scope, countries){
countries.list().then(function(countries) {
$scope.countries = countries;
});
}, function(error){
console.log("unable to fetch the list of countries : " + error)
});
countryApp.controller('CountryDetailCtrl', function ($scope, $routeParams, countries){
countries.find($routeParams.countryName).then(function(country) {
$scope.country = country;
}, function(error){
console.log("unable to find the country: " + error)
}));
Hope it helped you.
first of all we are defining modules for any application in angularJS.
then we are defining configuration for the module where inside [] we are keeping all required dependency.we can define our own angular directive which will connect java controller to get value in respective format like json etc.Then while defining angular controller we can invoke our defined directive in our angular controller to make availability for data, and from angular controller we can get value to angular view which will display in html or any view page.
Related
I am building an app to track movies and their info, I am new to Angular, and I cant not really sure how to pass a variable to this service.
I want the url to be a variable instead of hardcoded. whats the best way to do it?
tmdb.service('tmdbService', function($http, $q){
var deferred = $q.defer();
$http.get('https://api.themoviedb.org/3/movie/popular?api_key=jkhkjhkjhkjh').then(function(data){
deferred.resolve(data);
});
this.getMovies = function(){
return deferred.promise;
}
});
tmdb.controller("tmdbController", function($scope, tmdbService){
var promise = tmdbService.getMovies();
promise.then(function(data){
$scope.movies = data;
// console.log($scope.movies);
})
});
There is no need (in this case) to use $q.defer() because $http already returns a promise. So, your service code can be simplified to:
tmdb.service('tmdbService', function($http){
this.getMovies = function(){
return $http.get('https://api.themoviedb.org/3/movie/popular?api_key=jkhkjhkjhkjh');
}
});
Then, if you want to send a parameter you can do this:
tmdb.service('tmdbService', function($http){
this.getMovies = function(movieId){
return $http.get('https://api.themoviedb.org/' + movieId + '/movie/popular?api_key=jkhkjhkjhkjh');
}
});
In your controller, you can now send in the movieId:
tmdb.controller("tmdbController", function($scope, tmdbService){
tmdbService.getMovies(3).then(function(response){
$scope.movies = response.data;
// console.log($scope.movies);
})
});
I usually do this in the following way which I feel is neat and more readable:
tmdb.service('tmdbService', [function($http) {
return { //simple mapping of functions which are declared later
fn1: fn1,
fn2: fn3
}
function f1(param) { //param can be the url in your case
//fn code example
return $http.post(param).success(function(response) {
return response.data;
})
}
function f2(param) {
}
}]
And in your controller, using the service:
tmdb.controller('tmdbController', ['$scope', 'tmdbService', function($scope, tmdbService) {
tmdbService.f1(url).then(function(data) {
//handle the data here
})
}])
There are several ways you can go about achieving this goal. In my opinion there is really no right/wrong way; what is right is absolutely dependent on your need and this may change as you application grows.
Especially for large applications, you can define a module to manage urls and inject this module into your index application.
Another way is to define a service to manage your urls. In this case you also have to inject this service into any other services/controllers etc where you may need it.
The disadvantage to this is that this service is only available to the angular module its defined within or at most must be accessed via that module.
So using the service style here is how it may be implemented.
tmdb.service('urlService', function () {
this.urls = {
url1: 'https://api.themoviedb.org/3/movie/popular?api_key=jkhkjhkjhkjh',
url2: 'anotherUrl'
};
});
tmdb.service('tmdbService', ['$http', '$q', 'urlService', function ($http, $q, urlService) {
var deferred = $q.defer();
$http.get(urlService.url.url1).then(function (data) {
deferred.resolve(data);
});
this.getMovies = function () {
return deferred.promise;
}
}]);
Again there is no absolute right/wrong way; it depends.
I hope you find this helpful.
Cheers!
Below is the recommended way to get data into a controller from a factory using $http -- according to https://github.com/johnpapa/angularjs-styleguide
What i don't get is how the two success callbacks on $http work (i commented what i think the two callbacks are).
1) What is the point of the first callback?
2) Where does vm.avengers point to? Is it a reference to another object?
3) is 'data' in the second callback = 'response.data.results' from the first?
4) I'm counting 3 total callbacks chained, is that correct?
P.S. i already know about promises, but want to learn this pattern specifically
The factory
/* recommended */
// dataservice factory
angular
.module('app.core')
.factory('dataservice', dataservice);
dataservice.$inject = ['$http', 'logger'];
function dataservice($http, logger) {
return {
getAvengers: getAvengers
};
function getAvengers() {
return $http.get('/api/maa')
.then(getAvengersComplete)
.catch(getAvengersFailed);
//Callback One
function getAvengersComplete(response) {
return response.data.results;
}
function getAvengersFailed(error) {
logger.error('XHR Failed for getAvengers.' + error.data);
}
}
}
The Controller
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
activate();
function activate() {
return getAvengers().then(function() { //Callback 3
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return dataservice.getAvengers()
.then(function(data) { //Callback 2
vm.avengers = data;
return vm.avengers;
});
}}
The point of this first callback is to do any manipulation with the data prior to it entering into the app and to actually pull the useful data out of the http response object.
vm.avengers is declared at the top of your controller. It's using the "controller as" syntax and is being put on a reference to the "this" object of the controller. You're ultimately using vm.avengers to access the data in the view.
Correct.
HTTP Call -> getAvengersComplete -> getAvengers, so correct 3 callbacks.
With AngularJS, I'm trying to implement a simple service that returns a list of clients from a REST service which is then available in a controller.
I am stuck in figuring out how to properly pass data to the controller. Below is my service and it pulls the data just fine. I've verified that the data is there
app.service('clientsService', ['$http', function ($http) {
var serviceBase = 'http://localhost:56879/api/';
this.getClients = function () {
return $http.get(serviceBase + 'clients').then(function (results) {
console.log(results.data);
return results.data;
});
};
}]);
Next I attempt to use this in a controller
app.controller('clientsController', ['$scope', 'clientsService', function ($scope, clientsService) {
this.clients = clientsService.getClients();
console.log(this.clients);
}]);
In this controller, this.clients doesn't contain the data, it just contains a try-catch block
Object {then: function, catch: function, finally: function}
catch: function (a){return this.then(null,
finally: function (a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,g){var f=null;try{f=(a||c)()}catch(h){return b(h,!1)}return f&&P(f.then)?f.then(function(){return b(e,g)},function(a){return b(a,!1)}):b(e,g)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}
then: function (b,g,h){var m=e(),u=function(d){try{m.resolve((P(b)?b:c)(d))}catch(e){m.reject(e),a(e)}},F=function(b){try{m.resolve((P(g)?g:d)(b))}catch(c){m.reject(c),a(c)}},v=function(b){try{m.notify((P(h)?h:c)(b))}catch(d){a(d)}};f?f.push([u,F,v]):k.then(u,F,v);return m.promise}
__proto__: Object
I can't understand quite yet what it is that I've done incorrectly to actually pass data from the service to the controller.
That is because getClients method returns a promise, not data. The promise resolves to return the data in the callback. The methods that you are seeing in the console is of that of the promise object returned by the service method. So you should register a callback to then method of the promise:-
var _that = this;
clientsService.getClients().then(function(data) { //Runs when promise is resolved
_that.clients = data;
}).catch(function(){ //<-- Runs if the promise is rejected
});
You could look at the ngResource service which is really easy to use. It is based on $http but with a little more abstraction :
https://docs.angularjs.org/api/ngResource/service/$resource
I want to factor out the angularjs $http success callback function so that instead of having two (or N) anonymous callback functions I have one named callback function.
Here are the two controllers:
function CreateCurriculumCtrl($scope, $http, $location, select2Options){
$scope.curriculumInfo = {};
$scope.curriculumInfo.statusOK = true;
$scope.select2Options = select2Options;
$scope.saveCurriculum = function(){
$http.post('bignibou/curriculum/new', $scope.curriculumInfo).success(function(curriculumInfo) {
if(curriculumInfo.statusOK == true){
$scope.curriculumInfo.statusOK=true;
$location.path('/view/'+curriculumInfo.curriculum.id);
}
else{
$scope.curriculumInfo.statusOK = false;
$scope.curriculumInfo.errors = curriculumInfo.errors;
}
});
};
}
function EditCurriculumCtrl($scope, $http, $location, select2Options, $routeParams){
$scope.curriculumInfo = {};
$scope.curriculumInfo.statusOK = true;
$scope.select2Options = select2Options;
$scope.id = $routeParams.id;
$http.get('/bignibou/utils/findCurriculumById.json',{params: {id: $routeParams.id}}).success(
function(curriculum){
$scope.curriculumInfo.curriculum = curriculum;
});
$scope.editCurriculum = function(){
$http.post('bignibou/curriculum/edit/'+$routeParams.id, $scope.curriculumInfo)
.success(function(curriculumInfo) {
if(curriculumInfo.statusOK == true){
$scope.curriculumInfo.statusOK=true;
$location.path('/view/'+curriculumInfo.curriculum.id);
}
else{
$scope.curriculumInfo.statusOK = false;
$scope.curriculumInfo.errors = curriculumInfo.errors;
}
});
};
}
I am not sure how to do that because what will become the named callback function has a couple of dependencies (i.e. $scope and $location).
If I extract the function (named callback) out of the angularjs controllers, then the named
callback has no longer access to its dependencies.
Can anyone please help with factoring out the success callback function and making sure the dependencies are satisfied?
Just pass them as arguments.
First, $http.post expects the callback to accept one argument. So we write the function that post expects:
function (curriculumInfo) {
// .....
}
But the body of the function needs access to $scope and $location. So we write a function that accepts those and return the function that post expects:
function (scope,location) {
return function (curriculumInfo) {
// ... use scope and location in here
}
}
Now we can name the function appropriately. Lets see, it's handling the response to new curriculum so I'd call it either new_curriculum_callback or new_curriculum_callback or handle_new_curriculum to indicate that it's a callback:
function handle_new_curriculum (scope,location) {
return function (curriculumInfo) {
}
}
Now you can call it to return the callback function to post:
$http
.post('bignibou/curriculum/new',$scope.curriculumInfo)
.success(handle_new_curriculum($scope,$location));
Create a Curriculum service and refactor every call to the backend into it.
Your controllers shouldn't care on how to getting the data. Inject the Curriculum service into your controllers. Your controllers then simply call functions on the Curriculum service and get the data.
angular.module('resources.curriculum', ['..'])
.factory('Curriculum', function ($http, $q) {
return {
create: function(dataToSave) {
var deferred = $q.defer()
http.get('...').success(function(data) {
deferred.resolve(data)
}).error(function(err) {
deferred.reject(err)
})
return deferred.promise
}
}
})
.controller('SomeCtrl', function($scope, Curriculum) {
$scope.someValue = Curriculum.create($scope.someDataToSave)
})
You would probably want to create angularjs service and call particular request on click of some button. This way you call requests in a synchronous way.
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.