相对于 .js 文件的 Angular 指令 templateUrl

Posted

技术标签:

【中文标题】相对于 .js 文件的 Angular 指令 templateUrl【英文标题】:Angular directive templateUrl relative to .js file 【发布时间】:2014-02-01 22:23:20 【问题描述】:

我正在构建一个将在几个不同位置使用的角度指令。 我不能总是保证使用该指令的应用程序的文件结构,但我可以强制用户将directive.jsdirective.html(不是真实文件名)放在同一个文件夹中。

当页面评估directive.js 时,它认为templateUrl 是相对于它自己的。是否可以将templateUrl 设置为相对于directive.js 文件?

或者建议只在指令本身中包含模板。

我在想我可能想根据不同的情况加载不同的模板,所以希望能够使用相对路径而不是更新directive.js

【问题讨论】:

或者您可以使用 grunt-html2js 将您的模板转换为 AngularJs 将缓存在其模板缓存中的 js。这就是 angular-ui 的人所做的。 好主意 Beyers,我不知道 grunt-html2js。会检查的。 【参考方案1】:

正如一些用户所指出的,相关路径在构建静态文件时没有帮助,我强烈建议这样做。

Angular 中有一个名为$templateCache 的漂亮特性,它或多或少会缓存模板文件,下次该 Angular 需要一个,而不是发出实际请求,它会提供缓存的版本。这是一种典型的使用方式:

module = angular.module('myModule');
module.run(['$templateCache', function($templateCache) 
$templateCache.put('as/specified/in/templateurl/file.html',
    '<div>blabla</div>');
]);
)();

因此,通过这种方式,您既可以解决相对 url 的问题,又可以提高性能。

当然,我们喜欢拥有单独的模板 html 文件的想法(与 react 相比),因此上述内容本身并不好。构建系统来了,它可以读取所有模板html文件并构建一个如上的js。

grunt、gulp、webpack 有几个 html2js 模块,这是它们背后的主要思想。我个人经常使用 gulp,所以我特别喜欢gulp-ng-html2js,因为它非常容易做到这一点。

【讨论】:

【参考方案2】:

除了Alon Gubkin 的回答之外,我建议使用立即调用函数表达式定义一个常量来存储脚本的路径并将其注入指令:

angular.module('app', [])

.constant('SCRIPT_URL', (function () 
    var scripts = document.getElementsByTagName("script");
    var scriptPath = scripts[scripts.length - 1].src;
    return scriptPath.substring(0, scriptPath.lastIndexOf('/') + 1)
)())

.directive('test', function(SCRIPT_URL) 
    return 
        restrict :    'A',
        templateUrl : SCRIPT_URL + 'directive.html'
    
);

【讨论】:

【参考方案3】:

有些人可能会认为它有点“hacky”,但我认为只有一种方法可以做到这一点,否则任何事情都会变得 hacky。 我也很幸运地这样做了:

angular.module('ui.bootstrap', [])
  .provider('$appUrl', function()
    this.route = function(url)
       var stack = new Error('dummy').stack.match(new RegExp(/(http(s)*\:\/\/)[^\:]+/igm));
       var app_path = stack[1];
       app_path = app_path.slice(0, app_path.lastIndexOf('App/') + 'App/'.length);
         return app_path + url;
    
    this.$get = function()
        return this.route;
     
  );

然后在将模块包含在应用程序中之后在应用程序中使用代码时。 在应用配置函数中:

.config(['$routeProvider', '$appUrlProvider', function ($routeProvider, $appUrlProvider) 

    $routeProvider
        .when('/path:folder_path*', 
            controller: 'BrowseFolderCntrl',
            templateUrl: $appUrlProvider.route('views/browse-folder.html')
        );
]);

在应用控制器中(如果需要):

var MyCntrl = function ($scope, $appUrl) 
    $scope.templateUrl = $appUrl('views/my-angular-view.html');
;

它会创建一个新的 javascript 错误并提取堆栈跟踪。然后它会解析出所有 url(不包括调用线路/字符号)。 然后,您可以只拉出数组中的第一个,这将是代码正在运行的当前文件。

如果您想集中代码然后拉出数组中的第二个 ([1]) 以获取调用文件位置,这也很有帮助

【讨论】:

这会很慢,很麻烦,而且实际使用起来很烦人,但是 +1 创新! 就像我说的,没有办法真正做到这一点,所以任何方法都会有它的优点和缺点。我还没有真正发现任何性能问题,因为每次应用程序加载它只使用一次来查找应用程序所在的位置。此外,它实际上很容易使用,我只是没有显示将其包装到 angularjs 提供程序中的完整代码......可能会更新我的答案。【参考方案4】:

当前正在执行的脚本文件永远是scripts数组中的最后一个,所以你可以很容易地找到它的路径:

// directive.js

var scripts = document.getElementsByTagName("script")
var currentScriptPath = scripts[scripts.length-1].src;

angular.module('app', [])
    .directive('test', function () 
        return 
            templateUrl: currentScriptPath.replace('directive.js', 'directive.html')
        ;
    );

如果您不确定脚本名称是什么(例如,如果您将多个脚本打包成一个),请使用:

return 
    templateUrl: currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1) 
        + 'directive.html'
;

注意:在使用闭包的情况下,您的代码应该在外部以确保在正确的时间评估 currentScript,例如:

// directive.js

