Invoke function between two controllers while using routeprovider - angularjs

I am using route provider as follows,
var appModule = angular.module('ngLogin', ['ngRoute','restangular','btford.socket-io','ngSanitize','xeditable']);
appModule.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/home', {
templateUrl: 'sample/homepage.html',
controller: 'ngHomeControl'
}).
when('/contacts', {
templateUrl: 'sample/homepage.html',
controller: 'ngContactControl'
});
}]);
Here I need to call function from ngHomeControl to ngContactControl.
I tried as follows, but the function didn't invoked.
appModule.controller('ngHomeControl', function($scope,$routeParams,socket,Restangular,$http) {
$rootScope.$broadcast('getFriendList',{"userName":userName});
});
appModule.controller('ngContactControl', function($scope,$routeParams,$rootScope,socket,sharedProperties,Restangular,$http,$timeout) {
$scope.$on("getFriendList",function(event,data)
{
console.log('getFriendList');
});
});
Can anyone help me to resolve?

This will not work as only one controller is instantiated at a time (in your case).
A proper way would be to use a service. There is a nice article that wil help you with this.
See also this answer on how to create a service.
Based on those two resources you should came up with something similar to this:
var appModule = angular.module('appModule', ['ngRoute']);
appModule.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/home', {
templateUrl: 'home.html',
controller: 'ngHomeControl'
}).
when('/contacts', {
templateUrl: 'contacts.html',
controller: 'ngContactControl'
});
}]);
appModule.service('friendsService', function(){
this.getFriendList = function () {
return ['John', 'James', 'Jake'];
}
});
appModule.controller('ngHomeControl', function($scope, friendsService) {
$scope.homeFriends = friendsService.getFriendList();
});
appModule.controller('ngContactControl', function($scope, friendsService) {
$scope.contactFriends = friendsService.getFriendList();
});
There is a complete working JSFiddle so you can test it out.
Please also checkout the console output to see when used components are instantiated.
You will see that controllers are instantiated each time the route changes - they are instantiated implicitly via the ngController directive used inside templates. The service is instantiated only once and this is at the time when it is needed/injected for the first time.

The reason your event listener isn't fired is because there isn't a ngContactControl instance alive when your at /home. You can create a parent controller, which handles the scope events but a better way is to use a service that is shared among the controllers that need this functionality.
See this plunker for an example how to share data and/or functions via a service.

Related

How to redirect views using the same angularjs controller?

When i am using "location.href" in order to redirect to another view my current controller reinitialize. The both views are using the same controller. What is the better way to go between views but not recreating controller?
function onAddNewTest() {
//some logic here
location.href = '/#/testList';
};
route itself:
.module('applicationModule', ['ngRoute'])
.config(function ($routeProvider) {
$routeProvider
.when('/testList', {
controller: 'testController as vm',
templateUrl: '/Scripts/views/testList.html'
})
Try to use $location.path() in your controller like this:
function onAddNewTest() {
//some logic here
$location.path('/testList');
};
And don't forget to add dependency to your controller.
Hope it helps

Argument 'mainController' is not a function, got undefined 1.4

There are a ton of examples of using the newer angular directives like ng-blur, ng-focus, form validation, etc. They all work great in a single page, or in plinkr, jsfiddle, etc. with the exception of the people who try to define the function on the global namespace, that mistake is WELL documented.
However, I was having a different problem.
I was using an example from Scotch.io. This one works great...until you introduce it into an SPA that is using angular-route :(
After many hours of fighting with the error 'Argument 'mainController' is not a function, got undefined', I found the answer in a comment from Hajder Rabiee.Thanks Hadjer, Love you man!
Hajder left this comment and in it, he says:
If you're using routes (high probability) and your config has a reference to a controller in a module that's not declared as dependency then initialisation might fail too.
E.g assuming you've configured ngRoute for your app, like
angular.module('yourModule',['ngRoute'])
.config(function($routeProvider, $httpProvider) { ... });
Be careful in the block that declares the routes,
.when('/resourcePath', {
templateUrl: 'resource.html',
controller: 'secondModuleController' //lives in secondModule
});
Declare secondModule as a dependency after 'ngRoute' should resolve the issue. I know I had this problem.
Even with this help it took me a minute to get it working, so I thought I would share my sample code here to help the next poor bastard that gets stuck on this.
First, in the place where i declare my routes:
var app = angular.module('sporkApp', ['ngRoute','validationApp']);
app.config(function ($routeProvider) {
$routeProvider
.when('/home',
{
controller: 'HomeController',
templateUrl: 'home/home.template.html'
})
.when('/tags',
{
controller: 'TagsController',
templateUrl: 'tags/tags.template.html'
})
.when('/test',
{
controller: 'mainController',
templateUrl: 'test/test.template.html'
})
.otherwise({ redirectTo: '/home' });
});
Then, you need to add your controller code somewhere, where it will get loaded in your shell page:
// create angular app
var validationApp = angular.module('validationApp', []);
// create angular controller
validationApp.controller('mainController', function($scope) {
// function to submit the form after all validation has occurred
$scope.submitForm = function() {
// check to make sure the form is completely valid
if ($scope.userForm.$valid) {
alert('our form is amazing');
}
};
});
Finally, you need to add the corresponding ng-app and ng-controller to some page element that wraps the controls you want to validate. I put the following inside of a div tag:
<div ng-app="validationApp" ng-controller="mainController">

Prevent $routeParams from Bleeding Through Codebase

I am using the Angular $routeProvider service to wire-up my single-page HTML5 applciation. I am using the following routing configuration:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/show-order/:orderId', {
templateUrl: 'templates/order.html',
controller: 'ShowOrdersController'
});
}]);
Within the ShowOrdersController I need access to the RESTful URL parameter described above as :orderId. It is suggested that to best achieve this, I should use the $routeParams service in my controller:
app.controller('ShowOrderController', function($scope, $routeParams) {
$scope.order_id = $routeParams.orderId;
});
I have serious concerns about this. My routing logic has now bled through to my controller! If I want to drastically change the routing scheme, I would have to go through all my controller code and correct all the references to $routeParams.
Furthermore, if I want to re-use the ShowOrderController for multiple routes, it's going to enforce all of the routes to use the same token variable :orderId.
This just seems like poor coding to me. It would make more sense to provide some linking mechanism, so the router can specify well-known parameters to the controller.
This would be just like how a modal's resolve method works:
$modal.open({
controller: 'ShowOrderController',
resolve: {
orderId: function () {
return $routeParams.orderId;
}
}
});
app.controller("ShowOrderController", ["orderId", function (orderId, $scope) {
$scope.orderId = orderId;
}]);
Is there any way to achieve this or something similar with the out-of-the-box AngularJS routing services?
As per AngularJS - How to pass up to date $routeParams to resolve? it is possible to reference the current route's parameters in the resolve method of the $routeProvider using $route.current.params:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/show-order/:orderId', {
templateUrl: 'templates/order.html',
controller: 'ShowOrdersController',
resolve: {
orderId: function( $route ) {
return $route.current.params.orderId;
}
}
});
}]);
This will then honour the suggestion above, that the controller can declaratively specify its parameters:
app.controller("ShowOrderController", ["orderId", function (orderId, $scope) {
$scope.orderId = orderId;
}]);
In conjunction, this effectively decouples the controller from the route's parameters.

