CaribouCode
CaribouCode

Reputation: 14398

Check if file exists for ng-include in $routeProvider template

I've setup my Angular routes to use the slug from the URL to determine what file to load. It looks like this:

$routeProvider.when("/project/:slug", { 
  controller: "ProjectController",
  template: function($routeParams){ 
    return '<div id="project" ng-include="\'/views/' + $routeParams.slug + '.html\'"></div>'; 
  }
});

Works a charm, but there's no fallback here if the file for the ng-include doesn't exist. I thought about using resolve, but I can't seem to get it checking the file correctly. My attempt looks like this:

$routeProvider.when("/project/:slug", { 
  controller: "ProjectController",
  resolve: {
    check: ["$route", "$http", "$location", function($route, $http, $location){
      $http.get("/views/" + $route.current.params.slug + ".html").then(function(res){
        if (!res.data) $location.path("/");
        else return true;
      });
    });
  },
  template: function($routeParams){ 
   return '<div id="project" ng-include="\'/views/' + $routeParams.slug + '.html\'"></div>'; 
  }
});

It's probably worth noting that this is part of a single page application on a MEAN stack. I have my Express routing in NodeJS set up like so:

app.get("*", function(req, res){ res.sendFile(__dirname + "/public/index.html"); });

Upvotes: 0

Views: 1339

Answers (2)

CaribouCode
CaribouCode

Reputation: 14398

It turns out the issue was because I was routing all file requests to my index.html in my NodeJS. So when I tried an $http call to check if a .html view exists, it always returned the index.html and therefore true. The solution was as follows in Node:

app.use(express.static(__dirname + "/public", { maxAge: 86400000 }));
app.get("/framework/*",function(req,res){ res.sendFile(__dirname + "/public" + req.path); });
app.get("/views/*",function(req,res){ res.sendFile(__dirname + "/public" + req.path); });
app.get("*", function(req, res){ res.sendFile(__dirname + "/public/index.html"); });

Then I can use resolve in my Angular routing like so:

$routeProvider.when("/projects/:slug", { 
  controller: "ProjectController",
  resolve: {
    check: ["$route", "$http", "$location", function($route, $http, $location){
      return $http.get("/views/" + $route.current.params.slug + ".html").success(function(res){
        return true;
      }).error(function(res){
        return $location.path("/");
      });
    }]
  },
  template: function($routeParams){ 
    return '<div id="project" ng-include="\'/views/' + $routeParams.slug + '.html\'"></div>'; 
  }
});

So this issue was specific to a single page application setup.

Upvotes: 1

Mark Polak
Mark Polak

Reputation: 403

You can return a promise from the resolve function. Your example currently fires the request and then returns undefined.

I have not tested the following, but it should return a promise. (This promise will fail when either, the request fails or the data property on the response is falsy.

$routeProvider.when("/project/:slug", { 
  controller: "ProjectController",
  resolve: {
    check: ["$route", "$http", "$q", function($route, $http, $q){
      return $http.get("/views/" + $route.current.params.slug + ".html")
            .then(function(res){
                if (!res.data) {
                    return $q.reject('No data');
                }
                return $q.resolve('Found');
            });
    }];
  },
  template: function($routeParams){ 
   return '<div id="project" ng-include="\'/views/' + $routeParams.slug + '.html\'"></div>'; 
  }
});

For the cases where the promise is rejected. (The request failed or there is no data) You could subscribe to the $routeChangeError event to handle that case and locate the browser to the / route.

Upvotes: 0

Related Questions