9

When I use ng-include as a title, how do I catch the error when the address (file path) does not exist?

I finished a ng-include router inside a ng-view(with ng-route), It's a little bit like this:

ContentCtrl:

var content = $route.current.params.content,
    tmplArr = content.split("_"),
    tmpl = {},
    personId=$route.current.params.personId||$scope.persons[0].id;
$scope.personId=personId;
tmpl.url = "content/";
for (var i = 0, len = tmplArr.length; i < len; i++) {
    tmpl.url += tmplArr[i] + "/";
}
tmpl.url = tmpl.url.substring(0, tmpl.url.length - 1) + ".html";
$scope.template = tmpl;

ContentView:

<div ng-include="template.url" class="ng-animate"></div>

when I use the addr is not exist like:/home/#/content/profile_asdfa, the angular just fetch the resource in a loop. So I need to catch the ng-include error,when there is no template file in the hash. Can anybody Help me ? Thx!

1
  • Not related to your question, but it might be better to not include the ng-animate class in the element. This is added (+ removed) automatically when Angular does its enter/leave animations, so it might cause slightly unpredictable behaviour. Commented Dec 30, 2013 at 10:50

1 Answer 1

14

Looking in the source for ngInclude, there seems to be no hook or way to detect directly a 404 (or other) error when the template doesn't exist. You might want to consider a feature request to add this, as it sounds like a useful feature.

However, right now you could do something with a http response interceptor. If there is some way to tell if a http reguest is for a template, say it is in the 'content' directory, you can intercept errors, and do something with them. For example you could replace the data with a custom directive, that then emits an event so controller(s) could respond to it.

The interceptor could be written like:

app.config(function ($httpProvider) {
    $httpProvider.interceptors.push('templateInterceptor');
});

// register the interceptor as a service
app.factory('templateInterceptor', function($q) {
  return {
    'responseError': function(rejection) {
       var isTemplate = !!rejection.config.url.match(/^content/g);
       if (isTemplate) {
         rejection.data = '<div><template-error url="\''+ (rejection.config.url) + '\'"><strong>Error from interceptor.</strong></template-error></div>';
         return rejection;
       } else {
         return $q.reject(rejection);
       }
    }
  }
});

So when there is an error after fetching something from the 'content' directive, it adds an element <template-error> in place of the template content. When this is compiled and then linked, it $emits a custom event, templateError, which parent controllers can respond to, by $scope.$on. So the directive can be code up like:

app.directive('templateError', function() {
  return {
    restrict: 'E',
    scope: {
      'url': '='
    },
    link: function(scope) {
      scope.$emit('templateError', {url:scope.url});
    }
  };
});

And then in the parent controller of the original ngInclude, you can react to this event:

$scope.$on('templateError', function(e, data) {
  $scope.templateError = true;
  $scope.templateErrorUrl = data.url;
})

You can see the full working code in this Plunker. Although I think this is slightly hacky, if the Angular team decide to add an $emited event to the code of ngInclude on error, then it should be easy to just remove the interceptor / your custom element.

Sign up to request clarification or add additional context in comments.

3 Comments

It seems your scheme can solve my problem, I will try it out later. Thx a lot : )
Can I assume the same solution will work when loading a ng-view template?
@ruben I don't see why not... You can always try it and see :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.