I have an Angular app which makes some calls (POST and GET for now) to a backend service (powered by node.js with a REST interface). While developing the app itself I noticed it makes two requests to the backend each time a button is pressed or a page is loaded. Curiously everything works but each time I press some button the backend gets two requests. I am not using any fancy package only ngRoute, ngResource and routeStyles for css partials. Anybody has an idea of what could be the reason why the app behaves like that?
I actually found another question similar to this one but the OP there was using express aside of Angular and there is no answer...
EDIT added some code.
in app.js:
'use strict';
var cacheBustSuffix = Date.now();
angular.module('myApp', ['myApp.controllers', 'myApp.services', 'myApp.filters', 'ngRoute', 'ngResource', 'routeStyles'])
.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$locationProvider
.html5Mode({enabled: true,
requireBase: false})
.hashPrefix('!');
$routeProvider
.when('/', {redirectTo: '/myApp'})
.when('/myApp', {
templateUrl: '/partials/home.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrlHome'
})
.when('/myApp/search', {
templateUrl: '/partials/search.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrlSearch'
})
.when('/myApp/list/', {
templateUrl: '/partials/list.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrlList'
})
// a bunch of other redirections
.otherwise({
templateUrl: '/partials/404.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrl404'});
}]);
from services.js:
'use strict';
var app = angular.module('myApp.services', ['ngResource']).
factory('List', function ($resource) {
return $resource(WSROOT + '/search', {}, {get: {method: 'GET', isArray: false}});
});
from controllers.js, one controller that makes multiple requests
var controllers = angular.module('myApp.controllers', []);
var ctrlList = controllers.controller('ctrlList', function ($scope, $window, List) {
$window.document.title = 'myApp - List';
List.get({}, function (data) {
// $scope.res is an array of objects
$scope.res = data.response;
$scope.nitems = data.response.length;
});
});
ctrlList.$inject = ['$scope', 'List'];
And the network call when loading the index+home and navigating to some other page. As you can see, it first loads the index page, the scripts and styles listed there (not shown), then the home where I have a controller similar to the one above and suddenly two wild request to my web server:
Can we see your HTML files? I had this problem a while back. My solution was that by declaring a controller in the routing, and in the pages, a double post was created as each controller was loaded twice.
//Home
.state('tab.home', {
url: '/home',
views: {
'tab-home': {
templateUrl: 'templates/tab-home.html',
controller: 'HomeCtrl' // <-- This goes away
}
}
})
Related
I've my backend developed in Silex with various routes that I can fully access using localhost:8080/project/api/index.php/user (example of a route).
if i'm coding an http get(I use only one controller called mainController ) in a js file using angular 1.5.8, how can I make that when I enter angularsite/user
(user is the route, "/user"), it returns me the data and then show it on my index.html ?
You need to set your app routes to your mainController controller.
angular.module('YourProject', [
'YourModule',
'ngRoute'
])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/user', {
templateUrl: 'view.html',
controllerAs: 'mainCtrl',
controller: 'mainController '
});
$routeProvider.otherwise({
redirectTo: '/user'
});
}])
Now when the page loads the RouteProvider will finds the default path to user (default route) route and that will load your view and controller. The templateUrl contains the route to the template.
If you only want to load a partial page you can do something like this..
'use strict';
var App = angular.module('YourProject', ['ngRoute', 'AppControllers']);
App.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/user', {
templateUrl: 'user.html'
}).
otherwise({
redirectTo: '/path'
});
}]);
Since your question mentions an API if you are trying to just load some data you can call user API with something like this from your controller (provided that the url given is an API which accepts GET request and responds with JSON data)..
var AppControllers = angular.module('AppControllers', []);
AppControllers.controller('MainController', ['$scope','$http', function ($scope, $http) {
//Declare a function to fetch user data from API
$scope.user= function () {
var req = {
method: 'GET',
url: baseURL + 'project/api/index.php/user'
}
return $http(req).success(function (data,status,header) {
$scope.user = data;
}).error(function () {
console.log(error);
});
}
//Call the get user function
$scope.user();
}]);
And finally in your partial file (user.html) add the controller to bind it with the controller declared above.
<!doctype html>
<html ng-app="YourProject">
<body>
<head>
<title>User Page</title>
</head>
<div class="user-content" ng-controller="MainController">
</body>
</html>
SOLVED:
Resolution here:
https://stackoverflow.com/a/29662815/4004456
Batarang Chrome plugin was firing all ng-click actions twice. Nothing wrong with my code.. Batarang owes me an afternoon...
NOTE: I have attempted all fixes in: Combating AngularJS executing controller twice
EDIT: something puzzling:
It seems all controllers are initialised twice, as it doesn't matter which view calls which function, it its fired twice, this is what lead me to believe it is a problem within the app.js file..
EDIT: resolutions I've attempted:
I've tried removing bower dependencies one by one to rule out a dependency causing it.
I've scoured my code for double controller init (both in index.html and then again in a div / bootstrap init. )
I've removed my custom directives one by one to ensure the "restrict:" / controller duplication isn't occurring.
I've removed all ng-if statements from the application.
Scoured all html for a duplicate ng-view / ui-view etc.
Tried all combinations of routes + links having and not having trailing slashes.
My issue however is nearly identical to the above question, my controllers are firing functions twice on button click.
I've tried to set up a plunkr but the code is too complex for me to get it working properly.. I've stripped any bloat out of the below code.
index.html
<section ng-show="!stateIsLoading"> <div ng-view></div></section>
<section class="colorful" ng-hide="!stateIsLoading">
<three-bounce-spinner></three-bounce-spinner>
</section>
app.js
'use strict';
/**
* #ngdoc overview
* #name smcmsApp
* #description
* # smcmsApp
*
* Main module of the application.
*/
angular
.module('smcmsApp', [
'ngAnimate',
'ngAria',
'ngCookies',
'ngMessages',
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch',
'ui.bootstrap',
'angular-spinkit',
'restangular'
])
.config(function ($routeProvider, RestangularProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'AboutCtrl'
})
.when('/sales', {
templateUrl: 'views/sales.html',
controller: 'SalesCtrl',
resolve: {
mailers: function (API) {
return API.mailers.getList();
},
totals: function (Restangular) {
return Restangular.all('profit').customGET();
}
}
})
.when('/design', {
templateUrl: 'views/design.html',
controller: 'DesignCtrl',
resolve: {
design: function (API) {
return API.design.getList();
}
}
})
.otherwise({
redirectTo: '/'
});
RestangularProvider.setBaseUrl('http://localhost/SMCMS_Angular/api/Slim/');
})
.run(function ($rootScope, $log) {
$rootScope.stateIsLoading = false;
$rootScope.$on('$routeChangeStart', function () {
console.log("route change start");
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$routeChangeSuccess', function () {
console.log("route change success");
$rootScope.stateIsLoading = false;
});
$rootScope.$on('$routeChangeError', function () {
//catch error
console.log("Route changing animation error");
});
});
DesignCtrl.js
angular.module('smcmsApp')
.controller('DesignCtrl', function ($scope, $log, design, Notifications, Data) {
/* ------------------------- Order Interactions --------------- */
$scope.saveOrder = function (order) {
Notifications.addAlert('error', 'called showModal() from design.js');
$log.debug("called showModal() from design.js");
};
});
views/design.html
<label ng-click="saveOrder(order)" class="btn btn-default">Save Details <span class="glyphicon glyphicon-floppy-open"></span>
I believe the problem is in the way the app handles during initialization. If you navigate to your application http://example.com/ then your url will be an empty string and trigger the route change. After it fails to resolve it will then hit the otherwise and cause the double route change/initialization.
Basically if you fire up the route yourself upon load you shouldn't see the double change. You can just inject the $location service and direct there yourself inside of the run assuming you want to start at any one given point. (see the plunk: http://plnkr.co/edit/lTOSYX?p=preview )
.run(function ($rootScope, $log, $location) {
$location.path('/');//Add this
// continue code here
});
If your controllers are double loading then you probably have other complications such as the double controller declaration/execution issue mentioned in the note (Combating AngularJS executing controller twice).
I am using controller in my Angularjs which gets question one by one from server and i want on specific condition this controller should call a routeprovider that should change my current view with templateUrl and put into a directive
my question is can i call route provider in controller rather than module
here is my CreateController
var CreateController = ['$scope', '$http', function ($scope, $http) {
$scope.model = {
};
................>
.................>
$scope.NextQuestion = function () {
$http.post('/Testing/NextQuestion', {
........................>
}).success(function (newdata) {
$scope.model = newdata;
var inccomplevel = $scope.model.servercomplevel;
if (qId == 10) {
here i need the code for routeProvider and directive please provide me help
}
......................>
}];
i did the route provider in app.js file there it works
here is the code for app.js when i do like this it works and when i shift the code of route provider
to the create controller in condition if qId == 10 there it does not work
var app = angular.module('app', ['ngRoute']);
app.controller('CreateController', CreateController);
app.config(function ($routeProvider) {
$routeProvider
.when('/',
{
templateUrl: 'Partials/TestCompleted.html',
controller: 'AppCtrl'
})
.otherwise({ redirectTo: '/' });
});
app.controller("AppCtrl",function ($scope) {
$scope.newmodel = {
}
});
Instead of trying to change the value of the templateUrl on the route, you should define another route. Then you can simply switch routes.
To change the route/view, you need update the path. You can do this using the $location service.
See AngularJS : How do I switch views from a controller function?
I am new to windows store app development. Any how manage to get worked angularjs inside Windows 8 store app by editing angular js library. But Could not understand how to use angulerjs routing inside store app.
Can anyone explain how to use angular js routing in side Windows Store app or give an example to how to do that.
I'm building a universal app and I'm using angularjs.
It is my app.js
var app = angular.module('app', ['ngRoute']);
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/', {
templateUrl: '/views/default.html',
controller: 'default'
}).
when('/customers', {
templateUrl: '/views/customers.html',
controller: 'customers'
}).
when('/products/:param', {
templateUrl: '/views/products.html',
controller: 'products'
}).
otherwise({
redirectTo: '/'
});
}]);
I have the below code for opening customers screen.
$scope.openCustomers = function () {
$location.path('/customers');
}
I have the below code for opening products screen.
$scope.openProducts = function () {
$location.path('/products/123');
}
It is my customers controller code
app.controller('customers', ['$scope', function ($scope) {
});
It is my products controller code
app.controller('products', ['$scope', '$routeParams', function ($scope,$routeParams) {
$scope.param= $routeParams.param;
});
I currently have an AngularJS application with routing built in.
It works and everything is ok.
My app.js file looks like this:
angular.module('myapp', ['myapp.filters', 'myapp.services', 'myapp.directives']).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', { templateUrl: '/pages/home.html', controller: HomeController });
$routeProvider.when('/about', { templateUrl: '/pages/about.html', controller: AboutController });
$routeProvider.when('/privacy', { templateUrl: '/pages/privacy.html', controller: AboutController });
$routeProvider.when('/terms', { templateUrl: '/pages/terms.html', controller: AboutController });
$routeProvider.otherwise({ redirectTo: '/' });
}]);
My app has a CMS built in where you can copy and add new html files within the /pages directory.
I would like to still go through the routing provider though even for the new dynamically added files.
In an ideal world the routing pattern would be:
$routeProvider.when('/pagename', { templateUrl: '/pages/pagename.html', controller: CMSController });
So if my new page name was "contact.html" I would like angular to pick up "/contact" and redirect to "/pages/contact.html".
Is this even possible?! and if so how?!
Update
I now have this in my routing config:
$routeProvider.when('/page/:name', { templateUrl: '/pages/home.html', controller: CMSController })
and in my CMSController:
function CMSController($scope, $route, $routeParams) {
$route.current.templateUrl = '/pages/' + $routeParams.name + ".html";
alert($route.current.templateUrl);
}
CMSController.$inject = ['$scope', '$route', '$routeParams'];
This sets the current templateUrl to the right value.
However I would now like to change the ng-view with the new templateUrl value. How is this accomplished?
angular.module('myapp', ['myapp.filters', 'myapp.services', 'myapp.directives']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/page/:name*', {
templateUrl: function(urlattr){
return '/pages/' + urlattr.name + '.html';
},
controller: 'CMSController'
});
}
]);
Adding * let you work with multiple levels of directories dynamically.
Example: /page/cars/selling/list will be catch on this provider
From the docs (1.3.0):
"If templateUrl is a function, it will be called with the following
parameters:
{Array.} - route parameters extracted from the current
$location.path() by applying the current route"
Also
when(path, route) : Method
path can contain named groups starting with a colon and ending with a star: e.g.:name*. All characters are eagerly stored in $routeParams under the given name when the route matches.
Ok solved it.
Added the solution to GitHub - http://gregorypratt.github.com/AngularDynamicRouting
In my app.js routing config:
$routeProvider.when('/pages/:name', {
templateUrl: '/pages/home.html',
controller: CMSController
});
Then in my CMS controller:
function CMSController($scope, $route, $routeParams) {
$route.current.templateUrl = '/pages/' + $routeParams.name + ".html";
$.get($route.current.templateUrl, function (data) {
$scope.$apply(function () {
$('#views').html($compile(data)($scope));
});
});
...
}
CMSController.$inject = ['$scope', '$route', '$routeParams'];
With #views being my <div id="views" ng-view></div>
So now it works with standard routing and dynamic routing.
To test it I copied about.html called it portfolio.html, changed some of it's contents and entered /#/pages/portfolio into my browser and hey presto portfolio.html was displayed....
Updated
Added $apply and $compile to the html so that dynamic content can be injected.
I think the easiest way to do such thing is to resolve the routes later, you could ask the routes via json, for example. Check out that I make a factory out of the $routeProvider during config phase, via $provide, so I can keep using the $routeProvider object in the run phase, and even in controllers.
'use strict';
angular.module('myapp', []).config(function($provide, $routeProvider) {
$provide.factory('$routeProvider', function () {
return $routeProvider;
});
}).run(function($routeProvider, $http) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
}).otherwise({
redirectTo: '/'
});
$http.get('/dynamic-routes.json').success(function(data) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
});
// you might need to call $route.reload() if the route changed
$route.reload();
});
});
In the $routeProvider URI patters, you can specify variable parameters, like so: $routeProvider.when('/page/:pageNumber' ... , and access it in your controller via $routeParams.
There is a good example at the end of the $route page: http://docs.angularjs.org/api/ng.$route
EDIT (for the edited question):
The routing system is unfortunately very limited - there is a lot of discussion on this topic, and some solutions have been proposed, namely via creating multiple named views, etc.. But right now, the ngView directive serves only ONE view per route, on a one-to-one basis. You can go about this in multiple ways - the simpler one would be to use the view's template as a loader, with a <ng-include src="myTemplateUrl"></ng-include> tag in it ($scope.myTemplateUrl would be created in the controller).
I use a more complex (but cleaner, for larger and more complicated problems) solution, basically skipping the $route service altogether, that is detailed here:
http://www.bennadel.com/blog/2420-Mapping-AngularJS-Routes-Onto-URL-Parameters-And-Client-Side-Events.htm
Not sure why this works but dynamic (or wildcard if you prefer) routes are possible in angular 1.2.0-rc.2...
http://code.angularjs.org/1.2.0-rc.2/angular.min.js
http://code.angularjs.org/1.2.0-rc.2/angular-route.min.js
angular.module('yadda', [
'ngRoute'
]).
config(function ($routeProvider, $locationProvider) {
$routeProvider.
when('/:a', {
template: '<div ng-include="templateUrl">Loading...</div>',
controller: 'DynamicController'
}).
controller('DynamicController', function ($scope, $routeParams) {
console.log($routeParams);
$scope.templateUrl = 'partials/' + $routeParams.a;
}).
example.com/foo -> loads "foo" partial
example.com/bar-> loads "bar" partial
No need for any adjustments in the ng-view. The '/:a' case is the only variable I have found that will acheive this.. '/:foo' does not work unless your partials are all foo1, foo2, etc... '/:a' works with any partial name.
All values fire the dynamic controller - so there is no "otherwise" but, I think it is what you're looking for in a dynamic or wildcard routing scenario..
As of AngularJS 1.1.3, you can now do exactly what you want using the new catch-all parameter.
https://github.com/angular/angular.js/commit/7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5
From the commit:
This allows routeProvider to accept parameters that matches
substrings even when they contain slashes if they are prefixed
with an asterisk instead of a colon.
For example, routes like edit/color/:color/largecode/*largecode
will match with something like this
http://appdomain.com/edit/color/brown/largecode/code/with/slashs.
I have tested it out myself (using 1.1.5) and it works great. Just keep in mind that each new URL will reload your controller, so to keep any kind of state, you may need to use a custom service.
Here is another solution that works good.
(function() {
'use strict';
angular.module('cms').config(route);
route.$inject = ['$routeProvider'];
function route($routeProvider) {
$routeProvider
.when('/:section', {
templateUrl: buildPath
})
.when('/:section/:page', {
templateUrl: buildPath
})
.when('/:section/:page/:task', {
templateUrl: buildPath
});
}
function buildPath(path) {
var layout = 'layout';
angular.forEach(path, function(value) {
value = value.charAt(0).toUpperCase() + value.substring(1);
layout += value;
});
layout += '.tpl';
return 'client/app/layouts/' + layout;
}
})();