I have the following
Service:
angular.module('app')
.factory('UserFactory', function ($http) {
function profile () {
return $http.get('/gimme')
.then(function success (response) {
return response;
});
};
var user = {
profile: profile
};
return user;
It is used in a controller as follows:
Controller
angular.module('app')
.controller('HeaderCtrl', function ($scope, UserFactory) {
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
$scope.user = UserFactory.profile().then(function (response) {
$scope.user = response.data;
});
$scope.change = function () {
$scope.user.name = 'New Name'
}
}
If I call the change() method in a directive which uses HeaderCtrl, what is the best way to make sure that that change, which temporarily changes the user.name, actually changes it on my server as well? In other words, how would I trigger the put request (I am assuming some function needs to be called on the Factory, but I am not sure the best way to make sure it is called or where to put the function call in the controller).
Thanks
Here's an example extending the code you provided, using free JSONPlaceholder API. I think example itself is enough of an answer?
HTML
<body ng-controller="Ctrl as vm">
<div>data: {{ vm.todo | json }}</div>
<div>response: {{ vm.response | json }} </div>
<hr>
<button type="button" ng-click="vm.change('my new title')">Change title</button>
</body>
JavaScript
app.controller('Ctrl', function(TodoService) {
var vm = this;
var id = 1;
TodoService.getPost(id).then(function(response) { // Get
vm.todo = response.data;
});
vm.change = function(val) {
vm.todo.title = val;
TodoService.putPost(vm.todo).then(function(response) { // Put
vm.response = response.status + ' ' + response.statusText;
}).catch(function(error) {
vm.response = angular.toJson(error);
});
};
});
app.factory('TodoService', function($http) {
var endpoint = 'http://jsonplaceholder.typicode.com/todos/';
var todoService = {};
todoService.getPost = function(id) {
return $http.get(endpoint + id).then(function(response) {
return response;
});
};
todoService.putPost = function(todo) {
return $http.put(endpoint + todo.id, todo).then(function(response) {
return response;
});
};
return todoService;
});
Related plunker here http://plnkr.co/edit/VBvVen
Related
I am using AngularJS and I have 2 controllers which are siblings. The first controller gets data from an $http request on click. The second controller has to retrieve this data, but I don't know how this controller will get the data because it has to wait for the function in the first controller before it can get its data.
This is the first controller:
$scope.getMessageData = function(username, full_url, main_item, item_id, sub_id){
$scope.ajax_spinner = true;
$http({
method: "GET",
url: "/getMessageData",
params:{
"adUsername" : username,
"fullUrl" : full_url,
"mainItem" : main_item,
"tagId" : item_id,
"subId" : sub_id
}
}).then(function successCallback(response) {
$scope.adUsername = response.data.adUsername;
$scope.fullUrl = response.data.fullUrl;
$scope.main_item = response.data.main_item;
$scope.message = response.data.docs[0];
$scope.sub_item = response.data.sub_docs[0];
$scope.ajax_spinner = false;
Data.setItem($scope.message)
});
};
The factory I use to share the data with the second controller:
App.factory('Data', function(){
return {
setItem: function(item){
this.item = item;
},
getItem: function(){
return this.item;
}
};
});
The second controller:
App.controller("dataController", function($scope, $http, $sce, Data){
$scope.message = Data.getItem();
console.log($scope.message)
});
Now obviously $scope.message is undefined because the controller already loads on page load, but the getMessageData function is not ready called, so how can I "wait" for the second controller to load before getMessageData is fired?
You could use $watch in your controller to keep up with the changes in your service, e.g.
var unwatch = $scope.$watch(function() {
/* set Data.item value in your service */
return Data.item;
}, function(item) {
$scope.item = item || 'nothing here';
});
$scope.$on('$destroy', unwatch);
Or you could implement simple mechanism to register and unregister callbacks in your service to get notified when ever data is changed, e.g.
Template
<html ng-app="App">
<head>
...
</head>
<body>
<div ng-controller="SomeController">
SomeController
<button ng-click="getData()">Fetch</button>
<div ng-bind="data | json"></div>
</div>
<br>
<div ng-controller="OtherController">
OtherController
<div ng-bind="data | json"></div>
</div>
</body>
</html>
JavaScript
angular.module('App', [])
.controller('SomeController', function($scope, Data) {
$scope.getData = function() {
Data.getData().then(function(data) {
$scope.data = data;
});
};
})
.controller('OtherController', function($scope, Data) {
var callback = function(data) {
$scope.data = data;
};
Data.register(callback);
$scope.$on('$destroy', function() {
Data.unregister(callback);
});
})
.factory('Data', function($http) {
var callbacks = [];
return {
register: function(callback) {
var index = callbacks.indexOf(callback);
if (index === -1) {
callbacks.push(callback);
}
},
unregister: function(callback) {
var index = callbacks.indexOf(callback);
if (index !== -1) {
callbacks.splice(index, 1);
}
},
getData: function() {
/* where data.json is { "message": "My message" } */
return $http.get('data.json').then(function(response) {
var data = response.data;
data.timestamp = (new Date()).getTime();
/* move inside $interval out of this fn maybe... */
angular.forEach(callbacks, function(callback) {
callback(data);
});
return data;
});
}
};
});
Im using angular service with a variable and my app.js looks like..,
app.service('sessionService', function() {
var users = [
name: ' ',
age: ' ',
gender: M,
marStatus: single
];
});
I have a logout option, how to reset my variable users to its default option?
You need to create a service with http call to your server action.The response will always set the data in a correct way.
var app = angular.module('myApp', []);
app.service('getUserDetailsService', ['$http', function($http) {
this.getDetails = function() {
var details = {name:'',age:'',gender:'M'}
$http.get('Your action url')
.success(function(data) {
details.name = data.name;
details.age = data.age;
details.gender = data.gender;
})
.error(function() {
//handle error
});
return details;
};
}]);
//Controller
app.controller("userController",function($scope,getUserDetailsService){
$scope.getDetails = function() {
getUserDetailsService.getDetails().then(function(detail) {
$scope.details = detail;
}, function(error) {
});
}
$scope.getDetails() //Call whenever you want
});
Fiddle link - https://jsfiddle.net/ftzyyt0L/3/
I'm very new to AngilarJS. I am trying to write a service in angularJS.
<script>
var module = angular.module("myapp", []);
module.service('BrandService', function ($http) {
var brands = [];
this.getBrands = function()
{
return $http.get('http://admin.localhost/cgi-bin/brand.pl')
.then(function(response)
{
brands = response.brands;
alert (brands);
});
}
//simply returns the brands list
this.list = function ()
{
return brands;
}
});
module.controller("brandsController", function($scope, BrandService) {
$scope.brandlist = BrandService.list();
alert ($scope.brandlist);
});
</script>
The statement "alert (brands);" is not getting called. What is the issue with this code. Is m missing any thing in implementation?
$http calls are always async. Meaning, even you do a .then at your service, there is no way it will properly the resolved data back into your controller. You will have to write it in your controller.
Your Service:
module.service('BrandService', function($http) {
var brands = [];
this.getBrands = function() {
//do not need the dot then.
return $http.get('http://admin.localhost/cgi-bin/brand.pl')
}
//simply returns the brands list
this.list = function() {
return brands;
}
});
In your controller:
module.controller("brandsController", function($scope, BrandService) {
BrandService.list()
.then(function(response) {
$scope.brandlist = response.brands;
alert($scope.brandlist);
});
});
In service:
this.getBrands = function() {
$http.get('http://admin.localhost/cgi-bin/brand.pl').then(function(response) {
brands = response.brands;
alert(brands);
return brands;
});
}
In controller:
$scope.brandlist = BrandService.getBrands();
alert($scope.brandlist);
I already write a code to display a loader div, when any resources is in pending, no matter it's getting via $http.get or routing \ ng-view.
I wan't only information if i'm going bad...
flowHandler service:
app.service('flowHandler', function(){
var count = 0;
this.init = function() { count++ };
this.end = function() { count-- };
this.take = function() { return count };
});
The MainCTRL append into <body ng-controller="MainCTRL">
app.controller("MainCTRL", function($scope, flowHandler){
var _this = this;
$scope.pageTitle = "MainCTRL";
$scope.menu = [];
$scope.loader = flowHandler.take();
$scope.$on("$routeChangeStart", function (event, next, current) {
flowHandler.init();
});
$scope.$on("$routeChangeSuccess", function (event, next, current) {
flowHandler.end();
});
updateLoader = function () {
$scope.$apply(function(){
$scope.loader = flowHandler.take();
});
};
setInterval(updateLoader, 100);
});
And some test controller when getting a data via $http.get:
app.controller("BodyCTRL", function($scope, $routeParams, $http, flowHandler){
var _this = this;
$scope.test = "git";
flowHandler.init();
$http.get('api/menu.php').then(function(data) {
flowHandler.end();
$scope.$parent.menu = data.data;
},function(error){flowHandler.end();});
});
now, I already inject flowHandler service to any controller, and init or end a flow.
It's good idea or its so freak bad ?
Any advice ? How you do it ?
You could easily implement something neat using e.g. any of Bootstrap's progressbars.
Let's say all your services returns promises.
// userService ($q)
app.factory('userService', function ($q) {
var user = {};
user.getUser = function () {
return $q.when("meh");
};
return user;
});
// roleService ($resource)
// not really a promise but you can access it using $promise, close-enough :)
app.factory('roleService', function ($resource) {
return $resource('role.json', {}, {
query: { method: 'GET' }
});
});
// ipService ($http)
app.factory('ipService', function ($http) {
return {
get: function () {
return $http.get('http://www.telize.com/jsonip');
}
};
});
Then you could apply $scope variable (let's say "loading") in your controller, that is changed when all your chained promises are resolved.
app.controller('MainCtrl', function ($scope, userService, roleService, ipService) {
_.extend($scope, {
loading: false,
data: { user: null, role: null, ip: null}
});
// Initiliaze scope data
function initialize() {
// signal we are retrieving data
$scope.loading = true;
// get user
userService.getUser().then(function (data) {
$scope.data.user = data;
// then apply role
}).then(roleService.query().$promise.then(function (data) {
$scope.data.role = data.role;
// and get user's ip
}).then(ipService.get).then(function (response) {
$scope.data.ip = response.data.ip;
// signal load complete
}).finally(function () {
$scope.loading = false;
}));
}
initialize();
$scope.refresh = function () {
initialize();
};
});
Then your template could look like.
<body ng-controller="MainCtrl">
<h3>Loading indicator example, using promises</h3>
<div ng-show="loading" class="progress">
<div class="progress-bar progress-bar-striped active" style="width: 100%">
Loading, please wait...
</div>
</div>
<div ng-show="!loading">
<div>User: {{ data.user }}, {{ data.role }}</div>
<div>IP: {{ data.ip }}</div>
<br>
<button class="button" ng-click="refresh();">Refresh</button>
</div>
This gives you two "states", one for loading...
...and other for all-complete.
Of course this is not a "real world example" but maybe something to consider. You could also refactor this "loading bar" into it's own directive, which you could then use easily in templates, e.g.
//Usage: <loading-indicator is-loading="{{ loading }}"></loading-indicator>
/* loading indicator */
app.directive('loadingIndicator', function () {
return {
restrict: 'E',
scope: {
isLoading: '#'
},
link: function (scope) {
scope.$watch('isLoading', function (val) {
scope.isLoading = val;
});
},
template: '<div ng-show="isLoading" class="progress">' +
' <div class="progress-bar progress-bar-striped active" style="width: 100%">' +
' Loading, please wait...' +
' </div>' +
'</div>'
};
});
Related plunker here http://plnkr.co/edit/yMswXU
I suggest you to take a look at $http's pendingRequest propertie
https://docs.angularjs.org/api/ng/service/$http
As the name says, its an array of requests still pending. So you can iterate this array watching for an specific URL and return true if it is still pending.
Then you could have a div showing a loading bar with a ng-show attribute that watches this function
I would also encapsulate this requests in a Factory or Service so my code would look like this:
//Service that handles requests
angular.module('myApp')
.factory('MyService', ['$http', function($http){
var Service = {};
Service.requestingSomeURL = function(){
for (var i = http.pendingRequests.length - 1; i >= 0; i--) {
if($http.pendingRequests[i].url === ('/someURL')) return true;
}
return false;
}
return Service;
}]);
//Controller
angular.module('myApp')
.controller('MainCtrl', ['$scope', 'MyService', function($scope, MyService){
$scope.pendingRequests = function(){
return MyService.requestingSomeURL();
}
}]);
And the HTML would be like
<div ng-show="pendingRequests()">
<div ng-include="'views/includes/loading.html'"></div>
</div>
I'd check out this project:
http://chieffancypants.github.io/angular-loading-bar/
It auto injects itself to watch $http calls and will display whenever they are happening. If you don't want to use it, you can at least look at its code to see how it works.
Its very simple and very useful :)
I used a base controller approach and it seems most simple from what i saw so far. Create a base controller:
angular.module('app')
.controller('BaseGenericCtrl', function ($http, $scope) {
$scope.$watch(function () {
return $http.pendingRequests.length;
}, function () {
var requestLength = $http.pendingRequests.length;
if (requestLength > 0)
$scope.loading = true;
else
$scope.loading = false;
});
});
Inject it into a controller
angular.extend(vm, $controller('BaseGenericCtrl', { $scope: $scope }));
I am actually also using error handling and adding authorization header using intercepting $httpProvider similar to this, and in this case you can use loading on rootScope
I used a simpler approach:
var controllers = angular.module('Controllers', []);
controllers.controller('ProjectListCtrl', [ '$scope', 'Project',
function($scope, Project) {
$scope.projects_loading = true;
$scope.projects = Project.query(function() {
$scope.projects_loading = false;
});
}]);
Where Project is a resource:
var Services = angular.module('Services', [ 'ngResource' ]);
Services.factory('Project', [ '$resource', function($resource) {
return $resource('../service/projects/:projectId.json', {}, {
query : {
method : 'GET',
params : {
projectId : '#id'
},
isArray : true
}
});
} ]);
And on the page I just included:
<a ng-show="projects_loading">Loading...</a>
<a ng-show="!projects_loading" ng-repeat="project in projects">
{{project.name}}
</a>
I guess, this way, there is no need to override the $promise of the resource
Hi I have two controllers
pqsAppModule.controller('NotificationBoxController',function($scope,items) {
$scope.list = items.list();
})
and
pqsAppModule.controller('NotificationController',function($scope,items) {
$scope.list = items.list();
})
I need to create an "items" service that would do an ajax request and return data for any controller which would have it injected. And I want, that the query will be done only once, and items will be shared between all controllers.
pqsAppModule.factory('items', function($http) {
var items = [];
var itemsService = {};
$http.get('api/notification').then(function(response){
items = response.data;
});
itemsService.list = function() {
return items;
};
return itemsService;
});
But I don't understand why angular makes the request, and receives data, but all items in controllers are empty.
It happens because the factory should be defined by different way.
(I took dummy URL only to load data by async way)
HTML
<div ng-controller="NotificationBoxController">
<button ng-click="showMe();">press me</button>
<pre>NotificationBoxController: {{items.getList()|json}}</pre>
</div>
<div ng-controller="NotificationController">
<pre>NotificationController: {{items.getList()|json}}</pre>
</div>
JS
var pqsAppModule = angular.module('myApp', []);
pqsAppModule.controller('NotificationBoxController',function($scope,items) {
$scope.items = items;
$scope.showMe= function(){
items.query();
}
});
pqsAppModule.controller('NotificationController',function($scope,items) {
$scope.items = items;
});
pqsAppModule.factory('items', function ($http) {
var current = {};
var address = 'Singapore, SG, Singapore, 153 Bukit Batok Street 1';
var URL = 'http://maps.googleapis.com/maps/api/geocode/json?address=' + address + '&sensor=true';
var factory = {
query: function () {
var data = $http({method: 'GET', url:URL}).then(function (result) {
current = result.data.results[0];
}, function (result) {
alert("Error: No data returned");
});
},
getList: function () {
return current;
}
}
return factory;
});
Demo Fiddle
From this example we call items.getList() from HTML for both controllers. But if we want to update the model through the controller, we need a listener like $watch
Try this
$http.get('api/notification').then(function(response){
angular.foreach(response.data,function(item) {
items.push(item);
});
});
Basically do not create a new array but fill the existing one.