I got a main.html file, inside that I got a section that is visible/hidden depending on the state of a $scope.parameter. This main.html is used in two of my routes, one is the 'main' other is a sub-route. The look like this,
.state('medications', {
url: '/medications',
templateUrl: '/partials/home.html',
controller: 'mainController',
resolve: {
postPromise: ['medicationservice', function(medicationservice) {
return medicationservice.getAll();
}]
}
})
.state('medications.add', {
url: '/add',
templateUrl: '/partials/home.html',
controller: 'mainController'
})
So what I would like to do in this case is set that $scope.parameter to show this section. I have seen this examples but I don't get how I set what controller it should use.
$stateProvider.state('contacts', {
template: '<h1>{{title}}</h1>',
controller: function($scope){
$scope.title = 'My Contacts';
}
})
How do I set a parameter with explcitly setting the controller name?
Option 1:
Make it a stateParams (recommended):
.state('medications', {
params: {showSection: false},
//...
.state('medications.add', {
params: {showSection: true},
//...
And set your $scope variable in mainController to that $stateParam:
$scope.$watch($stateParams.showSection, function(){
$scope.parameter = $stateParams.showSection;
}, true);
Option 2:
Watch the $stateChangeStart event and change your $scope variable according to toState:
$scope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
$scope.parameter = toState === 'medications' ? false : true;
})
Use the 'data' object of $state:
.state('medications.add', {
url: '/add',
templateUrl: '/partials/home.html',
controller: 'mainController',
data: { myParameter: 'true' }
})
Then, in the controller, you can access the data using the $state service. Assign the data to the scope of the controller and you're good to go:
angular.module('myapp').controller('myController',function($scope,$state){
$scope.myParameter = $state.data.myParameter;
});
Related
I have configured the following ui-router.
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return codeService.getPost($stateParams.id)
}]
}
}
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl'
})
.state('global.editor.panels', {
controller: 'PanelsCtrl',
params: { layout: 'horizontal' },
templateUrl: function (params) { return "/htmls/" + params.layout + '.html' }
}
}])
app.controller('EditorCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.layout = "horizontal";
$scope.$watch('layout', function () {
$state.go('global.editor.panels', { layout: $scope.layout });
});
}]);
As a result, https://localhost:3000/#/new in a browser leads to (the state global.editor, then to) the state global.editor.panels.
Now, I want to add a parameter connected:
I don't want it to be shown in the url
https://localhost:3000/#/new in a browser makes connected to be false, and https://localhost:3000/#/newTRUE in a browser makes connected to be true
connected can be past into the controller EditorCtrl and PanelsCtrl
connected can be available in the resolve of global.editor; ideally, we could resolve different objects according to the value of connected.
Does anyone know how to accomplish this?
You can add resolve for new and newTRUE:
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
resolve: {
isConnected: function() {
return false;
}
},
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
resolve: {
isConnected: function() {
return true;
}
},
controller: 'EditorCtrl'
})
And in EditorCtrl (or PanelsCtrl) you can use it like:
app.controller('EditorCtrl', ['$scope', '$state', 'isConnected', function($scope, $state, isConnected) {
console.log("connected : " + isConnected); // you can save this value in Service and use it later.
...
}]);
You can use classic approach - in resolve
Or you can use hidden parameters from angular ui router.
Define params : {isConnected' : null} in your parent global state.
In:
global.newTRUE - set value in $state config
global.new - set value in $state config
global.editor.panels - set parameters in transition/go or ui-sref
definition is like this:
$stateProvider
.state('global.newTRUE', {
url : '/:newTRUE',
params : {
'isConnected' : false
}
});
}
and in controller you get in from $stateParams.
Problem with this approach is hidden parameters are loses in refresh page, except if is set default value
You can surely use the params of UI-Router states' config to not show it in URL and achieve all mentioned points.
Also, as per #2, you need connected to be false for /new and true for /newTRUE. We can do so by passing true or false as default value for those states. Something like this:
$stateProvider
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
params: { connected: null },
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return codeService.getPost($stateParams.id)
}]
}
}
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
params: { connected: false }, // default false for /new
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
params: { connected: true }, // default true for /newTRUE
controller: 'EditorCtrl'
})
.state('global.editor.panels', {
controller: 'PanelsCtrl',
params: { layout: 'horizontal', connected: null },
templateUrl: function (params) { return "/htmls/" + params.layout + '.html' }
}
For #3, In order to access connected in your controllers (EditorCtrl and PanelsCtrl) you can inject $stateParams to controller and use $stateParams.connected to get it.
For #4, (This is more or less similar to achieveing #3)
Just like you get $stateParams.id, you can have $stateParams.connected as well, which you can use to resolve different objects according to the value of connected. Something like this:
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
params: { connected: null },
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return $stateParams.connected ?
codeService.getPost($stateParams.id) :
codeService.getSomethingElse($stateParams.id)
}]
}
}
But, for that to work, make sure that you are passing connected as params when you visit global.editor state (using $state.go or ui-sref)
Hope this helps!
I have tried to implement angular js $state.go() with parameter. But the $state.go() works fine without parameter. But with parameter it didn't works. I already tried many examples but no way. I need to display the parameter in html view.
my state provider is,
$stateProvider
.state('home', {
url: '/',
views: {
'content#': {
templateUrl: 'content.html',
controller: 'dashCtrl'
}
},
params: {
obj: {
value:''
}
}
});
and controller is,
dashboard.controller('dashCtrl', function ($scope, $http, $state, $stateParams){
$state.go('dash_home', {obj: {value:'admin'}});
});
and my div is
<div>
<h1>welcome : {{value}} || {{obj.value}}</div>
what is the problem.?
You cannot use parameters directly in the HTML. You first have to add it to the scope (or virtual model if you use that), e.g.
$scope.obj = $state.current.params.obj
And you have to do it in the controller of the state that you are going to of course, not in those where you call $state.go
I found my mistakes. The correct code is,
$stateProvider
.state('home', {
url: '/',
views: {
'content#': {
templateUrl: 'content.html',
controller: 'dashCtrl'
}
},
params: {
value:''
}
});
controller is,
dashboard.controller('mycontroller',function($scope, $http, $state, $stateParams){
$scope.user=$state.params.registerData;
$scope.redirect=function()
{
$state.params.registerData='mycontent';
$state.go('dash_home', {registerData:$state.params.registerData});
}
});
thanks all.
I guess you can't use objects in params. Just replace
params: {
obj: {
value:''
}
}
to this:
params: {
value:''
}
and also: $state.go('dash_home', {obj: {value:'admin'}}); to $state.go('home', {value:'admin'});
I have this plunkr. I define two controllers: mainController and itemsController. I want to access to the scope variable items from a view, but I can't.
To reproduce, you need to click first in the button Populate Items, (the array item is populated from a service) and you see in the console the values. Then click in the button Say Hello ! and then you will see that items is empty; but, the variable tab is being accessed.
I think that I'm not applying in the correct way the definitions of the controllers.
There is updated and working plunker
What we need, is the data sharing:
How do I share $scope data between states in angularjs ui-router?
so instead of this
app.controller("itemsController",
function($rootScope, $scope, $state, $stateParams, Data) {
$scope.items = []; // local reference, not shared
$scope.getFromJson = function() {
Data.get().then(function handleResolve(resp) {
$scope.items = resp;
console.log($scope.items);
});
};
})
we will fill the shared reference $scope.Model.items = resp;
app.controller("itemsController",
function($rootScope, $scope, $state, $stateParams, Data) {
$scope.getFromJson = function() {
Data.get().then(function handleResolve(resp) {
$scope.Model.items = resp;
console.log($scope.Model.items);
});
};
})
which will be hooked on the super root home. So, instead of this:
$stateProvider
.state("home", {
abtract: true,
url: "/home",
resolve: {
$title: function() {
return 'Tab1';
}
},
views: {
"viewA": {
templateUrl: "home.html"
}
}
});
we will have that:
.state("home", {
abtract: true,
url: "/home",
resolve: {
$title: function() {
return 'Tab1';
}
},
views: {
"viewA": {
templateUrl: "home.html",
// HERE we do the magic
// this is a super parent view
// at which $scope we will have shared reference Model
controller: function($scope){
$scope.Model = {}
}
}
}
});
check it in action here
Here is what i have so far with ui-router states:
$stateProvider
.state('tools', {
url: '/tools/:tool',
template: '<div ui-view=""></div>',
abstract: true,
onEnter: function ($stateParams, $state, TOOL_TYPES) {
if (TOOL_TYPES.indexOf($stateParams.tool) === -1) {
$state.go('error');
}
}
})
.state('tools.list', {
url: '',
templateUrl: 'app/tools/tools.tpl.html',
controller: 'ToolsController'
})
.state('tools.view', {
url: '/:id/view',
templateUrl: 'app/tools/partials/tool.tpl.html',
controller: 'ToolController'
});
As you can see parent state has parameter tool which can be only in TOOL_TYPES array. So in case when tool is not available, i want to redirect to the error page.
Actually, everything works as expected, but i get two errors:
TypeError: Cannot read property '#' of null
TypeError: Cannot read property '#tools' of null
So i guess, child states have been 'hit' anyway. Is it possible to prevent this? Or maybe there is some other way to achieve what i want?
Angular ui-router's documentation mentions that onEnter callbacks gets called when a state becomes active, hence, the child states were activated.
To solve this problem you need to implement two things:
Create a resolve that returns a rejected promise once a specific condition does not apply to that state. Make sure that the rejected promise is passed with information regarding the state to redirect to.
Create a $stateChangeError event handler in the $rootScope and use the 6th parameter which is the representation of the information you have passed in the rejected promise. Use the information to create your redirect implementation.
DEMO
Javascript
angular.module('app', ['ui.router'])
.value('TOOL_TYPES', [
'tool1', 'tool2', 'tool3'
])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('error', {
url: '/error',
template: 'Error!'
})
.state('tools', {
url: '/tools/:tool',
abstract: true,
template: '<ui-view></ui-view>',
resolve: {
tool_type: function($state, $q, $stateParams, TOOL_TYPES) {
var index = TOOL_TYPES.indexOf($stateParams.tool);
if(index === -1) {
return $q.reject({
state: 'error'
});
}
return TOOL_TYPES[index];
}
}
})
.state('tools.list', {
url: '',
template: 'List of Tools',
controller: 'ToolsController'
})
.state('tools.view', {
url: '/:id/view',
template: 'Tool View',
controller: 'ToolController'
});
})
.run(function($rootScope, $state) {
$rootScope.$on('$stateChangeError', function(
event, toState, toStateParams,
fromState, fromStateParams, error) {
if(error && error.state) {
$state.go(error.state, error.params, error.options);
}
});
})
.controller('ToolsController', function() {})
.controller('ToolController', function() {});
I am trying to do what was essentially answered here Unable to open bootstrap modal window as a route
Yet my solution just will not work. I get an error
Error: [$injector:unpr] Unknown provider: $modalProvider <- $modal
My app has the ui.bootstrap module injected - here is my application config
var app = angular.module('app', ['ui.router', 'ui.bootstrap','ui.bootstrap.tpls', 'app.filters', 'app.services', 'app.directives', 'app.controllers'])
// Gets executed during the provider registrations and configuration phase. Only providers and constants can be
// injected here. This is to prevent accidental instantiation of services before they have been fully configured.
.config(['$stateProvider', '$locationProvider', function ($stateProvider, $locationProvider) {
// UI States, URL Routing & Mapping. For more info see: https://github.com/angular-ui/ui-router
// ------------------------------------------------------------------------------------------------------------
$stateProvider
.state('home', {
url: '/',
templateUrl: '/views/index',
controller: 'HomeCtrl'
})
.state('transactions', {
url: '/transactions',
templateUrl: '/views/transactions',
controller: 'TransactionsCtrl'
})
.state('login', {
url: "/login",
templateUrl: '/views/login',
controller: 'LoginCtrl'
})
.state('otherwise', {
url: '*path',
templateUrl: '/views/404',
controller: 'Error404Ctrl'
});
$locationProvider.html5Mode(true);
}])
I have reduced my controller to the following:
appControllers.controller('LoginCtrl', ['$scope', '$modal', function($scope, $modal) {
$modal.open({templateUrl:'modal.html'});
}]);
Ultimately, what I am hoping to achieve is when login is required not actually GO to the login page, but bring up a dialog.
I have also tried using the onEnter function in the ui-router state method. Couldn't get this working either.
Any ideas?
UPDATE
Ok - so as it turns out, having both ui-bootstrap.js AND ui-bootstrap-tpls breaks this - After reading the docs I thought you needed the templates to work WITH the ui-bootstrap. though it seems all the plunkers only load in the ..tpls file - once I removed the ui-bootstrap file my modal works...Am i blind? or doesn't it not really say which one you need in the docs on github? -
Now i just need to figure out how to prevent my url from actually going to /login, rather than just show the modal :)
update 2
Ok, so by calling $state.go('login') in a service does this for me.
Hi I had a hard time getting through the similar problem.
However, I was able to resolve it.
This is what you would probably need.
app.config(function($stateProvider) {
$stateProvider.state("managerState", {
url: "/ManagerRecord",
controller: "myController",
templateUrl: 'index.html'
})
.state("employeeState", {
url: "empRecords",
parent: "managerState",
params: {
empId: 0
},
onEnter: [
"$modal",
function($modal) {
$modal.open({
controller: "EmpDetailsController",
controllerAs: "empDetails",
templateUrl: 'empDetails.html',
size: 'sm'
}).result.finally(function() {
$stateProvider.go('^');
});
}
]
});
});
Click here for plunker. Hope it helps.
I'm working on something similar and this is my solution.
HTML code
<a ui-sref="home.modal({path: 'login'})" class="btn btn-default" ng-click="openModal()">Login</a>
State configuration
$stateProvider
// assuming we want to open the modal on home page
.state('home', {
url: '/',
templateUrl: '/views/index',
controller: 'HomeCtrl'
})
// create a nested state
.state('home.modal', {
url: ':path/'
});
Home controller
//... other code
$scope.openModal = function(){
$modal.open({
templateUrl: 'path/to/page.html',
resolve: {
newPath: function(){
return 'home'
},
oldPath: function(){
return 'home.modal'
}
},
controller: 'ModalInstanceController'
});
};
//... other code
Finally, the modal instance controller.
This controller synchronizes the modal events (open/close) with URL path changes.
angular.module("app").controller('ModalInstanceController', function($scope, $modalInstance, $state, newPath, oldPath) {
$modalInstance.opened.then(function(){
$state.go(newPath);
});
$modalInstance.result.then(null,function(){
$state.go(oldPath);
});
$scope.$on('$stateChangeSuccess', function () {
if($state.current.name != newPath){
$modalInstance.dismiss('cancel')
}
});
});
You may create a state with the same templateUrl and controller as your page where you want to show the modal, adding params object to it
$stateProvider
.state('root.start-page', {
url: '/',
templateUrl: 'App/src/pages/start-page/start-page.html',
controller: 'StartPageCtrl'
})
.state('root.login', {
url: '/login',
templateUrl: 'App/src/pages/start-page/start-page.html',
controller: 'StartPageCtrl',
params: {
openLoginModal: true
}
})
And in controller of the page, use this parameter to open the modal
.controller("StartPageCtrl", function($scope, $stateParams) {
if ($stateParams.openLoginModal) {
$scope.openLoginModal();
}
I found a handy hint to get this working. There are probably caveats, but it works for me. You can pass a result still but I have no need for one.
Using finally instead of the then promise resolve sorted this for me. I also had to store the previous state on rootScope so we knew what to go back to.
Save previous state to $rootScope
$rootScope.previousState = 'home';
$rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from, fromParams){
$rootScope.previousState = from.name;
})
State using onEnter
$stateProvider.state('contact', {
url: '/contact',
onEnter: function ($state, $modal, $rootScope){
$modal.open({
templateUrl: 'views/contact.html',
controller: 'ContactCtrl'
}).result.finally(function(){
$state.go($rootScope.previousState);
})
}
});