Access parent controller from the child state - angularjs

I have a controller in AngluarJS defined like so:
'use strict';
var app = angular.module('app');
app.controller('AppCtrl', ['sth',
function (sth) {
this.inverse = false;
}
]);
here is routes deffinition:
$stateProvider.
state('app', {
abstract: true,
templateUrl: 'app/views/layout.html',
controller: 'AppCtrl',
controllerAs: 'app',
resolve: {}
}).
state('app.settings', {
abstract: true,
url: '/settings',
template: '<ui-view/>',
onEnter: function () {
}
});
How to access inverse variable from AppCtrl in app.settings route?

If you want to share data between 2 controllers, a service/factory is your best bet. Below is a an example of how you would do it. I have written it free-hand, so there may be syntax errors, but you get the idea.
app.controller('AppCtrl', ['sth', 'SharedData',
function (sth, SharedData) {
SharedData.inverse = false;
}
]);
app.factory('SharedDate', function() {
var data = {
inverse: true // or whatever default value you want to set
};
return data;
})
Now, you can inject SharedData factory in any controller, where you want to use that data.

Related

How can I get ui-router resolve to work when using ng-controller?

Is it possible to get resolves working when using ng-controller? I prefer to use ng-controller as it allows me access to all 1.6+ life-cycle hooks such as $onDestroy, which I loose when defining the controller on state obj.
Plunker:
https://plnkr.co/edit/2FJ0dGtFQtBtcQ0uVbTi?p=preview
In the example below, the view loaded in 'main' makes the myData available to inject, however in main2 the controller is defined with ng-controller and myData is no longer available to inject.
$stateProvider.state('home', {
url: '/',
views: {
'main': {
controller: 'MainCtrl',
controllerAs: 'vm',
templateUrl: 'main.html'
},
'main2': {
templateUrl: 'main2.html'
}
},
resolve: {
myData: function() {
return ['My', 'resolve', 'is', 'working'];
}
}
});
Instantiated with ui-router state:
app.controller('MainCtrl', function(myData) {
console.log("MainCtrl")
console.log(myData);
console.log(this);
this.message = myData.join(' ');
});
Instatiated with ng-controller:
app.controller('MainCtrl2', function($scope) {
console.log("MainCtrl2");
console.log($scope);
this.message = $scope.$resolve.myData.join(' ');
});
The DEMO on PLNKR.

Is it possible to mix injected dependencies with custom arguments in AngularJS?

