I'd like to implement a conditional .when(), like this:
.when('/abc', {
// if MyService.allow == true
template: '<myDirec></myDirec>'
// else
redirectTo: '/'
})
My /abc route shall be like "secured" by a variable hold in one of my services, in a stateful manner. So I want to set this very state somewhere else to true/false and the next time the user tries to get to /abc he will be served conditionally.
How can I achieve this? - with as few 3rd-party dependencies as possible
What I tried and read about:
- Simply injecting my Service in .config, which I learnt is not possible
- Read about using a provider as they can be injected. But can I use them like I use my service?
- template and templateUrl accept a function, but this didn't really help me
Thanks very much in advance!
You could use the $routeChangeStart event. It's fired when the url is changed and takes current and next as arguments something like:
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (next === 'abc') // do something;
});
Related
I'm using Angular 1.5 and ui-router. I want to detect when a user leaves a route. I've currently got something like:
$scope.$on("$stateChangeSuccess", function () {
if (!$scope.flag) {
//...
$scope.do_work(reason);
}
});
this doesn't quite work because when the user navigates to the this route, $scope.flag is set to false and this function incorrectly fires. Is there an idiomatic way to fire a function when a user navigates away from a particular route, but not when they navigate to it?
I'd say use onExit hook of ui-router which you can specify on single state. But you can't access $scope inside it.
To deal with it, I'd say maintain service which will have shareable data. Change that shareable data from onExit hook of your desired state. Then you can access the same service inside your controller as well.
$stateProvider.state("contacts", {
template: '<h1>Dummy Template</h1>',
controller: 'myCotroller',
onExit: function(sharableService){
//... do something sharableService variable...
}
})
I am developing an angular js application, and i need to run a function, via service provider, before everything else. The function is a get request, which returns two variables in JSON, which returns either true orfalse.
If the variable is true, then load another state (i am using ui router). How can i do that?
In angular the document loaded is not really has a good usage, because it is a framework and the we need to count on angular loaded/ready instead. Use angular.module('yourApp').run() like below:
var app = angular.module('yourApp');
app.run(['$rootScope', '$http', function($rootScope, $http){
$http('http://localhost/status/').then(function(res){
$rootScope.somethingStatus = res; // assuming that your response is either true of false.
});
$rootScope.$watch('somethingStatus', function(nv){
// here is where you know your request has been done...
if(nv === true) {
// ...
}
else if(nv === false) {
// ...
}
});
}]);
NOTE: please aware what run in app.run() will be fired before any controller initialing or view rendering. So later on when you want to access the value of what you been got. Inject $rootScope to the controller (or any place) when you needed it.
EDIT: fix some typos. Updated answer again, credit to #georgeawg and #Pierre Emmanuel Lallemant. Thanks you guys for the correction
You should write a controller for the page where you do the request (in order to display something to the user), with a default html view, and when you receive the response, then you change the state to use the desired controller.
I have some code...
spa.service("testService", function($route, $location, $routeParams) {
this.testValue = $location.path();
})
This is pretty straightforward. I want the path after the http://www.whatever.com. The problem with this is that I want that value dynamically. I want it when I enter the page, and I want it when I change routes, etc.
I've tried using $routes.current I've tried using $location.current,path() and nothing works. I have to be missing something. I've read the documentation over and over, and I'm not sure how to achieve this.
The overall goal is to give some links on my page a certain class when their associated route is active. I'm attempting to do this by creating a function in the controller assigned to the links that returns a boolean based on whether or not the route variable I'm trying to declare is equal to the input of the aforementioned function. When the function outputs true,I use the ng-class directive to append the class to the correct link.
How do I assign testValue to the dynamic value of the route?
To expand on #acicali's comment, you can do something like:
app.service('spasvc', function($rootScope, $location){
$rootScope.$on('$routeChangeSuccess', function(e) {
console.log('Route Change:', $location.path());
});
});
Plunk: http://plnkr.co/edit/ov5lxGdEBPcwNeq9lfxT?p=preview
However, this only makes sense if you have a plan of mutating the service's internal state. And you can assign some variable to store value of $location.path(). It doesn't have much utility wherever injected. Of course, you can code around that, but there are better mechanisms to deal with that...
I am using angular router` to track the state of my web app like this:
when('/', {
controller: "AController",
templateUrl: "APanel.html"
}).
when('/subpage/:id', {
controller: "BController",
templateUrl: "BPanel.html"
}).
And I am using Angular Service to track some shared values:
app.service('stateService', function() {
this.someSwitch = false;
this.someLongDataArray = [x, y, z];
});
Currently, before changing path to \subpage\:id url from AController, I will assign new values to members of the service, so they can be referenced in subpages.
Now the question is, if user directly launching the subpage url \subpage\:id, or hit the refresh button on browser on subpage, BController will be invoked, and I will lost the values in the service which are supposed to be prepared by AController.
I am wondering what I should do in this case. is there any way I can get called when user launch the subpage directly, so I have a chance to prepare the data? (Maybe I can watch for html onload event, but not sure that's the best answer).
Thanks
It appears, BController is dependent on AController.
Ideally, Controller should not contain any data/dom manipulaton, state maintenance. It is simply a glue between view and the $scope model.
Being said so, you need not create any such dependency between controllers. The service can be invoked from both controllers.
OR
If indeed there is a requirement that APanel.html must be loaded/initialized before BPanel.html is loaded, then you must check for some flag in BContoller and redirect user to APanel.html.
like
if(<check some flag>){
$location.path('/');
}
But then you have to find the way to redirect the user again to BPanel.html. I guess this is not a recommended approach.
I am not sure I get your question completely. But if there is a possibility that the user might hit BPanel.html directly then you should do something like this.
if(serviceExists()){
//Do your usual Bcontroller stuff here if the services was initialized
}
else{
//Show a warning/error like "Oops, something is wrong go back to '/'" OR
// Initialize services in BController
}
This should be in your BController if initializing your service before BController is that important. You basically force people to stay on AController.
How can I pass actual URL (with slashes, commas, etc.) as a $routeParam to AngularJS App?
this will work:
http://paprikka.github.io/le-bat/#/preview/asdadasda
this won't:
http://paprikka.github.io/le-bat/#/preview/http://page.com
neither will this:
http://paprikka.github.io/le-bat/#/preview/http%3A%2F%2Fpage.com
or this:
http://paprikka.github.io/le-bat/#/preview/?url=http%3A%2F%2Fpage.com
Details
AngularJS routing mechanism by its design does not allow to pass strings with slashes as query parameters. I can understand the reasoning behind this decision - we don't want to create a stateless server here.
However, there are still cases when using different separators or regular expressions in routes might be necessary.
I wanted to create an app that takes a url hash string parameter and loads its content to an iframe (link here). Routes are set up in pretty standard way (I'm using Coffeescript, but this snippet does not differ from pure js):
$routeProvider
.when('/preview/:src', {templateUrl: 'partials/preview.html',
controller: 'PreviewCtrl'})
.when('/preview', {templateUrl: 'partials/preview.html',
controller: 'PreviewCtrl'})
Of course, I can load url from hash before AngularJS gets bootstrapped and then pass it to the library, but it would be nice if I could also update current route parameter when changing data in scope - that's why I think it's much better not to avoid AngularJS API.
Using $routeProvider in Angular 1.2, you can pass in a url if it's at the end of the path by adding an asterik to the pattern. The following should work whether or not you URLComponentEncode the url.
The route:
angular.module('angularApp', ['ngRoute'])
.when('/frame/:picture_url*', {
templateUrl: 'views/frame.html',
controller: 'PictureFrame'
});
The controller:
.controller('PictureFrame', function($scope, $routeParams, $sce){
//whitelist the URL
$scope.picture_url = $sce.trustAsResourceUrl($routeParams.picture_url);
});
Then in your template:
<iframe ng-src="{{picture_url}}"></iframe>
Ok, I've managed to find a solution working with current stable version (#1.0.7).
Current way of handling this problem will involve $route-related events, parsing angular-incompatible urls on the fly and handling them via an additional service working in a similar way as $http interception.
You can see working code examples here: http://embed.plnkr.co/fIA2xj/preview
Main steps
pass an angular-incompatible url as usual, eg. go to site.com/url/http://site.com
listen to a $routeChangeStart event and extract correct url parameter for paths beginning with /url/
encode the correct url parameter to an angular-compatible form (in this particular case, I use base64). Don't use encodeURIComponent, because angular will treat as any other url
redirect to another route with your business logic, eg. site.com/parsed-url/BASE64_GOES_HERE
decode the URL in the controller and use it as usual :)
Code
Create angular app module as usual
angular.module('routes',[]).config([
'$routeProvider',
function($routeProvider){
$routeProvider
.when('/test', {templateUrl: 'test.html'})
// This one is important:
// We define a route that will be used internally and handle
// parameters with urls parsed by us via the URLInterceptor service
.when('/parsed-url/:url', {templateUrl: 'url.html', controller:'URLCtrl'})
.when('/', {redirectTo: '/test'})
.otherwise({templateUrl: '404.html'});
}
])
URL Interceptor service (singleton)
.service('URLInterceptor', function($rootScope, $location){
// We listen to $routeChangeStart event and intercept it if
// the path matches our url scheme. In this case, every route
// beginning with /url/ will be caught
$rootScope.$on('$routeChangeStart', function(e, next, current){
// $location.path does change BEFORE actual routing happens,
// so in this case we get parsed new location object
// for free.
// To be hones, a better way of handling this case might be using
// $locationChangeStart event instead, but it would require us to parse urls
// manually.
var path = $location.path();
// check if string begins with '/url/'
var matcher = path.slice(0,5);
var cleanPath = '';
if (matcher === '/url/'){
// Yes it does, yay!
// Remove leading '/url/' to extract the actual parameter
cleanPath = path.slice(5);
// Encode our url to a safe version. We know that encodeURIComponent won't
// work either, so a good choice might be base64.
// I'm using https://code.google.com/p/javascriptbase64/downloads
$location.path('/parsed-url/' + Base64.encode(cleanPath));
// Prevent default event execution. Note that, it won't cancel related $location Events
e.preventDefault();
}
});
return {
decode: Base64.decode,
encode: Base64.encode
}
})
Controllers
// Main application controller
// We instantiate our URLInterceptor service here
.controller('AppCtrl',function($scope, $location, URLInterceptor){
$scope.navigateTo = function (path) {
$location.path('/url/' + path);
}
})
.controller('URLCtrl', function($scope, $routeParams, URLInterceptor){
$scope.url = URLInterceptor.decode($routeParams.url);
});
Two things you should remember:
Although I tried to create a solution as clean as possible, usually passing the data this way to angular isn't considered a good practice, so try not to use it unless you really need to.
You can handle this issue with only one route. I just find it cleaner this way.
I have a solution but I don't know if it will help you. From Angular documention http://docs.angularjs.org/api/ng.$location $location has a function search(search, paramValue)
To pass the parameter:
parameter = encodeURIComponent url
$location.search({ yourURLParameter: parameter }).path('/preview')
To read the parameter:
url = decodeURIComponent $location.search().yourURLParameter
Of course you need to inject $location dependency
I have mixed search params with routes. Your search needs to come before your routes.. specifically for older browsers. I think ie7 blows up if its not url/?search/#/hash
Try this format:
domain.com/?my=params&another=param/#/my/hashes