Resolving relative $resource path in Angular 1.5 - angularjs

My Angular 1.5.8 web application is at http://www.example.com/foo/ and my RESTful list of bars is at http://www.example.com/foo/api/bars. When ui-router goes to the list of bars in Angular, my browser is at http://www.example.com/foo/#/bars. I simply want to connect to a RESTful resource to connect to http://www.example.com/foo/api/bars using HTTP GET.
So I try the most obvious thing:
$resource("api/bars/")
In my mind that should resolve api/bars/ to the current path http://www.example.com/foo/#/bars to give me http://www.example.com/foo/api/bar, right? But it doesn't work (even though I could swear it worked with $http). Instead it gives me a $resource:badcfg error.
Angular lets me do the following, but this doesn't produce the correct path, instead giving me http://www.example.com/api/bars:
$resource("/api/bars/")
So I try $browser.baseHref(), but that seems to be set to the empty string (why?), producing http://www.example.com/api/bars again.
$resource($browser.baseHref() + "/api/bars/")
I tried to use $location.path(), but Angular thinks I want the path after the hash sign, so that doesn't work, giving me http://www.example.com/bars/api/bars. Doh!
$resource($location.path() + "/api/bars/")
How can I simply resolve api/bars/ to the base path of the current URL http://www.example.com/foo/#/bars, producing http://www.example.com/foo/api/bars (or even just the path)?
Note that it is not acceptable for me to hard code an absolute path, or to change my HTML code. Why should I? Besides, this application will be mounted at two places on the same server (hey, it's easy with Java, Tomcat, and JAX-RS), at http://www.example.com/foo/ and http://www.example.com/foo-demo/. The application should run unmodified under each location, accessing the RESTful API list of bars at http://www.example.com/foo/api/bars and http://www.example.com/foo-demo/api/bars, respectively.
This is not complicated; this is simple "URI syntax 101" which has been outlined in RFCs for over two decades. I simply want to resolve api/bars/ to the base path of the current URL http://www.example.com/foo/#/bars. How do I do that in Angular 1.5?

OK, there's two ways to do this. If you absolutely want an absolute path, you can use this:
$resource(location.origin + location.pathname + "api/bars/")
Note the use of the location object location.origin to establish the protocol, domain, and port; and location.pathname to establish the base path. (Once again Microsoft will throw a wrench into the works: location.origin doesn't work on IE11 on some versions of Windows 10.)
But the better approach is simply to use:
$resource("api/bars")
"Wait, I thought you said that this will give you an error!" you might exclaim. Well it turns out I was wrong about that. I was indeed getting an error, but it was because I hadn't set the Accept header to ask for JSON, and the plain/text response I was getting (that's the default on my RESTful resource) was confusing $resource. It turns out that a relative URI will work after all.

It is possible to use relative-path syntax for $resource as you can use it with $http which is used by $resource. This can be done by omitting the leading slash (as you suggested) or by prefixing the resource with ./.
Your badcfg error seems more likely to be the case because your request works out correctly but the server response does not match the expectations. So probably you are returning a single object instead of an array. Please use the dev tools to debug what is really returned from the server.
See the docs on $resource for details.

Related

What is the equivalent of document.location.pathname in angular's $location?

There is a mismatch between document.location.pathname and $location.path().
I have an angular SPA running out of something like http://myhost.com/services/coolstuff.
When you navigate to that location, I have some router rules to navigate you to something like: http://myhost.com/services/coolstuff#/users/1.
At this point, you can call $location.path() and it will give you /users/1. And document.location.pathname gives you /services/coolstuff.
Is there a mechanism to retrieve the actual URL path from $location? I don't want the Angular hash path (/users/1), but the actual URL path (/services/coolstuff). How can I get that?
A bit late to the party here, but the parsing section of the URL specification is very specific about this (and super tedious to read...):
The gist of the above specification is, that an unencoded # character denoted the start of the URL fragment, which is not part of the path.
This essentially means, that $location.path() behaves differently from what the URL specification defines. An implementation compliant with the spec has to return /services/coolstuff as document.location.pathname consequently does.
In general, if something is supported in native JS, I would always opt for that, since you can be sure (as you see from your example) that browser implementations adhere to the specified standards which is often not the case for third party libraries.

Using Angular Mock Backend resource multiple times

I am trying to do backend less development in Angular while working disconnected from the backend resources.
Most functionality works fine, but if I try to use any resource a second time I get an error:
Error: Unexpected request: GET /localPTicket?ticket=123
No more request expected
The scenario I am mocking is one where, for every request to a backend service, I have to first make a Get call to get a valid Proxy Ticket, the response from this is then passed to the next API call.
I have set up a plunker that demonstrates the issue:
https://plnkr.co/edit/KKa6MXcnbK1gcMiBB7MI?p=preview
I think that the issue is related to flushing the mock requests, but my understanding of the documentation is that using ngMockE2E this should not be an issue.
Thanks for any pointers!
Les
It's because your are using global regexes.
Global regexes in JavaScript can be very confusing since they have a state. The first time you call it it returns the first match in the string, the second time you call it it returns the next match in the string. If there are no more matches it will return that there were no matches and reset its state.
Simply remove the g from the end of your regexes and it should behave as you expect.

Marionette Router Query String Parameters In URL Fragment Routes

I'm working on a project that requires that most UI state is reproducible via URL. In a traditional (server-side) app, I could use both URL parameters like:
/resources/:id
and unordered optional query string parameters, like:
/resources/:id?page=5&sort=date
Is there an idiomatic way to achieve this with Backbone/Marionette routing? I don't want to have to configure routes for every possible combination of parameters.
The fact that I don't see this addressed much makes me think I may be barking up the wrong tree, approach-wise, but I do think being able to represent as much UI state as possible in the URL is pretty important to a lot of projects.
It looks like the best option is the now-orphaned backbone-query-parameters project.
It supports routes exactly in the form I'm looking for:
#resources/:id?flag=true
URL parameters are not really enforced by Backbone/Marionette. One possible reason is that URL parameters are not SEO friendly.
Instead, you can configure optional URL fragments which will work pretty much like URL parameters, this way:
/resources/:id(/page/:page)(/sort/:sort)
If you do this way, the only gotcha here is that this sequence of "parameters" need to be ordered.
HOWEVER if you need it to be unordered, you can simply use Regular Expressions with router.route() method inside your initialize, as explained in Router#route

Dereferencing objects with angularJS' $resource

I'm new to AngularJS and I am currently building a webapp using a Django/Tastypie API. This webapp works with posts and an API call (GET) looks like :
{
title: "Bootstrap: wider input field - Stack Overflow",
link: "http://stackoverflow.com/questions/978...",
author: "/v1/users/1/",
resource_uri: "/v1/posts/18/",
}
To request these objects, I created an angular's service which embed resources declared like the following one :
Post: $resource(ConfigurationService.API_URL_RESOURCE + '/v1/posts/:id/')
Everything works like a charm but I would like to solve two problems :
How to properly replace the author field by its value ? In other word, how the request as automatically as possible every reference field ?
How to cache this value to avoid several calls on the same endpoint ?
Again, I'm new to angularJS and I might missed something in my comprehension of the $resource object.
Thanks,
Maxime.
With regard to question one, I know of no trivial, out-of-the-box solution. I suppose you could use custom response transformers to launch subsidiary $resource requests, attaching promises from those requests as properties of the response object (in place of the URLs). Recent versions of the $resource factory allow you to specify such a transformer for $resource instances. You would delegate to the global default response transformer ($httpProvider.defaults.transformResponse) to get your actual JSON, then substitute properties and launch subsidiary requests from there. And remember, when delegating this way, to pass along the first TWO, not ONE, parameters your own response transformer receives when it is called, even though the documentation mentions only one (I learned this the hard way).
There's no way to know when every last promise has been fulfilled, but I presume you won't have any code that will depend on this knowledge (as it is common for your UI to just be bound to bits and pieces of the model object, itself a promise, returned by the original HTTP request).
As for question two, I'm not sure whether you're referring to your main object (in which case $scope should suffice as a means of retaining a reference) or these subsidiary objects that you propose to download as a means of assembling an aggregate on the client side. Presuming the latter, I guess you could do something like maintaining a hash relating URLs to objects in your $scope, say, and have the success functions on your subsidiary $resource requests update this dictionary. Then you could make the response transformer I described above check the hash first to see if it's already got the resource instance desired, getting the $resource from the back end only when such a local copy is absent.
This is all a bunch of work (and round trips) to assemble resources on the client side when it might be much easier just to assemble your aggregate in your application layer and serve it up pre-cooked. REST sets no expectations for data normalization.

How do I send a more complex object in angular.js?

http://jsfiddle.net/EjyW4/
Essentially, I am trying to post an array of objects from the client using AngularJS with the resources module, and instead of sending a JSON object, Angular is sending a useless toString representation over the wire.
Unfortunately, the code in the fiddle itself doesn't do much -- the intent is outlined here with more context, though it still is very raw and do not yet resemble anything looking like the right way) But this seems to be an angular issue rather than grails, at least from looking at the Chrome console.
Query String Parameters:
callback:JSON_CALLBACK
tests:%5Bobject+Object%5D,%5Bobject+Object%5D
There seems to be an angular.toJson -- http://docs.angularjs.org/api/angular.toJson -- but it doesn't seem to work in this case. The documentation I've seen doesn't seem to cover more than sending a basic int. If I have to, I'll send over a comma separated string, but this seems like it should be a common use case.
The $resource function actually returns a new $resource object constructor, which you then set properties on, then call methods like save on.
So your problem in your fiddle is you're trying to save a $resource with no data set on it! All you have is a config property, tests, which it doesn't know what to do with.
You instead want to:
Set up your constructor for a new resource using $resource factory/method.
Create a new instance of your new resource.
Set a property on it (eg myNewResource.tests = $scope.tests);
Save it (myNewResource.$save())
http://jsfiddle.net/EjyW4/2/
It looks like what you were trying to do originally is better suited for $http (I put an example of that in the fiddle too).

Resources