Separate common parts of controller into a common controller - angularjs

I'm developing an web app using AngularJS with uiRouter. I developed my route configuration as follows:
(function () {
'use strict';
var module = angular.module('app', ['ngMaterial', 'ui.router']);
function Config($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider.state('Home', {
url: '/',
templateUrl: 'Partials/homeview.html',
controller: 'homeCtrl'
}).state('Table', {
url: '/tableview',
templateUrl: 'Partials/tableview.html',
controller: 'tableCtrl'
}).state('List', {
url: '/listview',
templateUrl: 'Partials/listview.html',
controller: 'listCtrl'
}).state('New', {
url: '/new',
templateUrl: 'Partials/new.html',
controller: 'newCtrl'
}).state('Edit', {
url: '/edit/:index',
templateUrl: 'Partials/edit.html',
controller: 'editCtrl'
});
}
Config.$inject = ["$urlRouterProvider", "$stateProvider"];
module.config(Config);
}());
The thing in some controller passed to the view the code is duplicated, so I would like to know if there is a way to pass 2 controllers to the view at the same time or if there is a way to create a separate file with that specific part of the duplicated controller and pass it as Dependency Injection in the desired controllers.

You can't have two controllers linked to a uiRouter route. But you could certainly make a service or factory that includes your universal functionality. (See angular.service vs angular.factory for more research.)
var app = angular.module('app',[])
app.service('myFunctions',function() {
this.addNumbers = function(a,b) {
// calculate some stuff
return a+b;
}
}
app.controller('myController',function(myFunctions){
myFunctions.addNumbers(2,2); // 4
})

Related

Modularize AngularJS App Using UI-Router

I'm trying to modularize a large AngularJS app (1.4.8) that is using UI-Router.
The current structure is like this:
transactionApp.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function ($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('root', {
url: '/transaction',
views: {
'header': {
templateUrl: '/Partials/Shells/Header/header-partial.html',
controller: 'headerController',
resolve: {
headerItems: ['$http', function ($http) {
var data = {};
data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();
if (dataForLayout.IsLoggedIn) {
return $http({
method: 'POST',
url: '/myaccount/GetHeaderAccountInfo',
data: data
});
}
}]
}
},
'footer': {
templateUrl: '/Partials/Shells/Footer/footer-partial.html',
controller: 'footerController'
},
'mainPanel': {
templateUrl: '/Partials/Shells/Transaction/transaction-partial.html',
controller: 'mainPanelController'
},
'successPanel': {
templateUrl: '/Partials/Shells/Transaction/transaction-success-panel.html',
controller: 'successPanelController'
}
}
});
$urlRouterProvider.otherwise('/transaction');
// Enable HTML5Mode for #-urless
$locationProvider.html5Mode(true);
}]);
The part I want to modularize is "mainPanel." The mainPanel consists of step 1, step 2, step 3, and the final review/submit page. I'd also like to separate pop-up modals if possible/beneficial. The steps, review page, and modals are dependent on each other.
The URL has to stay "/transaction" no matter which step the user is in.
What I'm envisioning is having the parent as "mainPanel" that connects the sub modules and having sub modules that are "step 1," step 2," and so on.
I've read some articles, but can't seem to find one that talks about further modularizing a module.

How to setup a parent/child with a resolve and nested states in ui-router?

