I have a single page AngularJS application which is backed by Ruby on Rails server. The routing definition is only defined in RESTful Rails route.rb config, such as:
http://localhost/pages/:page_id/comments/:comment_id
The client is AngularJS with no routeProvider configuration. So it is just a simple AngularJS controller with view.
When I am in http://localhost/pages/:page_id/comments/:comment_id.html, I want to extract the value of :page_id and :comment_id. What is the best way to do it in AngularJS?
Note: I don't mind if really needed to use routeProvider. But I would only have one templateUrl in that case.
The $routeParams service allows you to retrieve the current set of route parameters.
Requires the ngRoute module to be installed.
The route parameters are a combination of $location's search() and path(). The path parameters are extracted when the $route path is matched.
Note that the $routeParams are only updated after a route change completes successfully. This means that you cannot rely on $routeParams being correct in route resolve functions. Instead you can use $route.current.params to access the new route's parameters.
Example:
// Given:
// URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
// Route: /Chapter/:chapterId/Section/:sectionId
//
// Then
$routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
for more infor
EDIT :
If you are using angular-ui-router, you can inject $stateParams
check out this https://github.com/angular-ui/ui-router/wiki/URL-Routing
While routing is indeed a good solution for application-level URL parsing, you may want to use the more low-level $location service, as injected in your own service or controller:
var paramValue = $location.search().myParam;
This simple syntax will work for http://example.com/path?myParam=someValue. However, only if you configured the $locationProvider in the html5 mode before:
$locationProvider.html5Mode(true);
Otherwise have a look at the http://example.com/#!/path?myParam=someValue "Hashbang" syntax which is a bit more complicated, but have the benefit of working on old browsers (non-html5 compatible) as well. [via : Javarome]
Related
Maybe the title isn't really clear.
I'm working on two nested Angular apps. Both have their own router built with ui-router. If I call a state that is unknown by the main router, I'd like it to search for this state in the sub-app router to get the templateUrl and url related to this state.
I thought about creating a service with a parser. It could parse the file to find the data I want. This solution is probably not the best option I have, that's why I wanted to know if there is a specific function/method in ui-router to achieve it. From what I read on ui-router doc, it seems not :/
Feel free to ask for more details or to suggest another solution which can match with my goal :)
You can achieve this by using dynamic parameters in your $stateProvider configuration defined on your sub-module. So you have some anchored routes on the main module, and if there is a match, ui-router will simply fetch the associated template. If there is no matched absolute route/url, then ui-router falls back on your parameterised route, like so:
// anchored
$stateProvider
.state('mainAppState', {
url: '/anchored',
controller: 'myCtrl'
})
// sub module
.state('subAppState', {
url: /:parameter
resolve: { // use the resolve and $stateParams to get some parameter with which you can make a request to your API }
templateProvider: { // inject a service and use it to call your backend }
You can use a the parameter, which gets passed to $stateParams, which gets passed to your resolve block functions, which can retrieve some data or process the data from $stateParams, that can then be used by templateProvider to provide a meaningful fallback URL. That's because templateProvider receives all resolves. It will always wait for all promises in the resolve block to resolve one way or another before executing.
That way you can catch any unmatched URLs from the main app in your sub app. Hope this helps.
See my issue here: ui-router: async data in templateUrl function
Oh and one more thing - if you are going to mangle your JS in your build process, make sure you define your templateProvider function with .$inject = [ ... ]. If not, you'll get an unknown provider error. ngAnnotate will NOT catch the dependencies in your templateProvider function. Alternatively you can just use /* #ngInject */, which worked for me.
I have a question regarding AngularJS and Node.js.
I have a web application and I use client-side routing with routeProvider to navigate through the pages of my web application.
And I get the data through a RESTful API server-side. But all of logic is done in AngularJS, because with the client-side routing, all I do in Node.js is :
exports.partials = function(req, res, err) {
var name = req.params.name;
res.render(name);
};
So, I only use Node.js to render the template layout and the partial view, but all of the logic is in AngularJS.
Is it the proper way to use it?
Angular.js is a javascript framework to create a SPA or Single page application.
It creates its own navigation system using the hash(#) or hashbang(#!) in the url to represent the different states or pages of your application but all this happens in your home page. The browser never changes to another page because all application state will be lost in a page refresh (HTTP is a stateless protocol).
Usually you need 3 parts to create an Angular application each one with it's own routing system.
Your angular application: All the scripts and resources are loaded in the home page. The routing system is provided by $routeProvider and the hash(#). Eg: http://mywebsite/#/products or http://mywebsite/#/providers. All this is relative to your home page.
Your templates: This is retrieved using ajax and can be routed however you want, Eg: http://mywebsite/product.html or http://mywebsite/templates/product.html serving static html files or even http://mywebsite/templates/products using a restful approach and a server side routing mechanism. There is no general rule here because basically depends on the server technologies chosen and your own design.
Your data: Usually a Resful API to supply you application with business data stored in a database. Rest creates some basic rules you must follow like treat everything as a resource and manipulate it with verbs. Eg: GET http://mywebsite.com/api/products or POST http://mywebsite.com/api/providers
This is an example of an Angular route provider
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: 'ChapterController'
});
In this case angular will fetch your home page from http://mywebsite.com initially and the template from the content of your chapter.html file located in http://mywebsite.com/chapter.html and your data from whatever configuration you set to your $http service. As long as you set your routes in a way that don't conflict with each other you are safe. In your case you can use express.js to create a restful routing system for your templates or serve them directly from the public folder as html.
I want to receive the oauth callback from Spotify and have problems with the # in my URL.
routes.js
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
...
when('/callback', {
templateUrl: 'views/callback.html',
controller: 'CallbackCtrl'
})
...
}]);
So to access that route the URL is e. g. http://test.com/#/callback
For the spotify endpoint the redirect_uri has to be url-encoded:
https://accounts.spotify.com/authorize/?client_id=...&response_type=code&redirect_uri=http%3A%2F%2Ftest.com%2F%23%2Fcallback&scope=user-read-private%20user-read-email&state=profile%2Factivity
The redirect from spotify:
http://test.com/%23/callback?code=..&state=profile%2Factivity
Which results in a 404
I know there are some workarounds like using the / route or enabling html5mode to get rid of the # but I hope there is a true solution for this.
Well your situation is not that awkward at all.
Since you can't control the way that spotify's redirect after OAuth works, it leaves you with two options:
1) Create a URL Path for example http://test.com/redirect_uri/?code=.... which will automatically redirect the user to the webapp in the state of logged in.
This method is not a good practice, unless you really know exactly what you are doing. The major problem here is security. Unless you add really good Mechanism to the redirection page.
2) Easier, and actually better in all aspects:
$locationProvider.html5Mode(true);
A small introduction on this:
Why does this remove the #? Because when HTML5 mode is on, it will use history to refer to native paths in your app.
But hold on this is not the only thing you have to do:
You have to redirect all request to go through the main page normally index.html.
I use to do this with .htaccess, But since of AngularJS 1.3 I know there is another method with adding meta tag of <base href=/base/path/of/app/directory">. Usually <base href="/">
But I still prefer .htacces rewriterule or w/e webserver you are using accordingly.
It looks like you are going to implement the Authorization Code flow client-side, exposing the secret key you were provided when you register your app. This is wrong, since someone might generate tokens on behalf of your application using the client id and secret key.
A better approach is to either use Implicit Grant (in example how it is done on https://github.com/possan/webapi-player-example) or implement the token exchange server-side and pass the token to your AngularJS webapp.
Check out this article on Scotch.io for "pretty URL's" that should fix this issue.
You will have to set html5Mode to true:
// Remember to inject $locationProvider
$locationProvider.html5Mode(true);
Then, include a base in the of your html file:
<base href="/">
This will let you navigate the webpage using relative links.
The article I mentioned also goes into fallbacks for older browsers.
I am using a third party service on my site with its own authentication flow. There is an opportunity to reset password. This feature has the following flow. You become the email with the link on yoursite.com/#xxx=123456
After you enter this page third party library is downloaded, and after some init process it checks the location url for #xxx=\d+ fragment to make some ajax call and prompt some ui modals
var hashKeyElements = window.location.toString().match('#xxx=(.*)');
There is no way to change the way they are doing it.
And here is the problem - Angular rewrites yoursite.com/#xxx=123456 to yoursite.com/#/xxx=123456 and after that regexp is not matched
I tried to create double # in url but it causes errors.
UPDATE: Using angular 1.2.4, it's not a SPA and all of the related links should not be prevented and pushed via pushState
Thank you
update
After you mentioned that you are not using client-side routing I checked again and it seems that angular.js (all versions) does not add hashes (/#/) to URL unless you use $locationProvider or $routeProvider even when html5Mode=false.
`
If you do client-side routing or using $location:
you need to use html5Mode - docs:
app.config(function($locationProvider){
$locationProvider.html5Mode(true)
})
I am interested in moving a lot of my client's "logic" away from Rails routing to AngularJS. I have slight confusion in one topic and that is linking. Now, I do understand there's more than one way to handle this, but what is the common practice in the AngularJS community for handling URLs on handling CRUD for resources. Imagine in the case of an athlete we have a URL such as the following to list all athletes:
http://example.com/athletes
To view an individual athlete:
http://example.com/athletes/1
To edit an individual athlete:
http://example.com/athletes/1/edit
To create a new athlete:
http://example.com/athletes/new
In AngularJS, is it common practice to reroute to similar URLs to create/edit/update? Would you just have one URL handle all of the CRUD type actions in one interface and never change the URL? If you were to change the URL, does that get handled via ng-click and in the click event would you use the $location object to change URLs? I'd love to be able to read up on common practices such as these, but having a difficult time in finding more recent literature on it in an AngularJS context.
** NOTE **
I totally get that you can still use RESTful routes to the backend in order to interact with server-side resources. My question is, what is the style that is recommended to use when updating URLs on the client-side. Are you using AngularJS to do that for each of the CRUD operations?
I would definitely recommend separate URLs for each operation (to enable direct linking). The ones you suggest look fine.
In AngularJS you can use the $route service in combination with the ngView directive to load the appropriate template for each operation and handle the browser location and history mechanics for you.
Step 7 of the AngularJS tutorial gives an example of using Views, Routing and Templates the way I describe here. The following is a simplified version for your example:
Define the routes
In your main application script (e.g. app.js):
angular.module('AthletesApp', []).
config(['$routeProvider', function($routeProvider, $locationProvider) {
// Configure routes
$routeProvider.
when('/athletes', {templateUrl: 'partials/athletes-list.html', controller: AthleteListCtrl}).
when('/athletes/:athleteId', {templateUrl: 'partials/athlete-detail.html', controller: AthleteDetailCtrl}).
when('/athletes/:athleteId/edit', {templateUrl: 'partials/athlete-edit.html', controller: AthleteEditCtrl}).
when('/athletes/:athleteId/new', {templateUrl: 'partials/athlete-new.html', controller: AthleteNewCtrl}).
otherwise({redirectTo: '/athletes'});
// Enable 'HTML5 History API' mode for URLs.
// Note this requires URL Rewriting on the server-side. Leave this
// out to just use hash URLs `/#/athletes/1/edit`
$locationProvider.html5Mode(true);
}]);
We also enable 'HTML Mode' for URLs, see note below.
2. Add an ngView directive to your HTML
In your main index.html you specify where the selected partial template will go in the overall layout:
<!doctype html>
<html ng-app="AthletesApp">
...
<!-- Somewhere within the <body> tag: -->
<div ng-view></div>
...
</html>
3. Create templates and controllers
Then you create the partial view templates and matching controllers for each of the operations. E.g. for the athlete detail view:
partials/athelete-detail.html:
<div>
... Athete detail view here
</div>
athleteDetailCtrl.js:
angular.module('AthletesApp').controller('AtheleteDetailCtrl',
function($scope, $routeParams) {
$scope.athleteId = $routeParams.athleteId;
// Load the athlete (e.g. using $resource) and add it
// to the scope.
}
You get access to the route parameter (defined using :athleteId in the route config) via the $routeParams service.
4. Add links
The final step is to actually have links and buttons in your HTML to get to the different views. Just use standard HTML and specify the URL such as:
Edit
Note: Standard vs Hash URLs
In older browsers that don't support the HTML5 History API your URLs would look more like http://example.com/#/athletes and http://example.com/#/athletes/1.
The $location service (used automatically by $route) can handle this for you, so you get nice clean URLs in modern browsers and fallback to hash URLs in older browsers. You still specify your links as above and $location will handle rewriting them for older clients. The only additional requirement is that you configure URL Rewriting on the server side so that all URLs are rewritten to your app's main index.html. See the AngularJS $location Guide for more details.
The angular way is the restful way:
GET all http://example.com/athletes
GET one http://example.com/athletes/1
POST new http://example.com/athletes
PUT edit http://example.com/athletes/1
DELETE remove http://example.com/athletes/1
Note that $resource also expects a few other things, like resource URLs not ending with a slash, PUT requests returning the updated resource, etc.
If your API doesn't meet these criteria, or you simply need more flexibility, you can build your own $resource-like CRUD service based on the lower-level $http service. One way of doing the latter is explained here
Option 1: $http service
AngularJS provides the $http service that does exactly what you want: Sending AJAX requests to web services and receiving data from them, using JSON (which is perfectly for talking to REST services).
To give an example (taken from the AngularJS documentation and slightly adapted):
$http({ method: 'GET', url: '/foo' }).
success(function (data, status, headers, config) {
// ...
}).
error(function (data, status, headers, config) {
// ...
});
Option 2: $resource service
Please note that there is also another service in AngularJS, the $resource service which provides access to REST services in a more high-level fashion (example again taken from AngularJS documentation):
var Users = $resource('/user/:userId', { userId: '#id' });
var user = Users.get({ userId: 123 }, function () {
user.abc = true;
user.$save();
});
Option 3: Restangular
Moreover, there are also third-party solutions, such as Restangular. See its documentation on how to use it. Basically, it's way more declarative and abstracts more of the details away from you.
In AngularJS you can definitely use RESTful server side data sources, there is build in service called $resource.
Alternatively you can also use restangular which has additional features over $resource.
If you want to have full control you can always use $http service which is low level angular component for interacting with http.
Simply implement something that is RESTful, that is the angularJS way. If you have no idea what RESTful is or, know a little and want to know a lot more, then I would recommend that you read this article.
Basically, REST is what is understood to be, an intuitive implementation of WEB URIs, it also makes use of all HTTP verbs, their correct use actually. REST is an approach, and architecture to building web apps.