Angular directive templateUrl trying to GET from Express - angularjs

I'm trying to get a third-party Angular directive (ngCart) to work with my Angular application. It seems like the ngcart directives are looking for a templateUrl as a relative path to the directive, and instead it seems like Express is trying to serve up the files instead of the directive finding the HTML file at the relative templateUrl path.
Project structure
projectName
| - bower_components/
| - node_modules/
| - public/
| - css
| - index.html
| - app.js (angular app)
| - src/
| - server.js (express app)
| - routes/
| - models/
| - package.json
server.js
...
app.use("/", express.static("public"));
app.use('/bower_components', express.static(__dirname + '/../bower_components'));
app.listen(8000, function () {
console.log("App is listening");
});
third-party directive file causing issues:
templateUrl: function(element, attrs) {
if (typeof attrs.templateUrl == 'undefined') {
return 'template/ngCart/addtocart.html'; // This is the relative path that's freaking out
} else {
return attrs.templateUrl;
}
}
The console error I'm getting is:
GET http://localhost:8000/template/ngCart/addtocart.html 404 (Not Found)
It seems like express is trying to serve up the HTML file in the template folder inside the third-party directive, but I just want it to be a relative path to the HTML file without express being involved.

As pointed out by #Claies in the comments, someone brought this concern up with the creator of ngCart (thread found here), to which he responded saying that you basically are supposed to copy the template files into a folder called "template" in your main public directory. It isn't documented anywhere in the ngCart documentation though, so hopefully this can help someone in the future.
It appears this is the most common way to bring in third-party angular directives that have their own template files.

Update:
I'm no expert in AngularJS, I've been fighting this exact issue with a programming buddy all afternoon.
Eventually we tripped across another question on SO which describes a vastly more elegant solution.
(I've also commented on the GitHub issue tracking page, for future reference.)
In short:
<ngcart-summary template-url="/libraries/ngCart/template/ngCart/summary.html"></ngcart-summary>

Related

Is it possible to define AngularJS route's templateUrl path fully agnostic to IIS app folder structure?

I have have done some research here and in general, but couldnt find a proper way to implement this.
We have and MVC/WebAPI2/AngularJS Web app.
I am trying to achive this goal: being able to put my app anywhere in IIS app folder structure without any code changes. That means, get rid of any IIS Application name in my code.
Here is a simple fragment of ng-route path defined:
.when("/", { templateUrl: "Home/Main", controller: "MainController",
controllerAs: "vm" })
Now, the IIS app is called ReportsWeb (but could be any name)
If I set ReportsWeb app under Default Web Site root:
http://localhost/ReportsWeb/
then everything works fine, and templateUrl page is loaded fine.
But if the app is placed under another IIS App (the one we have here is called RealSuiteApps, which is itself under Default Web Site)
http://localhost/RealSuiteApps/ReportsWeb/
then, the templates are not loaded, since constructed URL for templateUrl is no longer valid:
http://localhost/RealSuiteApps/Home/Main
If I change templateUrl to "/Home/Main", constructed URL is not valid again
http://localhost/Home/Main
If I change templateUrl to "./Home/Main", constructed URL is not valid again
http://localhost/RealSuiteApps/Home/Main
Is it possible to define templateUrl only relative to the latest App name in the IIS hierarchy without hardcoding all these names, so that it will work in any IIS app tree
http://localhost/App1/App2/App3/ReportsWeb/
Thanks!
I use grunt-html2js tool to convert all html templates to Javascript at build time, which uses Angular's template cache.
I've defined my grunt task to overwrite the default module naming to templates/ so that I can reference any template easily:
html2js: {
options: {
rename: function (moduleName) {
return moduleName.replace(/^.*[\/\\]/, 'templates/');
}
},
all: {
src: ['./src/**/*.html', '!./src/index.html'],
dest: 'tmp/templates.js'
}
},
This will create a templates.js file that I can include as a script tag. Then I can access any template like so:
templatUrl: 'templates/my-template-filename.html'
Regardless of where it's path was in the source code.

Serve up web files from different directory - Error 400 Bad Request

I have an MVC app that uses AngularJS. I am in the process of organizing the folder structure to be feature based rather than type based.
An example folder structure is now
App (Folder within main project, at same level as Views)
Assets
CSS
JS
Pages
Home
home.js
home.tests.js
home.html
About (etc)
Now the problem is that when ui-router tries to load home.html the web server throws a 400 error. If I go to the file manually in the browser it works. What is the problem here? Why can I not serve files up from this directory. Note that I actually right click the 400 message in the console and choose open in new tab. So I know the file I am looking at is the URL being requested from AngularJS
Here is my ui-router setup
.state('home', {
url: '/home',
templateUrl: '/app/pages/home/home.html',
controller: 'mainPageController'
})
Note:
I serve up a .js file from the same directory which seems to work fine!
I had the exact same problem and it was caused by another developer trying to fix an IE caching bug.
I found the solution here by Ben Cull: https://stackoverflow.com/a/30014936/710268.
//initialize get if not there
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
}
//disable IE ajax request caching
$httpProvider.defaults.headers.get["If-Modified-Since"] = "Fri, 01 Jan 2010 00:00:00 GMT";
My problem was that the "If-Modified-Since" header was set to '0' and for some reason that was causing html template files to not load.
By default the router is going to start at the top level of your MVC project (i.e. where your Views and App folder sit) and not the folder where your JavaScript is served from.
So in Angular your templateUrl needs to be App/Pages/Home/home.html.
So in your angular router configuration have the property:
templateUrl: 'App/Pages/Home/home.html'