how to execute the same init code on each controller instantiated by $routeProvider?

In my .config I have a router that instantiate a pair controller-router:
angular.module('reporting', ['ng', 'ngRoute', 'ngResource', 'reporting.directives', 'reporting.controllers', 'reporting.config', 'ngGrid', 'ui.bootstrap'])
.config(["$routeProvider", "$provide", function ($routeProvider, $provide) {
$routeProvider
.when('/dealersReq', {
templateUrl: 'reporting/partials/dealersReqs.html',
controller: 'DealersCtrl'
})
.when('/lmtReq', {
templateUrl: 'reporting/partials/lmt.html',
controller: 'lmtCtrl'
})
.when('/leadsCreated', {
templateUrl: 'reporting/partials/leadsCreated.html',
controller: 'LeadsCreatedCtrl'
})
...
but each controller share the same initialization code (think about it like a constructor) that sets in the rootScope some variable like a title and other useful information for some controllers outside the <view>:
.controller('DealersCtrl', ['$scope','$rootScope', 'CONFIG',
function($scope, $rootScope, CONFIG) {
//////////// duplicated code
var key = 'qtsldsCrtSncheQ';
$rootScope.openReport.key = key;
$rootScope.openReport.title = CONFIG.reports['' + key].title;
//////////// duplicated code
console.log('Initialized! Now I do what a controller should really do');
}]);
What I would like to do is finding a way to move that code - which is duplicated into every controller at the moment - into something smarter and neater. Soemthing that the route can call during the routing instanciation for example. Of course each controller should have a different key, but that one could be exactly the controller name actually. I really don't know how to improve this. Any suggestion?
Why don't create a method on the $rootScope which does that, and then call it from each controller, i.e.: $rootScope.init().
You could use a Service for shared code but you should avoid to use $rootScope
https://stackoverflow.com/a/16739309/3068081

AngularJS $location not updated properly when using $routeProvider

I have an Angular JS application with a defaultController which controls the header of the app. Then I have some other controllers one for each view. The views are loaded in the <main>. I load the views using the $routeProvider with this code:
myapp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/login', {
templateUrl: 'templates/login.html',
controller: 'loginController'
}).
when('/dashboard', {
templateUrl: 'templates/dashboard.html',
controller: 'dashboardController'
}).
...
I am trying to display a LOGOUT button inside the header when the loaded view is the dashboard and hide it if the loaded view is the login view. In order to do that I have on the defaultController the $location object and I properly add and remove classes from the LOGOUT button with ng-class.
There is only one problem: $location gives me the correct path the first time I load the page, but after I change the view (changed by the $routeProvider) that variable is not updated anymore, so when I am actually on /#/dashboard , the $location.url is still on /login. Here the controller code:
controllers.controller('defaultController', ['$scope', 'ipCookie', '$location', function($scope, ipCookie, $location) {
$scope.url = $location.url();
...
I also tried with $window.location.hash with the same result.
Any idea?
EDIT: after the accepted answer this is what I ve added on the defaultController in order to make it work
$scope.$on("$locationChangeSuccess", function() {
$scope.url = $location.url();
});
The location is probably updated in the service after your default controller is loaded.
You can either inject the $location service into the scope and make decisions in your template based on it (then it will automatically be watched and re-evaluated) or you could listen for the $locationChangeSuccess event.
When injecting, you can simply $scope.location = $location and then use something like <a ng-hide="location.path() != '/something'">.
$location broadcasts the $locationChangeSuccess on the root scope, so you should be able to listen for it on whichever scope you have available: $scope.$on( "$locationChangeSuccess", function() { /* do something */ } );

Resources