I'm building a UI using AngularJS that consumes a REST service:
Here is the Server API
/items/ GET
/items/:id GET
/items/ POST (to create new item)
/items/:id PUT (to edit item)
/items/:id DELETE
What are the best practices when setting up the routes in Angular? These routes would map to the server REST API, but obviously there is a problem. I'm guessing I would need the action as part of the URL, right? Something like this:
Angular Routes:
/items/
/items/:id
/items/new
/items/:id/edit
/items/:id/delete
However the above pattern also has a problem. /items/new will match both /items/:id and /items/new so what is the best practice when setting up a route for create?
Also keep in mind that the client side, Angular, routes you're defining could be dependent on your UI. The routes you've defined are more like a traditional web application where you click an "Add New" button that takes you to a new page that has a form that you fill out. However, this may not be the pattern you use for a Single Page App (SPA) as is often created with Angular.
By this I mean that most of the SPA apps I've done don't actually have a standalone "/items/new" route on the client side. Instead, the "/items/new" functionality is handled on the "/items/" route/partial ("page" in traditional web app terms). This page lists the existing items, and there is a form on this page that you can fill out to create a new item. Or, there is an "Add New" button on this page (just like a traditional web app); but, clicking it either slides in a modal form or ng-shows a form that is already defined (but initially hidden) on the /items/ partial template.
Upon submission, the controller hits against "POST /items/" on the server to create the new item, updates scope with the return value from the POST (assuming success, this would be the new item info), and clears/re-hides the "new" form.
Bottom line -- keep in mind that in a SPA you may not actually need a "/items/new" if the UI is such that the new is handled as a capability of the item list page. Of course, if you're needing it to be a standalone page as an addressable endpoint (i.e. you're planning to link to this from multiple places, not just from within the app), then you'll obviously need a direct route.
In our case, we typically don't need a named route for it in our apps and we just have it serviced as a capability of the "/items/" route.
Using UI-Router we can setup client side routing.
Make sure you disable html5 routes, because some browsers still doesn't support html5 and they hit the server api instead of hitting the client route
You can do this by setting $locationProvider.html5Mode(false); in app.config method while defining angular app
/items/ - this is for listing items
/items/{id:[0-9]{1,4}} - this is for displaying one item in detail
/items/add - for displaying new item form
/items/:id/edit - for displaying existing item in Form for editing
/items/:id/delete - **This is not required, I mean you just hit the API when
use clicks delete, we cant show any deletion form**
You can use regex for params '/items/{id:[0-9]{1,4}}' this means allow only numbers 0 to 9 and 1 to 4 characters long
Related
The basic question is if there should be one HATEOAS entry point per page or one entry point for the entire app?
Most examples I have seen implement a single page with a HATEOAS backend, e.g. a list that is paginated using prev and next links. But what happens when the app navigates to a different page that has completely different capabilities and needs a different entry point? How does one organize such an app?
Concrete example: We have an app with tab navigation. The home tab shows a product catalog so the entry point is /products which returns page 1 of products along with pagination links. However, now I click on the orders tab which must show a list of past orders. This page needs a completely different entry point /orders which the home tab knows nothing about. In fact, user can directly navigate to the orders tab using a deep link.
How to think about this problem? Are there any examples illustrating an approach?
There's a few different ways to solve this. Here's two:
The SPA takes context from the uri (such as an id), and uses it to search on the API. The API can describe templated links/actions to search for resources by their id.
Instead of doing client-side routing, let the server decide what to render.
We're going more towards #2. If we have a uri such as:
https://spa.example/https://api.example/foo/bar
We take the path part of this uri and hit the API. Based on what the API responds with, we decide what to render. This means that most of the routing for these kinds of endpoints is delegated to the server, which feels more in line with HATEOAS.
The path part in this case is an entire absolute URI, but we also support relative uris and have a default base uri. So in practice these two uris are equivalent (for our SPA):
https://spa.example/https://api.example/foo/bar
https://spa.example/foo/bar
We develop & use ketting / react-ketting to do most of the heavy lifting
I've angular app with lots of states and modules etc. Now, I want to send a link to the user. When user'll hit this url, I want to redirect him to a new tab rendering only that particular state (specified in URL) i-e I don't want anything else to be visible to the user. Or you can say, I want to open a popup window rendering that particular state's html in popup window . This is the approach that comes to my mind to sort it out.
Ps. There are Rest APIs at the backend which I am calling through angular resource service to bind data with the model of the views
Option
I've rest APIs on backend, So, I was thinking to developing s separate Nodejs application, And I will send nodejs application url to the user and in the default/home route I'll call backend API and, the returned resultset will be rendered in html file within nodeJs application and this way, I'll render the corresponding data to user's browser window.
This is the flow for that
I don't know if that is right or clever approach. Please suggest me what will be the best approach to sort it out.
Thanks in advance.
This is what my app looks like
Everything in the left side-nav is a module and clicking on this I am routing to a different state. I am using angular-material and lots of other dependencies in this project.
And this is what I want.
I'll refer a link to the user for example www.myapp.com/specificpage.html. And hitting this url, a new tab/popup will be opened rendering state defined in the same app but with some non-editable url. And it should like.
There are multiple ways to achieve this and each approach has advantage and disadvantage. You have to choose depending on requirement and architecture. Details are below-
Create a separate app - You can do it through separate code base or use the module based build process and include this module only for new app.
Divide application is two part, public pages and private pages - Include this page and required APIs for this page in the public modules for your app.
Send token in the link - If you want to make secure page, send short lived token in the eMail and validate token on the server before displaying page to the user.
I am enhancing an ASP.NET MVC application with angularJS for selected pages. The primary means of navigating the site is still full page request, but some pages contain angular views and associated controllers for interactivity.
Currently, I achieve this by having something like this in the server side view:
<div ng-controller="ExampleCtrl" ng-include="exampleview.html"></div>
However, I would prefer to not inline controller and template definition like that and handle this through routing instead. However, angular routing only uses the client side of the URL (i.e. the parts after the first #). For various reasons, the page URL should remain http://domain.com/Example/Action instead of http://domain.com/Example/Action/#/example (this is just ugly) or http://domain.com/#/example (this will not work because I actually need the server side view as well).
Is there a way to make angular routing take the whole URL path into account instead of just the client part (and works in IE9)?
We are looking for some advice on handling URLs (and the state related to each URL) in a web application backed by a HATEOAS REST API, more specifically on
how to avoid having the web application URLs coupled with the REST API URLs
how to handle multiple resources in a single view
But let me first provide some more context:
We are building an Angular web application on top of a REST layer with Hypermedia constraint. (Note: I prefer simply using the term 'Hypermedia (constraint)' over HATEOAS).
As dictated by the Hypermedia constraint, the available actions and links in the application at any point in time are provided by the REST API. So the web application should not contain any hardcoded urls of the REST API, except for the 'root' (assuming that concept really exists in a REST API).
On the other hand, each page in the web application needs to be bookmarkable. So we cannot create a black-box application (with a single url and all state changes handled in the SPA without changing the URL). This means the web application also has its URL space, which needs somehow to be mapped to the REST API URL space. Which is already a conflict with the Hypermedia idea.
In the Angular application we use UI Router for handling application state. Here is how we got it working:
We only define states, no URLS
We defined a $urlRouterProvider.otherwise handler that will map the current web application URL to the corrsponding REST API URL, retrieve the representation of the resource that corresponds with that REST URL and pass it to the controller (in $stateParams).
The controller can then use the data (and links and actions) in the representation, just like it would if it would have made the REST call itself (or through a service)
So far so good (or not really) because there are some downsides on this approach:
The Web application URLs are mapped to the REST API URLs, so both URL spaces are coupled, which conflicts with one of the basic assumptions of using Hypermedia constraint: we cannot change the REST API URLs without having to change the web application.
In the $urlRouterProvider.otherwise handler we retrieve the representation of the current web app URL. But in some cases we have two resources in a single view (using UI Router nested states): for example a list of items and a detail of a single item. But there is only a single URL, so only the representation of the item detail is retrieved and the list of items remains empty.
So we would love to hear some suggestions on how we could improve on our approach in handling the two URL spaces. Is there a better way to make the REST API dictate the (available) behaviour of the web application and still have bookmarkable URLs in the webapplication? Because now we have some kind of hybrid approach that does not feel completely right.
Thanks in advance.
Regards,
Luc
that's a tough setup. Roughly you want bookmarks into your API, and RESTful systems somewhat discourage bookmarks.
One possible solution is a "bookmark service" that returns bookmark (bit.ly like) urls for the current resource being presents that are guaranteed to be fowards compatible because as you may change the canonical url structure, the bookmark service can always translate the bit.ly like url into the canonical url. Sounds complicated, but we see this all the time and we call them SEO urls eg: /product-name/ maps to products/ today, but may be /catalog/old-products/ tomorrow.
How you match that up to a UI that shows 2 resources the first being a list of summary like resources, and the second being a specific resource get's really tricky. I would expect such a page to know contain the state of what's it's displaying in it's url (probably in the fragment). As such since it's [likely] the controller that processing such commands it probably needs both (the list resource and the expanded resource) as input. I bet the url would look something like:
list=http://path/to/list/results&expand=http://self/link/of/path
So the last part you have is to make sure that works going forwards. Again this is the bookmark problem. What i may suggest if you don't want to build a bookmark service is that given you want to have such bookmarks you need to transition people to the new URLs. When a request is made to http://path/to/list/results and you want to switch that over you should be 301 redirecting them to the new canonical url and the app should be updating the bookmark. such a redirect can include the &flag=deprecate_message param to trigger the presentation in the UI that the client's bookmark is old and should be replaced. Alternatively the response can be internally forwarded and the deprecation flag & canonical (or latest) link included in the response to the old URL. This causes a phased transition.
In summary: I have yet to see HATEOAS be a cure all for backwards & forwards compatibility, but it's much better than the existing techniques. that said you must still make decisions in v1 of your API about how you want your users to move to v2.
I have a simple app built with AngularJS routes which is loading the controller and template for each path. I have a register form and login form on separates paths/templates. Say I go to the login form (/#/login) and enter my username/password, if I then hit "Register" (redirects me to /#/register), and then I hit back in my browser, it will return me to /#/login but the form will now be empty; the information I typed in has been removed.
Expected behaviour would be that the form data is still there.
Anyway to make that happen (without manually caching the data in a service)?
I'm guessing when the page changes, Angular is tossing the old template data and reloading the template again. Is there a way to instead cache that page template/DOM and reload it when the user returns to that path (instead of downloading and showing new template file)?
Well, this is a bit tricky. The browser should implement this kind of feature out of the box. Firefox started doing some work around this "issue" but I don't really know the current status of it.
Alternatively you can use a bit of javascript with LocalStorage to make this works. You're using AngularJS you can create a Directive that encapsulates this feature to be used on multiple places.
Basically you need to create a mechanism that translate an field to and unique-identifier and a value. Every time the user type on the field, you update the store. If the user "finish" the interaction on the form, you clean the value from the store.
You can also grab a jQuery plugin and just create a directive that uses the plugin.
https://github.com/kugaevsky/jquery-phoenix (never tested it).
TL:DR
There's nothing you can't do using a DOM property/attribute or something similar.
You'll need to get your hands dirty on some javascript to make this happen.