I'm using UI Bootstrap's $uibModal to create a modal. I'm also using UI Router 0.2.15, so what I want is a state opening in a new modal.
This is what I have in my config function:
$stateProvider
.state("mystate.substate1", {
url: '...',
template: '<div ui-view></div>',
onEnter: showFirstCustomModal
})
.state("mystate.substate2", {
url: '...',
onEnter: showSecondCustomModal
});
// End of calling code
function showFirstCustomModal($uibModal) {
var options = {
backdrop: 'static',
templateUrl: '...',
controller: 'Controller1',
controllerAs: 'controller'
};
$uibModal.open(options);
}
function showSecondCustomModal($uibModal) {
var options = {
backdrop: 'static',
templateUrl: '...',
controller: 'Controller2',
};
$uibModal.open(options);
}
The two modal methods above are very similar. I would like to replace them with a generic method:
$stateProvider
.state("mystate.substate1", {
url: '...',
onEnter: showGenericModal('some_template','SomeController1', 'alias1')
})
.state("mystate.substate2", {
url: '...',
onEnter: showGenericModal('some_other_template', 'SomeController2')
});
// End of calling code
function showGenericModal(templateUrl, controller, controllerAlias, $uibModal) {
var options = {
backdrop: 'static',
templateUrl: templateUrl,
controller: controller
};
if(!!controllerAlias) {
options.controllerAs: controllerAlias;
}
$uibModal.open(options);
}
I put the $uibModal as the last argument to avoid it getting reassigned. But I can't get this to work. The error I get is
Cannot read property 'open' of undefined
Also, I've been reading this and I know that you'll have to use the $injector in order to allow your service to be injected. But I supposed that's already handled by UI-Bootstrap.
Since $stateProvider is defined in config block, $uibModal can't be passed from there as a reference.
It is not possible to mix dependencies and normal arguments in Angular DI. For onEnter it should be a function that accepts the list of dependencies.
The code above translates to:
onEnter: showGenericModal('some_other_template', 'SomeController2')
...
function showGenericModal(templateUrl, controller, controllerAlias) {
return ['$uibModal', function ($uibModal) {
...
$uibModal.open(options);
}];
}
Or a better approach:
onEnter: function (genericModal) {
genericModal.show('some_other_template', 'SomeController2');
}
...
app.service('genericModal', function ($uibModal) {
this.show = function (templateUrl, controller, controllerAlias) {
...
$uibModal.open(options);
}
});
#estus answer correct, I don't know how I didn't saw the state: "For onEnter it should be a function that accepts the list of dependencies.".
However, I will let my answer here to provide another perspective. You can define a service to wrap up and organize correctly your code, in order to call a customized modal on onEnter state event:
angular.module('app').service('AppModals', AppModals);
// or use /** #ngInject */ aswell
AppModals.$inject = ['$uibModal'];
function AppModals($uibModal) {
this.open = function _generateModal(options) {
var defaultOptions = {
backdrop: 'static'
// Any other default option
};
return $uibModal.open(angular.extend({}, defaultOptions, options);
};
}
On the state definition:
$stateProvider
.state('app.state', {
url: '/state-modal',
template: '<ui-view></ui-view>',
controller: 'DummyCtrl',
controllerAs: 'dummy',
onEnter: appState_onEnter
});
// or use /** #ngInject */ aswell
appState_onEnter.$inject = ['$uibModal'];
function appState_onEnter(AppModals) {
AppModals.open({
templateUrl: 'modals/state-modal.html',
controller: 'DummyCtrl',
controllerAs: 'dummy'
});
}

How can I pass an existing controller to a state with specific args for injecting if I need to pass it like a string, not a function?

I've implemented my controller like this:
angular
.module(appSettings.appName)
.controller('someCtrl', function (objectFromResolve) {
var vm = this;
vm.serviceName = objectFromResolve;
})
And in another file I have the state implementations:
angular
.module(appSettings.appName)
.config(function ($stateProvider, $locationProvider) {
$urlRouterProvider.otherwise(appSettings.stateNames.menu);
$stateProvider.state(appSettings.stateNames.root, {
abstract: true,
resolve: {
objectFromResolve: function () {
return {};
}
},
templateUrl: './some_url.html'
}
);
$stateProvider
.state(appSettings.stateNames.menu, {
url: '/menu',
parent: appSettings.stateNames.root,
templateUrl: './some_url2.html',
controller: 'someCtrl(objectFromResolve)',
controllerAs: 'vm'
});
});
How can I pass the controller like 'someCtrl(objectFromResolve)' in order To invoke it not like a function, but a string and with args from the resolve object ???

stateParams vs $stateParams with ui-router?

I am confused. For a long time now I have been using stateParams as a means of find out the stateParams inside a templateUrl.
Now I tried to do the same in a resolve and it does not work. In fact nothing happens when I use stateParams.
However by chance I found that I can use $stateParams in the resolve and it works.
Can someone tell me what is the difference and why do I need to use stateParams in the templateUrl and $stateParams in the resolve?
var auth = {
name: 'auth',
url: '/Auth/:content',
templateUrl: function (stateParams) {
var page = 'app/auth/partials/' + stateParams.content + '.html';
return page;
},
controller: function ($scope, authService) {
$scope.aus = authService;
},
resolve:
{
init: function ($stateParams) {
var x = 99;
return true;
}
}
};
I've created working example here, showing that $statePrams are accessible in the resolve
// States
$stateProvider
.state('auth', {
url: "/auth/:content",
templateUrl: 'tpl.html',
controller: 'AuthCtrl',
resolve : {
init : ['$stateParams' , function($stateParams){
return { resolved: true, content: $stateParams.content };
}]
}
})
Controller
.controller('AuthCtrl', ['$scope', 'init', function ($scope, init) {
$scope.init = init;
}])
and this could be the calls
auth/8
auth/xyz
Check it here

How to map a segment of a route to routeParams?

Lets say I have two routes defined, served by the same view and controller, such as
/customers/:cutomerId/edit
/customers/add
in a controller I need to determine in which "mode" is view in. How do I map "edit" and "add" segments of the route so that it appears in $routeParams. Is there a way?
You may user route's resolves to solve it.
For example:
myApp.config(function($routeProvider) {
$routeProvider
.when('/customers/:cutomerId/edit', {
templateUrl: 'myView.html',
controller: 'MyController',
resolve: {
mode: function() {
return 'edit';
}
}
})
.when('/customers/add', {
templateUrl: 'myView.html',
controller: 'MyController',
resolve: {
mode: function() {
return 'add';
}
}
});
myApp.controller('MyController', function($scope, mode){
// Now controller knows it's mode.
});

Resources