I am currently using ui router as below: -
$stateProvider
.state('login', {
url: '/login'
, resolve: loadSequence(
'base')
, templateUrl: 'app/shared/main/login-main.html'
, controller: 'mainController'
, abstract: true
})
.state('login.signin', {
url: '/signin'
, resolve: loadSequence(
'login-items'
, 'spin'
, 'ladda'
, 'angular-ladda'
, '_loginController'
)
, templateUrl: "app/components/login/login_login.html"
, controller: 'loginController'
});
Now in loginController I want to be able to access a function in the mainController.
Is that possible with my current implementation:-
angular.module('app').controller('mainController', function($scope, $state) {
$scope.showWarning= function(){
//show warning
}
});
angular.module('app').controller('loginController', function($scope, $state) {
// I want to access $scope.showWarninghere;
});
Extract the getData() method out into a service and then you can inject it into both controllers:
angular.module('app').factory('dataService', function () {
return {
getData: function() { ... }
}
});
angular.module('app').controller('mainController', function($scope, $state, dataService) {
// You probably don't need to put this into your scope, but if you do:
$scope.getData = dataService.getData.bind(dataService);
});
angular.module('app').controller('loginController', function($scope, $state, dataService) {
dataService.getData();
});
It is useful to remember that scopes and controllers are created and destroyed as you navigate between states, so anything that actually wants to exist in more than one state really does want to be stored in a service.
As said by Duncan and Sanjay, it might indeed be a better idea to use a service to get the data, but I thought I'd answer the original question so you know :
As stated in the docs, for prototypal inheritance to be active the views must be nested, not only the states.
So in order for loginController to have access to the scope of mainController, the login-main.html template must use the uiView directive (e.g <div ui-view></div>) which will be the placeholder for the login_login.html template of the login.signin state.
you can use angular service for this.
I found a Plunker code to resolve your problem
Related
I have this controller:
class Ctrl{
constructor($http, $scope, $stateParams){
if(!$scope.items){
$http.get(...).then((res)=> {
$scope.items = res.data;
});
}
if($stateParams.id){
$scope.currItem = $scope.items[$statePrams.id];
}
}
I have 2 states:
.state('dashboard.items', {
url: '/items',
templateUrl: 'items.html',
controller:'Ctrl'
})
.state('dashboard.items.details', {
url: '/:id',
templateUrl: '/ItemDetails.html',
controller: 'Ctrl'
})
I want to be sure that items array already initialized before I show the details of one of them.
How is it possible without code duplicating? (If else and same code inside each)
Thanks.
you can use state resolve on parent level (that is exact answer on your question)
or just use service and make initialisation there. service is singleton.
I need to switch view in my angular js webapp.
In order to handle the routing, I am using the $stateProvider, as here:
.config(['$httpProvider', '$stateProvider', '$urlRouterProvider', function ($httpProvider, $stateProvider, $urlRouterProvider) {
$stateProvider
.state('common', {
templateUrl: 'assets/html/template.html',
abstract: true,
})
.state('login', {
url: '/login',
parent: 'common',
templateUrl: 'app/models/authentication/views/login.html',
controller: 'LoginController'
})
.state('home', {
url: '/',
parent: 'common',
templateUrl: 'app/models/home/views/home.html',
controller: 'HomeController'
})
...
Inside my login controller, I am trying to switch the view (from Login to Home)
.controller('LoginController', ['$scope', '$rootScope', '$cookieStore', '$location', '$window', '$http',
function ($scope, $rootScope, $cookieStore, $location, $window, $http) {
$scope.login = function () {
loadHttp();
};
function loadHttp() {
var url = "http://myurl";
...
.then(function (response) {
$scope.$apply(function () {
$location.path('/home');
console.log("$location.path: " + $location.path);
});
});
}
But when I reach $location.path('/home'); I get this error:
Error: [$rootScope:inprog] http://errors.angularjs.org/1.4.8/$rootScope/inprog?p0=%24digest
at Error (native)
at http://localhost:3000/adminTool/assets/js/angular.min.js:6:416
at t (http://localhost:3000/adminTool/assets/js/angular.min.js:126:132)
at r.$apply (http://localhost:3000/adminTool/assets/js/angular.min.js:133:515)
at http://localhost:3000/adminTool/app/models/authentication/controllers.js:46:32
at http://localhost:3000/adminTool/assets/js/angular.min.js:119:129
at r.$eval (http://localhost:3000/adminTool/assets/js/angular.min.js:133:313)
at r.$digest (http://localhost:3000/adminTool/assets/js/angular.min.js:130:412)
at r.$apply (http://localhost:3000/adminTool/assets/js/angular.min.js:134:78)
at g (http://localhost:3000/adminTool/assets/js/angular.min.js:87:444)
What am I doing wrong? How to get rid of it and finally switch view?
I read to use $apply from SO
PS: I am very new to angular
The error you get is because of the $scope.$apply around the $location.path('/home') statement. Angular is already in a digest loop so triggering it again within that loop (by calling $apply) will give you this error. Removing the $scope.$apply will therefor probably fix your problem.
The reason the digest loop is already triggered is because you are probably using the $http service which uses a promise to return the value. Angular always triggers a digest when resolving this promise.
But aside from that you probaly want to use the $state service to move to another state instead of moving to another location using the $location service. $state.go('home') would probably be what you are looking for.
I think here is the problem $location.path('/home');
You don't have path /home you have state called home
so you need to go $location.path('/'); or inject $state and use method $state.go('home'), also you do not need to wrap it inside $apply
i'm investigating if i can have what the title says.
Here's my thought.
Let's assume that i've got this routes:
.when('/', {
templateUrl : 'partials/homepage.html',
})
.when('/test', {
templateUrl : 'partials/test.html',
})
.when('/page/:pageID', {
templateUrl : 'partials/page.html',
})
.when('/page/single/:pageID', {
templateUrl : 'partials/page-single.html',
})
Until now i had the opportunity to add the templateUrl as also the controller details in the route and everything was working just fine.
Now the app is changed and there is only one controller with all the information needed and must remain one controller. And the routes will be something like that:
.when('/:templateName/:pageID', {
controller: 'myCtrl'
})
Can i set from the controller the template id by getting the templateName parameter? And if so how about the last route example /page/single/:pageID? How can i know that there is a second option in route?
I can take the templateName parameter and see it changing with the $routeChangeSuccess method but i cannot find any way to set the template on the fly.
Any ideas?
One solution could be the following one:
angular.module('myapp', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/:templateName/:pageId', {
templateUrl: function(urlattr){
return '/pages/' + urlattr.templateName + '.html';
},
controller: 'YourCtrl'
});
}
]);
From the AngularJs 1.3 Documentation:
templateUrl – {string|function()} – path or function that returns a path to an html template that should be used by ngView.
If templateUrl is a function, it will be called with the following parameters:
Array.<Object> - route parameters extracted from the current $location.path() by applying the current route
I would move your singleton logic from your controller to a service. Since you didn't provide much code below is an example to give you an idea how it could work.
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'partials/homepage.html',
controller: 'SingleController'
})
.when('/test', {
templateUrl: 'partials/test.html',
controller: 'SingleController'
})
.when('/page/:pageId', {
templateUrl: 'partials/page.html',
controller: 'SingleController'
});
});
app.provider('appState', function() {
this.$get = [function() {
return {
data: {}
};
}];
});
app.controller('SingleController', function ($scope, appState) {
$scope.data = appState.data;
});
But if it must be a singleton controller you actually could use the ng-controller directive before your ng-view directive so it becomes a $rootScope like scope for all your views. After that just add empty function wrappers in your $routeProvider for the controllers.
Here's my controller code
.when('/showprofile/:UserID', {
templateUrl: 'resources/views/layout/showprofile.php',
controller: 'ShowOrderController',
})
I am passing the parameter by url.
I am trying to access this page by the url directly like this
http://192.168.1.58/myapp/#/showprofile/8
But it is redirecting me to
http://192.168.1.58/myapp/#/showprofile/:UserID
How can i get the url value in my view ?
Here is my app.js and here is my authCtrl.js
Try this in your controller, it will return the object based on url value then we can get the respected value like this
//it will return the object
console.log($routeParams);
//get the specific url value like this
console.log($routeParams.UserID);
or
console.log($route.current.params.UserID);
Yes possible but you have to inject the $state in your controller and get
if you use $state means
console.log($state.params.userID);
Try this...
var sampleApp = angular.module('sampleApp', []);
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/ShowOrder/:orderId', {
templateUrl: 'templates/show_order.html',
controller: 'ShowOrderController'
});
}]);
sampleApp.controller('ShowOrderController', function($scope, $routeParams) {
$scope.order_id = $routeParams.orderId;
});
Right, you have something like this in app.js:
.when('/showprofile/:UserID', {
templateUrl: 'resources/views/layout/showprofile.php',
controller: 'authCtrl',
})
That means that authCtrl is assigned to this view.
So, it's neccessary to inject routeParams to authCtrl( remember about dependency injection in javascript ) :
app.controller('authCtrl', ['$scope','$rootScope','$routeParams','$location', '$http', 'Data', function ($scope, $rootScope, $routeParams, $location, $http, Data) {
$scope.myVar = $routeParams.UserID;
console.log('UserID: ',$scope.myVar);
/* (...) */
}]);
Could you tell me, if this change, logs UserID in console? Or is empty?
If it logs, then everything works fine and you can use service to pass this variable between various controllers.
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);
})
}
});