I am using angular-ui-router module of AngularJS and have the following ui-sref which when I hover over shows http://localhost:3000/u/bob#/123/pin/4567, but when I click on it, I get only http://localhost:3000/u/bob#/123/pin/ on the browser's URL address bar and get the parameter value 4567 lost.
HTML code (index.html):
<a ui-sref="users.maker">
<!-- stuff -->
</a>
JavaScript code:
.state('users', {
url: '/:foo',
views: {
'': {
templateUrl: '/partials/index.html',
controller: ['$rootScope', '$stateParams',
function($rootScope, $stateParams) {
//stuff
}]
}
}
})
.state('users.maker', {
url: '/pin/:bar',
templateUrl: '/partials/users.maker.html',
controller: ['$stateParams', '$scope', '$http',
function($stateParams, $scope, ) {
//stuff
}]
})
Could somebody help me with it?
Have you tried defining your ui-sref as following?
ui-sref="users.maker({ param })
Dynamic ui-sref do not work in ui-router. A function using $state.href() could be used to resolve this issue.
So <a ui-sref="users.maker({ foo: bar })">...</a> could be done by following:
HTML
<a href="{{goToState('users.maker', bar)}}">..<a>
Inside AngularJS Controller
$scope.goToState = function(state, param) {
return $state.href(state, {foo: param});
}
This is with angular-ui-router version 0.2.10.
For more:
https://github.com/angular-ui/ui-router/issues/395
Related
I'm stuck with nested states in the ui-router.
I'm rendering a page with regions, countries and people per country
The index.html has three regions as link, EMEA APAC and AMER
the part of the index page has:
<a ui-sref="territory({territory: 'amer'})">AMER</a> <a ui-sref="territory({territory: 'emea'})">EMEA</a> <a ui-sref="territory({territory: 'apac'})">APAC</a>
<ui-view></ui-view>
when the link is clicked, the countries are returned by a $http.post action and displayed by /views/countries.html as:
<h1>This is countries</h1>
<br>
Teritory: {{region}}
<div ng-repeat='country in countries'>
<a ui-sref=".support({country: '{{country}}'})">{{country}}</a ui-sref>
</div>
This is working so far! so I have a .support link on each country.
The problem is now, when I click the link, the state is accepted...cause it is loading the templateURL, but it seems that the controller specified in that state is not loaded.
the angularjs code:
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'views/home.html',
controller: 'supportCtrl'
})
.state('territory', {
url: '/territory/:territory',
templateUrl: 'views/countries.html',
controller: 'countryController'
})
.state('territory.support',{
url: '/:country',
templateUrl: 'views/supporter.html',
controller: 'supportController'
})
});
the support controller is writing a console.log entry...but nothing happens.
the controllers:
supportApp.controller('countryController',['$scope', 'supportService', '$location', '$stateParams', function($scope, supportService, $location, $stateParams){
$scope.fromTerritory = $stateParams.territory;
$scope.getCountries = function(){
var countries = supportService.getCountries($scope.fromTerritory);
countries.then(function(response){
if(response.data.error){
$scope.error = true;
$scope.message = response.data.message;
}
else{
console.log('response OK');
$scope.region = $scope.fromTerritory;
$scope.countries = response.data;
}
});
}
$scope.getCountries();
}]);
supportApp.controller('supportController',['$scope', 'supportService', '$location', '$stateParams', function($scope, supportService, $location, $stateParams){
console.log('in the supportController');
var test = function(){
console.log('supportController country: '+$stateParams.country);
}
test();
}]);
I'm probably doing something wrong here.
The goal is clicking the country and then displaying names of the people belonging to that specific country.
If it's not clear, I can provide more info where needed. But for now it seems that the controller (supportController) for the nested state (.territory.support) is not running / loaded
thank you!
Ok,
I have my answer for now...
because I did not use the <ui-view></ui-view> on the supporter.html the controller was not 'needed' and did not load.
after adding the the consol.log is showing the log entries.
I am trying to make a nested views, here is the plunker https://embed.plnkr.co/oRMnMW4QoWwhSkm9maHf/. The state changes but the template not changes.
Can anyone correct me what have I done wrong
Goto Link > Second Nested .
On Clicking the button , state changes successfully but the content is not injected. I want the link page content to be replaced by the second-nested content
Try to put abstract:true on the 'father' root like:
var routerApp = angular.module('routerApp', ['ui.router','ncy-angular-breadcrumb']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home/list');
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('home', {
url: '/home',
abstract: true,
templateUrl: './partial-home.html'
})
// nested list with custom controller
.state('home.list', {
url: '/list',
templateUrl: './partial-home-list.html',
controller: function($scope) {
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
}
})
.state('home.second', {
url: '/second',
templateUrl: './second.html',
});
});
routerApp.run(['$rootScope', '$state', function ($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log("state Change")
});
}]);
but Remember .. if you put abstract: true .. the url is not a real one .. it's a prefix .. or as i call it .. a father of other routing ..so you can not call it in the .otherwise()
And in your link (and routing) of the second view .. just remove the .list ... so like this:
.state('home.second', { //<-- HERE .. REMOVE THE .list
url: '/second',
templateUrl: './second.html',
});
and in the link:
// AND HERE ..
<a ui-sref="home.second" class="btn btn-danger">Second Nested</a>
Answer is pretty simple - you are initializing 3rd level of nesting states but in ./partial-home-list.html you've didn't add ui-view directive.
Add <ui-view></ui-view> in ./partial-home-list.html and you will see that it works as you defined.
If you want to display home.list.second as separate page then define second state like this
.state('home.second', {
url: '/home/list/second',
templateUrl: 'second.html',
});
Remember to update ui-sref to home.second on button
--
Just to get nested breadcrumb I have not "nice" solution but will work.
-- Partial home list html
<div ng-if="state.current.name != 'home.list.second'">
<ul>
<li ng-repeat="dog in dogs">{{ dog }}</li>
</ul>
<div ncy-breadcrumb></div>
<a ui-sref="home.list.second" class="btn btn-danger">Second Nested</a>
</div>
<ui-view></ui-view>
App js
// nested list with custom controller
.state('home.list', {
url: '/list',
templateUrl: 'partial-home-list.html',
controller: function($scope, $state) {
$scope.state = $state;
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
}
})
.state('home.list.second', {
url: '/second',
templateUrl: 'second.html',
});
I have difficulties using <a href, so i try to find another workaround. The plan is to use ng-click to invoke an url, and reload the page using that url.
This is my html :
<li class="item" ng-click="go_to()">
<img src="img/ionic.png">
<p>Beginners Guide</p>
</li>
Which call :
app.controller("listController", ['$scope', '$location', function ($scope, $location) {
$scope.go_to = function () {
alert("a"); //CALLED
var url = "http://google.com";
$location.url(url); //NOT LOADED
}
}]);
The end goal is, i just want my apps to open the url when the <li> is clicked
Thanks a lot for your help
One simple way which always worked for me was :
<a href="...">
<li class="item" ng-click="go_to()">
<img src="img/ionic.png">
<p>Beginners Guide</p>
</li>
</a>
You can use
$window.location.href = 'http://google.com'
Full code should look like
app.controller("listController", ['$scope', '$location', '$window', function($scope, $location, $window) {
$scope.go_to = function() {
alert("a"); //CALLED
var url = "http://google.com";
$window.location.href = url;
};
}]);
Its better to use $stateProvider and you also need to add whitelist plugins to
access external resources.
Use $stateProvider like this
.config(function($stateProvider, $urlRouterProvider) {
// Ionic uses AngularUI Router which uses the concept of states
// Learn more here: https://github.com/angular-ui/ui-router
// Set up the various states which the app can be in.
// Each state's controller can be found in controllers.js
$stateProvider
// setup an abstract state for the tabs directive
.state('tab', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html'
})
// Each tab has its own nav history stack:
.state('tab.dash', {
url: '/dash',
views: {
'tab-dash': {
templateUrl: 'templates/tab-dash.html',
controller: 'indexController'
}
}
})
.state('tab.chats', {
url: '/chats',
views: {
'tab-chats': {
templateUrl: 'templates/tab-chats.html',
controller: 'CenterListController'
}
}
})
.state('tab.chat-detail', {
url: '/chats/:centerId',
views: {
'tab-chats': {
templateUrl: 'templates/chat-detail.html',
cache:true,
controller: 'CenterDetailController'
}
}
})
.state('tab.manual-Location', {
url: '/manualLocation',
views: {
'tab-account': {
templateUrl: 'templates/tab-account.html',
controller: 'ManualLocationController'
}
}
})
.state('tab.manual-Location-List', {
url: '/manualLocationList/:locationID',
views: {
'tab-account': {
templateUrl: 'templates/manualLocationList.html',
controller: 'ManualLocationListController'
}
}
})
.state('tab.manual-Location-Detail', {
url: '/manualLocationDetail/:centerId',
views: {
'tab-account': {
templateUrl: 'templates/manual-Location-Detail.html',
controller: 'ManualLocationDetailController'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/tab/dash');
});
You could use $window.open(url, '_blank')
OR
You could look at this answer, in which I have made a such changes that you could redirect to other page, from ui-router state itself.
You can also customize ui-router by adding parameter external, it can be true/false.
$stateProvider
.state('external', {
url: 'http://www.google.com',
external: true
})
Then configure $stateChangeStart in your state & handle redirection part there.
Run Block
myapp.run(function($rootScope, $window) {
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams) {
if (toState.external) {
event.preventDefault();
$window.open(toState.url, '_self');
}
});
})
Working Plunkr
The $location service is not designed to navigate away and reload the current page. It is designed for single page applications.
https://docs.angularjs.org/guide/$location
The $location service allows you to change only the URL; it does not
allow you to reload the page. When you need to change the URL and
reload the page or navigate to a different page, please use a lower
level API, $window.location.href.
If you are trying to navigate to a different page, use:
$window.location.href = "http://www.google.com";
If you are using ui-router. You can use <a ui-sref="" as a way to navigate to different routes. Which is really just a wrapper around $state.go
YOU CAN USE THE FOLLOWING CODE FOR THIS PROBLEM
$window.open("http://google.com", "_blank");
This will open the url in next tab
http://plnkr.co/edit/hfQ18sdHHzEgkF4vEYs5?p=preview
This is one of the working solution for your question
$scope.go_to = function() {
alert("a"); //CALLED
var url = "http://google.com";
$window.open(url, "_blank");
};
Finally found the culprit :)
Its because i commented this single code in my index :
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
After that, i just need to use a basic href
Original Question
I'm developing a mobile app using the Ionic Framework and AngularJS and I am having issues with controllers not reloading once they have been initialised.
One of the state transitions (from 'app.postbox-details' to 'app.audit-questions') should pass a parameter to the 'app.audit-questions' controller but this controller does not update itself with the new parameter because it isn't reloading.
Code Example
app.js file - config
angular.module('sf-maintenance', ['ionic', 'starter.controllers', 'starter.services', 'ngCordova'])
.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'templates/home.html',
controller: 'HomeCtrl',
})
.state('app', { //app state being the side-menu
url: '/app',
abstract: true, //means that this state will never be activated directly, users will always go to a child state instead.
templateUrl: 'templates/side-menu.html',
controller: 'MenuCtrl'
})
.state('app.postbox-details', {
url: '/postbox-details',
views: {
'menuContent': {
templateUrl: 'templates/postbox-details.html',
controller: 'PostboxDetailsCtrl'
}
}
})
.state('app.audit-questions', {
url: '/audit-questions/:postboxGuid',
views: {
'menuContent': {
templateUrl: 'templates/audit-questions.html',
controller: 'AuditCtrl'
}
}
})
$urlRouterProvider.otherwise('/home');
});
controller.js file (left out the code that isn't relevant)
angular.module('starter.controllers', [])
.controller('HomeCtrl', function ($scope) {
})
.controller('MenuCtrl', function ($scope) {
})
.controller('PostboxDetailsCtrl', function ($scope, $ionicLoading, $ionicPopup, $cordovaBarcodeScanner, $state, DataService) {
$scope.postboxGuid = DataService.getNewGUID();
//Rest of the controller functions are below
})
.controller('AuditCtrl', function ($scope, $ionicSlideBoxDelegate, $stateParams, DataService) {
$scope.auditDetails = {
postboxGuid: $stateParams.postboxGuid
};
});
View - navigation code
The view code to perform the navigations all use <a> tags:
From the home view to the postbox-details view: <a class="button button-block button-dark icon-right ion-chevron-right" href="#/app/postbox-details">New inspection</a>
From the postbox-details view to audit-questions view: <a class="button button-block button-dark icon-right ion-chevron-right" ng-click="saveFormData()"
ng-href="#/app/audit-questions/{{postboxGuid}}">New audit</a>
So does anybody know how to get controllers to reload once it has been initialised or if I am going about this problem the wrong way could you guide me to a method that will work?
Updated Information
I recently saw a related question and the response by #Radim Köhler pointed to the answer in this question which provides good information on why it may not be a good idea to use cache:false on a view because of performance.
I thought I would share this because in some situations you may benefit more performance-wise by using one of Ionic's built-in view life cycle events to run code without having to disable the view from being cached.
Views are standard cached in ionic. The caching can configured in the view or stateprovider.
http://ionicframework.com/docs/api/directive/ionNavView/
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);
})
}
});