I'm trying to refactor our current layout to add in a dynamic show/hide section above the header on our page. We are using Angularjs 1.4.2 with ui-router and currently we are using separate route files, although we have a main route. The main.html section of the screen, up to now, was the only section with the dynamic content. In order to get my new route working, I'm having to add it and main to each of the existing routes.
My question is, would a better design be something along this line of a single parent route to handle a resolve, with nested states for the dynamic main content and a new view for my new route and content? As the application grows, do you still continue to put the new routes into the parent route or is there a better way to organize it like we were doing with individual route files or a combination of both?
This is what I'm trying, but it's not working yet, as I'm getting a blank page, but the design for future growth with the application is what I'm trying to get right here:
(function () {
'use strict';
angular
.module('myApp')
.config(config);
config.$inject = ['$stateProvider'];
/* #ngInject */
function config($stateProvider) {
$stateProvider
.state('root', {
template: '<ui-view></ui-view>',
abstract: true,
resolve:{
myLoader: ['myLoader', function(myLoader){
// $http returns a promise for the url data
return myLoader.load();
}]
}
}
.state('main'), {
url: '/?param1&param2&param3&param4',
templateUrl: 'app/routes/main/main.html',
controller: 'MainCtrl',
redirectTo: 'main'
})
$stateProvider
.state('basicsearch', {
url: '/basic-search',
templateUrl: 'app/routes/basicsearch/basicsearch.html',
controller: 'searchQuickCtrl'
})
$stateProvider
.state('advancedsearch', {
url: '/advaned-search',
templateUrl: 'app/routes/advancedsearch/advancedsearch.html',
controller: 'advancedSearchkCtrl'
})
$stateProvider
.state('anothersearch', {
url: '/another-search',
templateUrl: 'app/routes/anothersearch/anothersearch.html',
controller: 'anotherSearchCtrl'
})
.state('myChange', {
url: '/myChange?param5&param6&param7&param8',
views: {
"myChangeView": {
templateUrl: '/app/routes/myChange/myChange.html',
controller: 'myChangeCtrl'
}
}
});
}
})();
Here is a basic layout of what we have:
I am not sure about the issue. But I created working plunker, to show you how we can use state nesting and resolve.
So this is our parent controller for root state and the factory used in resolve
.factory('myLoader', function(){
return {
load: function () {return [1,2] }
};
})
.controller('ParenCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {
$scope.myLoader = myLoader;
}])
.controller('MainCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('searchQuickCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('advancedSearchkCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('anotherSearchCtrl', ['$scope', function ($scope) {}])
Next, we will use controller for abstract root state, to assign result into scope:
.state('root', {
template: '<div ui-view></div>',
abstract: true,
controller: 'ParenCtrl',
resolve:{
myLoader: ['myLoader', function(myLoader){
// $http returns a promise for the url data
return myLoader.load();
}]
}
})
And all states will use that as their parent, and inherit the scope:
.state('main', {
parent: 'root',
url: '/?param1&param2&param3&param4',
templateUrl: 'app/routes/main/main.html',
controller: 'MainCtrl',
//redirectTo: 'main'
})
.state('basicsearch', {
url: '/basic-search',
templateUrl: 'app/routes/basicsearch/basicsearch.html',
parent: 'root',
controller: 'searchQuickCtrl'
})
.state('advancedsearch', {
url: '/advaned-search',
templateUrl: 'app/routes/advancedsearch/advancedsearch.html',
parent: 'root',
controller: 'advancedSearchkCtrl'
})
.state('anothersearch', {
url: '/another-search',
parent: 'root',
templateUrl: 'app/routes/anothersearch/anothersearch.html',
controller: 'anotherSearchCtrl'
})
And properly use it inside of this view:
<div >
<h3>current state name: <var>{{$state.current.name}}</var></h3>
<h4>resolved in parent</h4>
<pre>{{myLoader | json }}</pre>
<h5>params</h5>
<pre>{{$stateParams | json}}</pre>
<h5>state</h5>
<pre>{{$state.current | json}}</pre>
</div>
while all these controller do not use that resolved value
.controller('MainCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('searchQuickCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('advancedSearchkCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('anotherSearchCtrl', ['$scope', function ($scope) {}])
So, each controller can be provided with stuff resolved in parent (as parameter). That is shown above. But also, becuase parent already used that and assigned that to some $scope variable... all is alraeady in place.
Also check:
Angularjs ui-router abstract state with resolve
How do I share $scope data between states in angularjs ui-router?

How can we switch between two modules in angularjs

I have two modules in my application. The requirement is that I need to navigate from one module to another. Is it possible?
The two modules are sol user and sol cutomer are 2 different module. This way it is not working? Am i doing something wrong? Any other alternative please
The code which i am using is given below but it is not displaying anything
'use strict';
(function () {
angular.module('sol.user')
.config(StateConfig);
StateConfig.$inject = ['$stateProvider', '$urlRouterProvider'];
alert("user-state");
function StateConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('/customer/search', '/user');
$stateProvider
.state('home.user',{
url:'/user',
controller:'sol.user.UserController',
controllerAs: 'userCtrl'
})
.state('home.search', {
url: '/customer/search',
templateUrl: 'views/customer/search/search.html',
controller: 'sol.customer.search.SearchCtrl',
controllerAs: 'searchCtrl'
})
}
})();

Controlling multiple views in one controller in AngularJS

Following the MVC pattern, one controller should be able to handle multiple views in AngularJS.
E.g. I have one view for showing all users and one for creating a new user. My $routeProvider (excerpt) looks like this:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/showusers', {
templateUrl: 'partials/showusers.html',
controller: 'userController'
}).
when('/createuser', {
templateUrl: 'partials/showusers.html',
controller: 'userController'
})
}]);
Both views share several methods such as retrieving all user attributes. These shared methods are accessed through a factory.
The userController (excerpt) currently looks like this where Users is a factory:
angular.module('supportModule').
controller('userController', function($scope, Users) {
var init = function(){
$scope.users = Users.getAll();
$scope.attributes = Users.getAttributes();
}
init();
})
The problem is: I don't need to request Users.getAll(); when I'm on the createuser view.
Of course, I could easily parse the route by using $location, the $routeProvider or pure JS and then conditonally call methods - but I figure there is a more elegant and efficient way in Angular :)
Something like in typical MVC frameworks where one controller has many actions - one for each view.
So my question:
How to elegantly call methods based on the view in a controller which controls more than one view?
You could use resolve: when setup $routeProvider to pass values to a controller depending on the matched route.
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/showusers', {
templateUrl: 'partials/showusers.html',
controller: 'userController',
resolve: {
ctrlOptions: function () {
return {
getAllUsers: true,
};
}
}
}).
when('/createuser', {
templateUrl: 'partials/showusers.html',
controller: 'userController',
resolve: {
ctrlOptions: function () {
return {
getAllUsers: false,
};
}
}
})
}]);
and then in the controller, you can inject item specified in resolve: like this:
app.controller('userController', function ($scope, Users, ctrlOptions) {
var init = function () {
if (ctrlOptions.getAllUsers) {
$scope.users = Users.getAll();
}
$scope.attributes = Users.getAttributes();
};
init();
});
Hope this helps.

