Angular UI Router - Compulsory URL parameter? - angularjs

I have a couple of states the use the same controller. Some of these do not require a URL parameter while some do. How do I avoid a state from being accessible if the URL parameter is not provided?
Example:
I have 2 views or states, list and single. They both share the same controller. I have the routes mapped as follows:
state: app.list
url: /list
controller: appCtrl
state: app.single
url: /single/:id
controller: appCtrl
Now, I want single to be accessed only if the id is specified, other wise redirect to some other page. How is that possible using the same controller?

Approach 1
You can use $urlRouterProvider with when() for redirection.
app.config(function($urlRouterProvider){
// when there is an single route without param, redirect to /list
$urlRouterProvider.when('/single/:id', ['$match', '$state', function($match, $state) {
if($match.id === ""){
$state.transitionTo("app.list");
}
}
]);
});
Working demo: http://plnkr.co/edit/sEoUGGCEge0XbKp3nQnc?p=preview
Approach 2
You can check the param in controller side and redirect it to specific page
myApp.controller('MainCtrl', function($state, $stateParams) {
if($state.current.name == 'app.single' && $stateParams.id === ""){
$state.transitionTo("app.list");
}
});
Working demo: http://plnkr.co/edit/QNF1RHy4Prde4CRhNLFa?p=preview
Note: In the above demos, redirection works when your current state should not be app.single. Means, State will not change if you are in app.single state and trying without param. So go to main page, then click the link without param of single state. it will redirect to list state.

Related

How to redirect using ajax in angularjs

