You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular.js中实现ui-router页面缺失时的Fallback跳转方案求助

Solution for ui-router Language Fallback & Code Duplication in AngularJS

Great question! You absolutely can implement this language fallback logic with a service—your approach was on the right track, but there were two key issues causing the fallbackService.checkLink is not a function error and unexpected behavior:

  1. The templateUrl function in ui-router doesn’t let you directly inject services as parameters. You need to retrieve the service via the $transition object’s injector instead.
  2. templateUrl doesn’t support asynchronous Promise returns (like using .then()), so your service needs to either use synchronous checks or you’ll need to pre-resolve the correct URL with ui-router’s resolve block.

Let’s walk through both solutions, starting with the simplest one for your local CMS use case.

Option 1: Synchronous Check Service (Simple for Local Environments)

First, create a reusable service that handles the synchronous URL existence check and fallback logic:

angular.module('yourAppName').service('FallbackService', function() {
  // Helper to sync check if a URL exists
  function urlExists(url) {
    var request = new XMLHttpRequest();
    request.open('HEAD', url, false); // Synchronous request
    request.send();
    return request.status === 200;
  }

  this.checkLink = function(lang, file) {
    const baseUrl = 'http://localhost:9000/docs/';
    const targetUrl = `${baseUrl}${lang}/${file}`;

    // 1. Check if target language file exists
    if (urlExists(targetUrl)) {
      return targetUrl.replace('http://localhost:9000', ''); // Return relative path for templateUrl
    }

    // 2. Fallback to the other language
    const fallbackLang = lang === 'jp' ? 'en' : 'jp';
    const fallbackUrl = `${baseUrl}${fallbackLang}/${file}`;
    
    if (urlExists(fallbackUrl)) {
      return fallbackUrl.replace('http://localhost:9000', '');
    }

    // 3. Neither exists—return 404 template
    return '/assets/partials/partial-404.html';
  };
});

Then update your route to use this service by retrieving it via $transition.injector():

.config(function ($stateProvider) {
  $stateProvider
    .state('docs', {
      url: '/docs',
      views: {
        '': {
          controller: 'docuCtrl',
          template: '<div class="row"><div class="col-sm-3"><div ui-view="tableContents"></div></div><div class="col-sm-9"><div ui-view="pageContents"></div></div></div>'
        },
        'tableContents@docs': {
          controller: 'docuCtrl',
          templateUrl: '/assets/partials/partial-toc.html'
        },
        'pageContents@docs': {
          controller: 'docuCtrl',
          templateUrl: '/assets/partials/partial-landing.html'
        }
      }
    })
    .state('docs.display', {
      url: "/:lang/:file",
      views: {
        'pageContents@docs': {
          templateUrl: function ($transition) {
            // Retrieve the service using the transition's injector
            const fallbackService = $transition.injector().get('FallbackService');
            const { lang, file } = $transition.params();

            if (file) {
              return fallbackService.checkLink(lang, file);
            }

            // Fallback to landing page if no file is specified
            return '/assets/partials/partial-landing.html';
          }
        }
      }
    })
})

Option 2: Asynchronous Check with Resolve (Better for Non-Local Environments)

If you want to avoid synchronous HTTP requests (which can block the UI), use ui-router’s resolve block to pre-fetch the correct template URL asynchronously:

.config(function ($stateProvider) {
  $stateProvider
    // ... keep your existing 'docs' state ...
    .state('docs.display', {
      url: "/:lang/:file",
      resolve: {
        resolvedTemplateUrl: function($transition, $http, $q) {
          const { lang, file } = $transition.params();
          const baseUrl = 'http://localhost:9000/docs/';

          // Helper to async check a URL
          const checkUrl = (url) => {
            return $http.head(url, { timeout: 1000 })
              .then(() => url.replace('http://localhost:9000', ''))
              .catch(() => $q.reject());
          };

          // First check target language, then fallback, then 404
          return checkUrl(`${baseUrl}${lang}/${file}`)
            .catch(() => checkUrl(`${baseUrl}${lang === 'jp' ? 'en' : 'jp'}/${file}`))
            .catch(() => '/assets/partials/partial-404.html');
        }
      },
      views: {
        'pageContents@docs': {
          templateUrl: function($transition) {
            // Retrieve the resolved URL from the transition injector
            return $transition.injector().get('resolvedTemplateUrl');
          }
        }
      }
    })
})

Key Fixes from Your Original Code

  • Service Injection: You can’t inject services directly into the templateUrl function—use $transition.injector().get('ServiceName') instead.
  • Synchronous vs Asynchronous: templateUrl expects a string return value immediately, so async Promises won’t work unless you pre-resolve the value in the resolve block.
  • 404 Fallback: Both solutions add the missing 404 redirect when neither language file exists.

内容的提问来源于stack exchange,提问作者PatrionDigital

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:22:08