Dynamically switch ng-include between controllers

I have the following bit of code for my navigation that I want to update dynamically between pages.
<nav ng-include="menuPath"></nav>
Here is my app and routing set up
var rxApp = angular.module('ehrxApp', ['ngRoute']);
// configure our routes
rxApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'mainController',
templateUrl: '/content/views/index.html'
})
.when('/census', {
templateUrl: '/content/views/admission/census.html',
controller: 'censusController'
})
.when('/messages', {
templateUrl: '/content/views/account/messages.html',
controller: 'messagesController'
})
.when('/profile', {
templateUrl: '/content/views/account/profile.html',
controller: 'profileController'
})
});
In my main controller I set the menuPath value here:
rxApp.controller('mainController', function (userService, $scope, $http) {
evaluate_size();
$scope.menuPath = "/content/views/index.menu.html";
});
rxApp.controller('censusController', function ($scope, $http, $sce, censusService) {
$scope.menuPath = "/content/views/admission/census.menu.html";
evaluate_size();
});
When the page switches to the census view it should change the menu. What happens though is the first page loads the main menu, then no matter what other page you go to the menu never updates.
I imagine this problem has something to do with a primitive values and prototypical inheritance between child scopes, but would need to see more of your html to determine that. Without that, I propose an alternative way that may solve your problem and keep the config all in one place.
$routeProvider will accept variables and keep them on the route, even if angular doesn't use them. so we modify your routing by including the menuPath like so:
var rxApp = angular.module('ehrxApp', ['ngRoute']);
// configure our routes
rxApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'mainController',
templateUrl: '/content/views/index.html',
menuPath: '/content/views/index.menu.html'
})
.when('/census', {
templateUrl: '/content/views/admission/census.html',
controller: 'censusController',
menuPath: '/content/views/admission/census.menu.html'
})
.when('/messages', {
templateUrl: '/content/views/account/messages.html',
controller: 'messagesController'
})
.when('/profile', {
templateUrl: '/content/views/account/profile.html',
controller: 'profileController'
})
});
Remove setting $scope.menuPath from each controller, then finally add a watch on rootScope that will change the menuPath on $routeChangeSuccess
rxApp.run(['$rootScope', function ($rootScope) {
$rootScope.$on('$routeChangeSuccess', function(event, current) {
if (current && current.$$route && current.$$route.menuPath) {
$rootScope.menuPath = current.$$route.menuPath;
} else {
$rootScope.menuPath = '';
}
});
}]);

Resources