I want to redirect a user on specific page using ajax call in angularjs. I am able to redirect using below mentioned code but when i again want to redirect user to root page i am unable to do so as the value of $window.location.href+ 'getTechnicianWorkOrder/'+woId is persisting as is:
$scope.getTechnicianWorkOrderFormURL = function(woId){
return $window.location.href + 'getTechnicianWorkOrder/'+woId;
};
Another place where i want to redirect to root page:
$scope.getAssignedListURL = function(){
return $window.location.href;
};
Note: i want to make this redirection work even in offline mode of HTML5 cache-manifest.
Using the $location service.
$location.path('/');
The answer by #prashant-palikhe is the right one, $location.path('/'); is the route to your root path. just use the dependency of $location to your controller like this:
yourapp.controller('YourController', ['$location', function($location) {
...
}
I always use the ui.router for my routes and in there you can add $urlRouterProvider.otherwise('/') for fallback in any unknown state or route.
In some cases you can add something like this in your states:
resolve : {
dataObj : ['$http', function($http) {
return $http({method : 'GET', url : '/your/ajax/endpoint'})
;}],
},
onEnter : ['dataObj', '$state', function(dataObj, $state) {
// dataObj is your ajax response object. Based on this you can redirect to a certain state of needed
$state.go('default');
}]
resolve is data that preloads data onEnter is called before entering the state. This can be used as some sort of middleware.

AngularJS. Retain state after page reload

I am using UI Router with html5Mode enabled, states are loaded from JSON.
Expected behavior after F5 or when pasting URL is, respectively, having current state reloaded or navigating to the said state, instead the initial application state is loaded.
For e.g. root/parent/child gets redirected to root/.
By the way, navigating with ui-sref works fine.
So, how can the state be retained after page reload?
In order to retain the state of page after reload app, a url represent the state should be gave. when you include ui-route module, url will be parsed and sent to corresponding state. You don't need to parse the url handly in most cases, ui-route born to do this.
Please can you post your code here? Specifically the $stateProvider.
This is an example of a correct $stateProvider and it works fine:
$stateProvider.state('main.admin', {
url: '/admin',
resolve: {},
views: {
'main-content#main': {
controller: 'AdminController as admin',
templateUrl: 'main/admin/admin.tpl.html'
}
}
});
Seems a bit hacky, but works for now.
app.run(['$location', '$state', function ($location, $state) {
function stateFromUrl () {
var path = $location.path(),
hash = $location.hash();
// do JSON states map parsing and find a corresponding to the URL state
return state;
}
if (stateFromUrl) {
$state.go(stateFromUrl);
} else {
$state.go('home'); // initial state
}
}]);

AngularJS: Checking if user is on a specific route that uses params

If I have a route as such:
$routeProvider.when('/event/:id', { templateUrl: 'view/event-details.html', controller: 'EventDetailCtrl'});
Is there a way to check if the user is on the /event/:id route
Is using if ($location.path().indexOf('/event/') !== -1) a safe way to check if the user is on this route?
Of course there is. Either you can use a console.log in controller or another way you can use an ng-init in the template URL to call any function. So if the template is loaded then it will call the function and hence you get to know that the user is on that route :)
I'm not sure to fully understand what you want to achieve. Your $routeProvider relays your incoming request to a particular controller. It is inside the controller you can fully access the tokens parsed by your $routeProvider.
Declare $routeParams in your controller to access the tokens of your matched route.
$routeProvider
.when('/event/:id', { templateUrl: 'view/event-details.html', controller: 'EventDetailCtrl'});
In your controller:
.controller('EventDetailCtrl', function($scope, $routeParams) {
$scope.id = $routeParams.id;
})

UI Router conditional ui views?

I can't figure out a reasonable way, which doesn't feel like a hack, to solve this rather trivial problem.
I want a guest to see a splash page when they access the index of the website and a logged in user to see their profile, with each page having it's own template and controller. Ideally, there would be two states for one url, and somehow I would be able to automatically alter the active one depending on the loggin status. Both of these views will have their own nested views so ng-include cannot be used (I assume).
I'm quite new to angular and ui router and think I might be overlooking an easy solution to the problem.
Could it be done with named views and ng-show?
If you're using UI Router, just create three states: the root state, with the '/' URL, and two direct descendant states with no URLs. In the onEnter of the root state, you detect the state of the user and transition to the correct child state accordingly. This gives the appearance of keeping the same URL for both child states, but allows you to have to separate states with separate configurations.
The templateUrl can be a function as well so you can check the logged in status and return a different view and define the controller in the view rather than as part of the state configuration
My Solution:
angular.module('myApp')
.config(function ($stateProvider) {
$stateProvider
.state('main', {
url: '/',
controller: function (Auth, $state) {
if (someCondition) {
$state.go('state1');
} else {
$state.go('state2');
}
}
});
});
where state 1 and state 2 are defined elsewhere.
For docs purposes, I used:
$rootScope.$on('$stateChangeStart', function(event, toState) {
if ((toState.name !== 'login') && (!$localStorage.nickname)) {
event.preventDefault();
$state.go('login');
}
});
Using $routeChangeStart didn't work for me.
It is used for me conditional view in ui-route
$stateProvider.state('dashboard.home', {
url: '/dashboard',
controller: 'MainCtrl',
// templateUrl: $rootScope.active_admin_template,
templateProvider: ['$stateParams', '$templateRequest','$rootScope', function ($stateParams, templateRequest,$rootScope) {
var templateUrl ='';
if ($rootScope.current_user.role == 'MANAGER'){
templateUrl ='views/manager_portal/dashboard.html';
}else{
templateUrl ='views/dashboard/home.html';
}
return templateRequest(templateUrl);
}]
});
If I understand the question; you want to make sure that the user who hasn't logged in cannot see a page that requires log in. Is that correct?
I've done so with code like this inside a controller:
if(!'some condition that determines if user has access to a page'){
$location.path( "/login" );
}
Anywhere (probably in some high-level controller) you should be able to just bind a '$routeChangeStart' event to the $rootScope and do your check then:
$rootScope.$on('$routeChangeStart', function(next, current){
if(next != '/login' && !userLoggedIn){
$location.path( "/login" );
}
});
This will get fired every time a new route is set, even on the first visit to the page.
The way I've done this is pretty simple. I made one for our A/B testing strategy. This is the gist:
resolve: {
swapTemplate: function(service) {
// all of this logic is in a service
if (inAbTest) {
this.self.templateUrl = '/new/template.html';
}
}
... other resolves
}
This gets called before the template is downloaded and therefor you're allowed to swap out the template url.
In my case, if two states can share logic of same controller, conditional template is a good choice. Otherwise, creating separate states is a good option.

How to update Angular named route param after data arrives

I have something like
angular.module('MyApp.routing', []).
config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider
.when("/",
{action: "home"})
.when("/calculator/:slug/:docId/:versionId",
{action: "calculator", reloadOnSearch: false})
.otherwise({redirectTo:'/'});
}]);
in my routing file. I make a request to backend API in my controller and I want to update URL slug param.
$scope.initPage = function () {
$scope.content = {};
$scope.isPreloaderVisible = true;
http.post("calculator.getContentData.angular",
{'slug': $scope.slug, 'docId': $scope.docId, 'versionId': $scope.versionId}, getContentSuccess, getContentErrorHandler);
}
To summarize, I use URL like:
http://localhost/#/calculator//123456/
and I want to update it (without reload a whole page) to:
http://localhost/#/calculator/slug/123456/
in method getContentSuccess after request.
Any route change i believe cause view (defined in ng-view) to update itself. The reloadOnSearch is a parameter which is applicable to search parameters which are basically querystring parameters.
So if you can make your slug as part of querystring instead of url path you can stop view change.
Or else you need to look at other routing library like ui-route

Resources