Kim George
Kim George

Reputation: 81

Angular.js Directive templateUrl not found on Mean.js stack

I've created my first directive in Angular running on the mean.js 0.4-dev stack vertical project file structure which I auto generated with Yeoman.

The directive compiles correctly when I use the 'template:' property however when I try to move the contents to a template file and use the 'templateUrl:' property it loads the un-compiled 'layout.server.view.html' file instead of my 'contact-form.client.template.html' file. It seems like it can't find my template file no matter what I set the path to (I tried all paths up the tree).

Can anyone see what I'm doing wrong. Perhaps someone can explain how angular resolves relative paths.

My program structure is like this... I generated a module for a contact form using the 0.4-dev of the generator-mean. The module contains my directive. My file structure is like this:

/app
  /config
  /modules
    /contact-forms
      /client
        /config
        /controllers  
          contact-forms.client.controller.js
        /views
          contact-form.client.template.html <- 2. should load this
        contact-forms.client.module.js
    /core
      /client
        /views
          home.client.view.html <- 1. <contact-form> directive here
      /server
        /controllers
        /routes
        /views
          layout.server.view.html <- 3. instead loads this
    /node_modules
    /public
    /uploads

My directive code is this:

contactForms.directive('contactForm',[function(){
return {
    restrict: 'E',
    transclude: 'true',
    //template: '<div>hello world!</div>', <--this works
    templateUrl: 'modules/contact-forms/client/views/contact-form.client.template.html'
};
}]);

And my template file is like this:

<div>
<form class="row col-md-6 col-md-offset-3 text-left">
    <div class="form-group col-md-12">
        <label for="Name">Name</label>
        <input type="text" class="form-control" id="inputName">
    </div>
    <div class="form-group col-md-12">
        <label for="email">Email</label>
        <input type="email" class="form-control" id="inputEmail">
    </div>
    <div class="form-group col-xs-12">
        <label for="emailSubject">Subject</label>
        <input type="text" class="form-control" id="inputSubject">
    </div>
    <div class="form-group col-xs-12">
        <label for="emailMessage">Message</label>
        <input type="text" class="form-control" id="inputMessage">
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>

I've seen a few posts on this type of issue however none of them seem to match my use case. I'm running a local express server so I don't think this post is the issue (Couldn't load template using templateUrl in Angularjs).

This post talks about templateUrl being spelt wrong but I think mine is correct (Angular directive templateURL not being loaded. Why?)

I understand from this post on the angular github (https://github.com/angular/angular.js/issues/10965) that the default behavior of angular.js is to load the index file in the event of not finding a template. The comment from feb 4 suggests this is how it has to be. There is no errors in my browser console so I'm not really sure whats happening.

My node.js routing hasn't been changed from the Yeoman install of the mean.js stack:

module.exports = function(app) {
// Root routing
var core = require('../controllers/core.server.controller');

// Define error pages
app.route('/server-error').get(core.renderServerError);
app.route('/not-found').get(core.renderNotFound);

// Define application route
app.route('/*').get(core.renderIndex);
};

and:

'use strict';

/**
 * Render the main application page
 */
exports.renderIndex = function(req, res) {
    res.render('modules/core/server/views/index', {
        user: req.user || null
    });
};

/**
 * Render the server error page
 */
exports.renderServerError = function(req, res) {
    res.status(500).render('modules/core/server/views/500', {
        error: 'Oops! Something went wrong...'
    });
};

/**
 * Render the server not found page
 */
exports.renderNotFound = function(req, res) {
    res.status(404).render('modules/core/server/views/404', {
        url: req.originalUrl
    });
};

Do i need to add a route for the template?

Upvotes: 4

Views: 1695

Answers (1)

Kim George
Kim George

Reputation: 81

I figured out why it's not finding the template. In mean.js 0.4.0 the express configuration file has a static routing function which removes the /client from the paths

/**
 * Configure the modules static routes
 */
module.exports.initModulesClientRoutes = function (app) {
    // Setting the app router and static folder
    app.use('/', express.static(path.resolve('./public')));

    // Globbing static routing
    config.folders.client.forEach(function (staticPath) {
        app.use(staticPath.replace('/client', ''), express.static(path.resolve('./' + staticPath)));
    });
};

So my line:

templateUrl: 'modules/contact-forms/client/views/contact-form.client.template.html'

should be:

templateUrl: 'modules/contact-forms/views/contact-form.client.template.html'

Which seems to work. No idea why they remove this /client from the path. There is a reference to it on this issue (https://github.com/meanjs/mean/issues/608) however it doesn't seem to mention exactly why they do it.

If you want to remove this behavior from mean.js you can do the following (Warning: The yeoman meanjs:vertical-module generator creates all paths without the client so either don't use it or correct its path output after creating each module.):

Remove the replace() function in express.js so it looks like this:

/**
 * Configure the modules static routes
 */
module.exports.initModulesClientRoutes = function (app) {
    // Setting the app router and static folder
    app.use('/', express.static(path.resolve('./public')));

    // Globbing static routing
    config.folders.client.forEach(function (staticPath) {
        app.use(staticPath, express.static(path.resolve('./' + staticPath)));
    });
};

For each module in the project add a /client after the module name in every static path. I did this in a brute force fashion however I'm sure there's a smarter way.

In the config.js file remove the /client masks from lines 121 and 124 so they look like the following:

    // Setting Globbed js files
    config.files.client.js = getGlobbedPaths(assets.client.lib.js, 'public/').concat(getGlobbedPaths(assets.client.js, 'public/'));

    // Setting Globbed css files
    config.files.client.css = getGlobbedPaths(assets.client.lib.css, 'public/').concat(getGlobbedPaths(assets.client.css, 'public/'));

Seems to work and doesn't appear to be any issues so far I'll comment if I run into any issues.

Upvotes: 3

Related Questions