Apache FallbackResource and request path behaviour

I'm making a simple website with angularjs on a Apache 2.4 webserver. My website folder looks like this:
APACHE24\HTDOCS\TEST
| .htaccess
| index.html
|
+---js
| angular-route.js
| angular.min.js
| app.js
|
\---partials
show-gem.html
I setup some routes, like these ones:
$routeProvider.when("/gem/:gemId", {
controller: "ShowGemController",
controllerAs: "showGemCtrl",
templateUrl: "partials/show-gem.html"
});
And i enabled FallbackResource /test/index.html into my .htaccess file.
Question:
Whenever I manually enter the URL I get the right page (e.g. I write http://localhost:8080/test/gem/123 and I get the corresponding page), but how can this happen? Shouldn't I be redirected to index.html since /test/gem/123 is not an existing resource? And how can angular know it should display that particular template?
FallbackResource is, essentially, a short-hand method for rewriting your URIs. As such, there won't be any redirection to index.html, but the URI you request will be mapped to index.html, where Angular can process it.
For more information, refer to the following article:
http://fideloper.com/apache-fallbackresource
I've always preferred to use mod_rewrite to handle this, as it's a lot more fine-grained, and gives you control.

How do you point to an AngularJS Template from within a JS file provided by a Grails plugin?

I have a Grails app which pulls in several Grails Plugins. These plugins need to be reused by several other Grails apps not just my own.
In a Resources Bundle in one of the Grails Plugins I have the following defined:
leaving this for completeness, but I have since switched to the Asset-Pipline
modules = {
directivea {
resource url: 'directives/directivea/directivea.js'
resource url: 'services/restapis.js'
}
}
In a Javascript file in one of the plugins I have the following directive defined:
ModuleA.directive('directivea',function() {
return {
restrict: 'EA',
scope: {
'objId' : '='
},
replace: true,
link: function(){
$scope.ObjId = attributes[''];
$scope.someFunction($scope.ObjId);
},
controller: function(){
$scope.someFunction = function(objId){
//some stuff happens here
};
},
templateUrl: 'directives/directivea/directivea.html'
}
});
It seems to be executing the controller just fine but when it tries to pull in the template it chokes on:
GET /appname/directives/directivea/directivea.html
404 (Not Found)
This makes sense because that's not where the partial template will be. Where would it be though? How can I keep that information isolated to within the plugin but usable by all the downstream applications? I'd like to avoid defining in-line templates if I can.
**EDIT TO INCLUDE STRUCTURE**
Structure of Plugin:
grails-app
conf
PluginNameForGrailsResources.groovy
BuildConfig.groovy
controllers
PluginControllerA.groovy
PluginControllerB.groovy
domain
PluginDomainA.groovy
web-app
css
directives
directivea
directivea.js
directivea.html
services
restapis.js
application.properties
PluginNameForGrailsGrailsPlugin.groovy
Structure of the application referencing my Plugin:
grails-app
conf
BuildConfig.groovy
controllers
domain
views
layouts
main.gsp
index.gsp (references to directivea via resources r:require tag)
web-app
css
main.css
js
index.js
application.properties
**EDIT TO INCLUDE STRUCTURE POST-ASSET-PLUGIN-SWITCHOVER**
no more Resources defined, BuildConfig.groovy now references the asset pipline instead of Resources, no more files in web-app
grails-app
conf
BuildConfig.groovy
controllers
PluginControllerA.groovy
PluginControllerB.groovy
domain
PluginDomainA.groovy
assets
javascripts
directivea
directivea.js
directivea.html
services
restapis.js
application.properties
PluginNameForGrailsGrailsPlugin.groovy
Using the Grails Resource plugin
Refer this sample grails app which uses this grails plugin.
Plugin hosts an angular module. I tried to keep it simple by just adding a directive and no other module. Also note that I have used static/.. in the templateUrl in directive.
Also refer Config.groovy in the app which uses below config
grails.resources.adhoc.includes = ['/images/**', '/css/**', '/js/**', '/img/**']
as mentioned in the answer to this question. May be you are hitting this issue.
Give it a try. "Hello World" is rendered from the directive present in an angular module inside the plugin.
If you are using latest version of Grails then I would suggest to switch to asset pipeline whenever convenient.
Using the Grails Asset Plugin
Use the Grails asset-pipeline plugin (v 1.9.7 as of this posting) and the Grails Angular-template-asset-pipeline plugin (v 1.2.5 as of this posting).
For details on exactly how to set this up, see the answers to How to reference a static HTML resource using the Grails Asset Pipeline plugin.
Primarily, ensure your Angular JS module is lowercase or uses _'s or -'s in the name and that your template file has a slightly different filename (ignoring the extension) than any of the javascript assets you're including.
Note that the javascript is referencing things from the web-app root, not from the resources or assets directory. You can find more about Angular routing from the core documentation: $route
So templateUrl: directives/directivea/directivea.html needs to actually reference a file in the /web-app/directives/directivea/directivea.html directory.
Note that the resources plugin does not seem to be very modular, and global files need to be specified for sharing (and most other things). If you move to grails 2.4 and the assets plugin, you can do modular file paths.

AngularJS and PhoneGap: $location.path causes subsequent tempateUrl lookup to fail

I'm having trouble getting path lookup to work with a AngularJS v1.2.0 and PhoneGap/Cordova Android application. I've come pretty far with html5mode(true) by setting <base href="."/> in index.html and then changing $routeProvider.when('/') to $routeProvider.when('/android_asset/www/index.html'). After that I am able to get redirectTo('login') to reach $routeProvider.when('/login') and there render templateUrl: 'static/partials/login.html' as expected.
The problem I have is that if I instead try to redirect to the login page from my Javascript code with $location.path('/login');, the route is found but templateUrl loading fails with an insecurl exception.
I've tried whitelisting access to file:// by using the new angular-sanitize module, but that does not help.
How can I make $location.path() do the same things as redirectTo so that the partial is loaded? Or is there some other way to solve this problem?
UPDATE: I got a bit forward by adding a call to replace() after the path function, e.g.:
$location.path('/login').replace();
but that seems like a hack, and it still causes the templateUrl in the otherwise route to fail with the same exception.
Any ideas on what might be wrong? Is it that html5mode(true) just does not work at this moment with Phonegap and the only way to fix this is to set it to false and add hashtags to every path (like is done in the angular phonegap seed project)?
For future reference, this is how I managed to solve the problem:
AngularJS currently does not seem to support html5mode(true) inside a Cordova application because of the insecurl problem I reported. What I had to do is add
var h5m = (typeof html5Mode !== 'undefined') ? html5Mode : true;
$locationProvider.html5Mode(h5m);
which gives me the possibility to explicitly set html5Mode in the PhoneGap index.html with a global variable:
<script>
var html5Mode = false;
</script>
So now $location.path('/login') as well as redirectTo: 'login' works, but links in html files, don't. To get those working in PhoneGap, with html5Mode disabled, I had to add #/ in front of every link, e.g. login.
That makes PhoneGap work, but breaks the web page which uses History API with html5Mode(true). The last piece of the puzzle was to add <base href="/"/> to the web page's index.html (and leave it out of the index.html of the PhoneGap project.) So now even though I have a link that says #/login in the web page, I get to the url http://example.com/login and don't see any hashes in the address bar.
**
So in the end I have History API working in my web page and History API disabled in the PhoneGap project (where there really is no need for History API as there is no address bar). The only downside is the extra #/ I have to put in each template html file, but that is a minor annoyance compared to the ability to use all of the same html and javascript files for both web and mobile.
I had this same problem as well. I managed to fix it by skipping the leading slash in the route config:
$routeProvider
// route for the foo page
.when('/foo', {
templateUrl: 'foo.html', //previously: '/foo.html'
controller: 'fooController'
}) //etc.

Resources