Angular"状态已定义"运行concat / uglify后出错

时间:2015-06-17 10:08:47

标签: angularjs

我正在开发一个AngularJS应用程序。为了在生产中发布代码,我使用了这个Grunt配置/任务:

  grunt.registerTask( 'compile', [
    'sass:compile', 'copy:compile_assets', 'ngAnnotate', 'concat:compile_js', 'uglify', 'index:compile'
  ]);

调试起来真的很难,对于那些已经遇到过这些问题并且可以指向某个方向的人来说,这是一个问题。

我的主要模块包括那些子模块:

angular
  .module('controlcenter', [
    'ui.router',
    'ui.bootstrap',
    'templates-app',
    'templates-common',
    'authentication',
    'api',
    'reports',
    'interceptors',
    'controlcenter.websites',
    'controlcenter.users',
    'controlcenter.campaigns',
    'controlcenter.reports',
    'controlcenter.login'
  ])
  .run(run);

我得到的错误如下:

Uncaught Error: [$injector:modulerr] Failed to instantiate module controlcenter due to:
Error: [$injector:modulerr] Failed to instantiate module controlcenter.websites due to:
Error: State 'websites'' is already defined
  

如果我删除网站模块,我会收到相同的错误   controlcenter.users。

我正在使用ui-router来处理应用内的路由。

在我的构建过程(用于集成测试)之后,一切正常:

  grunt.registerTask( 'build', [
    'clean', 'html2js', 'jshint', 'sass:build',
    'concat:build_css', 'copy:build_app_assets', 'copy:build_vendor_assets',
    'copy:build_appjs', 'copy:build_vendorjs', 'copy:build_vendorcss', 'index:build', 'karmaconfig',
    'karma:continuous'
  ]);

所以也许ngAnnotate或或concat / uglify在这里做了奇怪的事情?

更新1: 它与我的模块配置有关。这是代码:

angular
  .module('controlcenter.websites',
    [
      'ui.router'
    ]
  )
  .config(config);

config.$inject = ['$stateProvider'];

function config($stateProvider) {
  $stateProvider.state( 'websites', {
    url: '/websites',
    views: {
      "main": {
        controller: 'WebsitesController',
        templateUrl: 'websites/websites.tpl.html'
      }
    }
  });
}
  1. 当我将状态名称更改为websites_2时,出现错误 使用'网站_2已定义'。
  2. 当我完全删除模块时,下一个模块在配置文件中存在同样的问题。结构错了吗?
  3. 更新2

      

    这个问题似乎与此有关。

    它需要每个JS文件并将其一个接一个地添加到一个更大的文件中。我的所有模块都在最后。最后一个模块始终存在已定义的状态问题'。所以它不仅仅是相互附加的模块的顺序,还有其他的东西......

    更新3: 我在gist中放置了我的代码(我已经排除了每个控制器代码和函数,只是脚手架)。这是我编译过程之后的结果,没有弄清楚它。

3 个答案:

答案 0 :(得分:19)

<强>问题:

您有多个文件包含config功能来配置您的模块,如下所示:

angular
    .module('controlcenter.websites', [])
    .config(config);

function config() {
    // ...
}

问题在于,在连接所有文件后,最终会得到一个包含多个config声明的大文件。由于JavaScript的变量提升,所有声明都被移到顶部,只评估它们中的最后一个,这个是:

function config($stateProvider) {
  $stateProvider.state( 'websites', {
    url: '/websites',
    views: {
      "main": {
        controller: 'WebsitesController',
        templateUrl: 'websites/overview/websites.tpl.html'
      }
    },
    data : {requiresLogin : true }
  });
}

因此,每次.config(config)模块时,您都在告诉Angular使用该特定配置函数配置模块,这意味着它会多次执行并尝试定义状态websites以上一次。

<强>解决方案:

使用闭包来包装每个JavaScript文件代码。这样您就可以避免多次声明变量/函数:

(function (angular) {

    'use strict';

    angular
      .module('controlcenter.website.details', ['ui.router'])
      .config(config);

    config.$inject = ['$stateProvider'];

    function config($stateProvider) {
      $stateProvider
        .state( 'websiteDetails', {
          url: '/websiteDetails/:id',
          views: {
            "main": {
              controller: 'WebsiteDetailsController',
              templateUrl: 'websites/details/website.details.tpl.html'
            }
          },
          data : {requiresLogin : true }
        })

        .state( 'websiteDetails.categories', {
          url: '/categories',
          views: {
            "detailsContent": {
              templateUrl: 'websites/details/website.details.categories.tpl.html'
            }
          },
          data : {requiresLogin : true }
        })
        ;
    }
})(window.angular);

修改

  • 我强烈建议您将文件包装成闭包。但是,如果您仍然不想这样做,可以根据各自的模块命名您的功能。这样,controlcenter.website.details的配置功能将变为controlcenterWebsiteDetailsConfig。另一个选择是在构建阶段使用grunt-wrap包装代码。

  • window.angular和闭包:这是我想在我的代码中使用的一种技术。通过将代码封装到闭包中并为其提供一个名为angular的参数,其实际值为window.angular,您实际上正在创建一个可以被修改的变量。这段代码,例如:

    (function (angular) {
        // You could also declare a variable, instead of a closure parameter:
        // var angular = window.angular;
    
        angular.module('app', ['controllers']);
        angular.module('controllers', []);
        // ...
    })(window.angular);
    

    可以很容易地对此进行说明(请注意,angular的每个引用都被a替换):

    !function(a){a.module("app",["controllers"]),a.module("controllers",[])}(window.angular);
    

    另一方面,这是一个未打包的代码片段:

    angular.module('app', ['controllers']);
    angular.module('controllers', []);
    

    会变成:

    angular.module("app",["controllers"]),angular.module("controllers",[]);
    

    有关闭包的更多信息,请检查this postthis post

答案 1 :(得分:1)

如果在连接文件中检查它,您是否定义了两次状态?是不是你要复制文件两次?检查从中获取文件的临时文件夹(也包括grunt配置,要复制的内容以及要删除的内容......)。

答案 2 :(得分:0)

所以我遇到了同样的问题但是设置如下:

  • yeoman angular-fullstack(使用打字稿)
  • Webstorm

使用angular-fullstack配置,闭包已经实现(正如Danilo Valente建议的那样)所以我挣扎了很长时间,直到我发现在Webstorm中,我启用了typescript编译器,编译了所有* .ts文件到* .js。但由于Webstorm非常“智能”,因此它不会在工作树中显示这些编译文件。然而Grunt当然连接了所有文件,无论它是JS的打字稿。这就是为什么 - 最后 - 我的所有州都被定义了两次。

所以明显的修复:webstorm的禁用打字稿编译器并删除了所有生成的* .js文件并且它可以正常工作。