(function(currentScriptPath)
    angular.module('app', [])
        .directive('test', function () 
            return 
                templateUrl: currentScriptPath.replace('directive.js', 'directive.html')
        ;
    );
)(
    (function () 
        var scripts = document.getElementsByTagName("script");
        var currentScriptPath = scripts[scripts.length - 1].src;
        return currentScriptPath;
    )()
);

【讨论】:

哦,现在真是太酷了!不幸的是,这会因打包和缩小我的 js 文件而中断,不是吗?另外,为什么“当前执行的脚本文件总是最后一个”?我的印象是所有脚本文件都加载到全局范围内。 当浏览器遇到&lt;script&gt;标签时,会立即执行它,然后再继续渲染页面,所以在全局脚本范围内,最后一个&lt;script&gt;标签总是最后一个。跨度> 另外,这不应该在缩小时中断,但它应该在打包时中断,所以我添加了一个解决方案 我想我明白你的意思了。您假设我在单个脚本标记中加载页面中的所有内容。情况并非如此,但我可以解决这个问题。感谢您提供出色而富有创意的答案! 不替换脚本名,可以path = currentScriptPath.split("/"); path.pop(); path.push("directive.html");这个对文件名没有任何依赖,支持“directive.js”、“/directive.js”和“path /to/directive.js”。代码高尔夫比赛是将这些组合成一个语句(使用 splice 等)。【参考方案5】:

此代码位于名为 routes.js 的文件中

以下内容对我不起作用:

var scripts = document.getElementsByTagName("script")
var currentScriptPath = scripts[scripts.length-1].src;
var baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);

以下行为:

var bu2 = document.querySelector("script[src$='routes.js']");
currentScriptPath = bu2.src;
baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);

我的测试基于以下关于使用 require 延迟加载 Angular 的博客: http://ify.io/lazy-loading-in-angularjs/

require.js 产生一个 requireConfig 引导程序

requireConfig 产生一个有角度的 app.js

angular app.js 产生我的 routes.js

revel web 框架和 asp.net mvc 提供了相同的代码。 陶醉 document.getElementsByTagName("脚本") 生成了我需要引导 js 文件的路径,而不是我的 routes.js。 在 ASP.NET MVC 中,它生成了 Visual Studio 的注入浏览器链接脚本元素的路径,该元素在调试会话期间放置在那里。

这是我的工作 routes.js 代码:

define([], function()

    var scripts = document.getElementsByTagName("script");
    var currentScriptPath = scripts[scripts.length-1].src;
    console.log("currentScriptPath:"+currentScriptPath);
    var baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
    console.log("baseUrl:"+baseUrl);
    var bu2 = document.querySelector("script[src$='routes.js']");
    currentScriptPath = bu2.src;
    console.log("bu2:"+bu2);
    console.log("src:"+bu2.src);
    baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
    console.log("baseUrl:"+baseUrl);
    return 
        defaultRoutePath: '/',
            routes: 
            '/': 
                templateUrl: baseUrl + 'views/home.html',
                dependencies: [
                    'controllers/HomeViewController',
                    'directives/app-style'
                ]
            ,
            '/about/:person': 
                templateUrl: baseUrl + 'views/about.html',
                dependencies: [
                    'controllers/AboutViewController',
                    'directives/app-color'
                ]
            ,
            '/contact': 
                templateUrl: baseUrl + 'views/contact.html',
                dependencies: [
                    'controllers/ContactViewController',
                    'directives/app-color',
                    'directives/app-style'
                ]
            
        
    ;
);

这是我从 Revel 运行时的控制台输出。

currentScriptPath:http://localhost:9000/public/ngApps/1/requireBootstrap.js routes.js:8
baseUrl:http://localhost:9000/public/ngApps/1/ routes.js:10
bu2:[object HTMLScriptElement] routes.js:13
src:http://localhost:9000/public/ngApps/1/routes.js routes.js:14
baseUrl:http://localhost:9000/public/ngApps/1/ 

我所做的另一件好事是利用 require 配置并在其中放入一些自定义配置。 即添加

customConfig:  baseRouteUrl: '/AngularLazyBaseLine/Home/Content'  

然后您可以使用 routes.js 内部的以下代码来获取它

var requireConfig = requirejs.s.contexts._.config;
console.log('requireConfig.customConfig.baseRouteUrl:' + requireConfig.customConfig.baseRouteUrl); 

有时您需要预先定义一个 baseurl,有时您需要动态生成它。根据您的情况选择。

【讨论】:

【参考方案6】:

正如您所说,您想在不同的时间为指令提供不同的模板,为什么不让模板本身作为属性传递给指令?

&lt;div my-directive my-template="template"&gt;&lt;/div&gt;

然后在指令中使用$compile(template)(scope) 之类的东西。

【讨论】:

我不知道为什么我今天才收到通知,MWay,抱歉没有回复。您是否建议在指令对象中,我有多个模板?然后只需将$compile 方法指向通过的模板?无论如何,我可能不得不使用$compile,所以这可能是一个很好的建议。

以上是关于相对于 .js 文件的 Angular 指令 templateUrl的主要内容,如果未能解决你的问题,请参考以下文章

Angular JS 中 指令详解

Vue入门---常用指令详解

Angular.js常用控件及指令

推荐 15 个 Angular.js 应用扩展指令

angular js中的directive

vue.js随笔记---初识Vue.js