I have an application with a parent controller called DashboardCtrl, and multiple child controllers (DashboardBuyerCtrl, DashboardSellerCtrl...)
I'd like to be able to share the results of an HTTP request between the different controllers.
At the moment, I'm executing my HTTP request in every controller, but I think that this is not ideal performance wise.
I've read that I should use service, but I'm stuck here.
can anyone help?
Thanks
Using services is, in general, a good practice because helps you to organize and share the code across your application, I've created a small app to showcase how services could be created and reused in controllers:
var app = angular.module('testApp', []);
app.controller('ctrlA', function($scope, getIp) {
getIp().then(function(resp) {
$scope.data = resp.data.origin;
});
});
app.controller('ctrlB', function($scope, getIp) {
getIp().then(function(resp) {
$scope.data = resp.data.origin;
});
});
app.service('getIp', function($http) {
return function() {
return $http.get('https://httpbin.org/ip');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="testApp">
<div ng-controller="ctrlA">
First controller
<p>IP: {{data}}</p>
</div>
<div ng-controller="ctrlB">
Second controller
<p>IP: {{data}}</p>
</div>
</div>
Regarding performance, I'd suggest using caching option if content is not being updated frequently(or not updated at all), like so:
$http({
url: '...',
method: 'GET',
cache: true
});
This will save request's response for future use.
You can read more about services in the official documentation.
Related
Suppose my web app needs to make a http request to get my site title, site description and so on. Since these variables are common to all pages, it makes sense to request those every time a user enter the site.
The question is, where do I make those calls? In run block? Or to create a root controller to do these tasks?
You can use one of these two approaches:
Make your call in run block and store the value in $rootScope and
use anywhere you want,
In your states, use resolve to get the page title and details , and
get it in the views , For ease use resolve in root state and use the
resolved variable as a dependency in other child or sibling routes
to get values..
$stateProvider.state('root', {
resolve:{
// Example using function with simple return value.
promiseObj: function($http){
// $http returns a promise for the url data
return $http({method: 'GET', url: '/someUrl'});
}
})
.state('sibling',{
controller:function($scope,promiseObj){
$scope.title = promiseObj.title;
}
})
You could define controller at the <html> level.
<html ng-app="app" ng-controller="initCtrl">
<head>
<title>{{ Page.title() }}</title>
...
You create service: Page and modify from controllers.
myModule.factory('Page', function() {
var title = //Code to get the title
.... //Other vars
return {
title: function() { return title; },
setTitle: function(newTitle) { title = newTitle }
...
};
});
Inject Page and Call 'Page.setTitle()' from controllers.
I am trying to run an $http function when my AngularJS application first loads.
This $http function needs to finish before any of the controllers in my application could properly function. How would I go about doing this? This sounds like a promise, but it sounds like I would be creating a promise in each controller...
I currently have the function that I want to run first like this:
app.run(function() {
$http.get('link').success(function(data) {
// success function. The data that I get from this HTTP call will be saved to a service.
}).error(function(error) {
});
});
However, sometimes the controller will load before the http call finishes.
The problem
Angular is not dynamic, you cannot add controller dynamically neither factory, etc. Also you cannot defer controller bootstrap, angular loads everything together, and it's quite disadvantage (will be fixed in Angular 2)
The cure
But javascript itself has very important feature - closure, which works anywhere, anytime.
And angular has some internal services that can be injected outside of angular ecosystem, even into browser console. Those services injected as shown below. We technically could use anything else (jQuery.ajax, window.fetch, or even with XMLHttpRequest), but let's stick with total angular solution
var $http_injected = angular.injector(["ng"]).get("$http");
The act
First of all, we defer whole angular app bootstrap, inject http service. Then you make your needed request, receive data and then closure get's to work, we pass received data into some service, or we could also assign in to some angular.constant or angular.value but let's just make demo with angular.service, so when your service has data, bootstrap whole app, so that all controllers get initialized with your needed data
Basically that kind of tasks solved like this
<body>
<div ng-controller="Controller1">
<b>Controller1</b>
{{text}}
{{setting.data.name}}
</div>
<hr>
<div ng-controller="Controller2">
<b>Controller2</b>
{{text}}
{{setting.data.name}}
</div>
<script>
//define preloader
var $http_injected = angular.injector(["ng"]).get("$http");
$http_injected.get('http://jsonplaceholder.typicode.com/users/1').then(function(successResponse) {
//define app
angular.module('app', []);
//define test controllers
//note, usually we see 'controller1 loaded' text before 'settings applied', because controller initialized with this data, but in this demo, we will not see 'controller1 loaded' text, as we use closure to assign data, so it's instantly changed
angular.module('app').controller('Controller1', function($scope, AppSetting) {
$scope.text = 'controller1 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
angular.module('app').controller('Controller2', function($scope, AppSetting) {
$scope.text = 'controller2 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
//define test services, note we assign it here, it's possible
//because of javascript awesomeness (closure)
angular.module('app').service('AppSetting', function() {
this.setting = successResponse;
});
//bootstrap app, we cannot use ng-app, as it loads app instantly
//but we bootstrap it manually when you settings come
angular.bootstrap(document.body, ['app']);
});
</script>
</body>
Plunker demo
You can do this when you configure your routes
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: 'main.html',
resolve: {
data: ['$http',
function($http)
{
return $http.get('/api/data').then(
function success(response) { return response.data.rows[0]; },
function error(reason) { return false; }
);
}
]
}
});
}]);
Similar question:
AngularJS - routeProvider resolve calling a service method
AngularJS: $routeProvider when resolve $http returns response obj instead of my obj
Heres a plunkr I found using a service, which is what I would recommend.
http://plnkr.co/edit/XKGC1h?p=info
I'm writing an Ionic app to read some data from a web API I created with asp.net MVC4. The Ionic app reads the data just fine when I'm hosting it on my local machine but the angular factory does not return anything when I try to read from the web API when it is published on Azure. Is there any logical reason why this wouldn't work when it is published to Azure?
The json string is the same on both when i visit the local and azure site.
[{"ID":1,"Name":"Fireworks","Time":"2015-11-30T21:00:00","Comments":"Be there by 8:00"},
{"ID":2,"Name":"Haircut","Time":"2015-11-30T21:00:00","Comments":"Be there by 8:00"}]
Here is the code for my angular factory
.factory('EventsFactory', ['$http', function($http) {
return $http.get('webAPI link here')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}])
Here is the code for my angular controller
.controller('EventCtrl', ['$scope', 'EventsFactory', function($scope, EventsFactory) {
EventsFactory.success(function(data){
$scope.activities = data;
});
}])
Here is the code for my view
<div class="list card" ng-controller="EventCtrl">
<div class="item item-divider">Upcoming Events</div>
<div class="item item-body" ng-repeat="activity in activities">
<div>{{activity.Name}}</div>
</div>
</div>
Why you add success method with Controller and factory. add success method only controller. like
.factory('EventsFactory', ['$http', function($http) {
return $http.get('webAPI link here');
}])
In your controller, you need to use then
EventsFactory().then(function(data){
$scope.activites = data;
})
I have a basic app, that fetches some data through the $http service, however it doesnt render the data correct in the template, when the template is served from the template cache. My code looks like this:
angular.module('app', [])
api service:
.factory('api', function($http, $q) {
return {
getCars: function() {
return $http.get('api/cars');
}
};
})
the controller using the service:
.controller('carsCtrl', function($scope, api) {
api.getCars().success(function(data) {
$scope.cars = data;
});
})
the route setup:
.config(function($routeProvider) {
$routeProvider.when('/cars', {
templateUrl: 'cars.html',
controller: 'carsCtrl'
});
});
and the template cars.html
<div ng-repeat="car in cars">
{{ car }}
</div>
this works the first time the browser hits /cars, however, if I push the back on forward button in the browser to hit the url a second time without a page reload, the {{car}} is not being rendered. If the cars.html is put in the templateCache like this:
angular.module('app').run(function($templateCache) {
$templateCache.put('cars.html', '<div ng-repeat="car in cars">{{ car }}</div>');
});
the {{car}} binding is not rendered either.
I suspect this has something to do with Angular not unwrapping promises in templates anymore, but not totally sure. Does anyone know what I am doing wrong and how to write this code correctly?
Well, I saw some syntax errors in your code (maybe you didn't copy the code but typed it manually for SO not sure). Also you returned deferred instead of deferred.promise. What you trying to achieve works just fine:
Plnkr Example
Below is my HTML and the code to do a web service call and display the data in ngGrid. The problem is with the route provider, I'm not being able to show the grid in my separate view, but if I do the exact same code without the route provider,and load just that page, it works perfectly fine.
Since I'm very new to angularJS, any suggestions would be appreciated.I've done lot of research but did not work, at least for me. Please consider this if I have missed related post somewhere. Thanks ahead!
<div>
<!--Placeholder for views-->
<div ng-view=""></div>
</div>
//this is what I have in one of my view for grid.
<div class="gridStyle" data-ng-grid="gridOptions">
</div>
/* display/get/call the JSON data from the web service and bind it to the view */
var app = angular.module('salesApp',['ngGrid']);
app.config (['$routeProvider', function ($routeProvider){
$routeProvider
.when('/sales',
{
controller:'salesCtrl',
templateUrl:'Partials/sales.html'
})
.when('/associate',
{
controller:'assocCtrl',
templateUrl:'Partials/associate.html'
})
.otherwise({redirectTo:'/sales'});
}]);
app.controller('salesCtrl', function($scope, $http) {
$http.jsonp('http://some url...')
.success(function (data) {
$scope.sales = data;
});
$scope.gridOptions = {data: 'sales',
columnDefs:[{field:'Region_Num', displayName: 'Region Num'}
],
showGroupPanel: true
};
});
This is an older post; but I was having the same problem. It turns out when I defined the app/Moduile; I was not passing ngGrid in as one of the options:
myApp = angular.module('myApp', ['ngGrid']);
That is clearly not your problem; as I do see the statement in your code; so this answer is probably for other people who find this question via Google.