Angular директива templateUrl относительно файла .js

Я создаю директиву angular, которая будет использоваться в нескольких разных местах. Я не всегда могу гарантировать файловую структуру приложения, в котором используется директива, но я могу заставить пользователя поместить directive.js и directive.html (а не настоящие имена файлов) в одну и ту же папку.

Когда страница оценивает directive.js, она считает, что templateUrl относительна к себе. Можно ли установить templateUrl относительно файла directive.js?

Или рекомендуется просто включить шаблон в свою директиву.

Я думаю, что я могу загрузить разные шаблоны на основе разных обстоятельств, поэтому предпочтет использовать относительный путь, а не обновлять directive.js

Ответ 1

В настоящее время исполняемый файл script всегда будет последним в массиве скриптов, поэтому вы можете легко найти его путь:

// 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')
        };
    });

Если вы не знаете, что такое имя script (например, если вы упаковываете несколько скриптов в один), используйте это:

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

Примечание. В случаях, когда используется закрытие, ваш код должен быть снаружи, чтобы гарантировать, что текущий сценарий оценивается в нужное время, например:

// 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;
    })()
);

Ответ 2

Как вы сказали, вы хотели бы предоставить разные шаблоны в разное время для директив, почему бы не позволить самому шаблону передавать директиву в качестве атрибута?

<div my-directive my-template="template"></div>

Затем используйте внутри директивы что-то вроде $compile(template)(scope).

Ответ 3

Этот код находится в файле, называемом 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);

Мой тест основан на следующем блоге об использовании, требуемом для ленивой загрузки angular: http://ify.io/lazy-loading-in-angularjs/

require.js запускает bootstrap requireConfig

requireConfig порождает angular app.js

angular app.js порождает мои маршруты .js

У меня был тот же код, который обслуживался веб-картой revel и asp.net mvc. В пире document.getElementsByTagName( "script" ) создал путь к моему файлу загрузки bootstrap js и NOT my routes.js. в ASP.NET MVC он создал путь к элементу Visual Studio, добавленному в Visual Studio script, который помещается туда во время сеансов отладки.

это мой рабочий route.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, иногда вам нужно динамически генерировать его. Ваш выбор для вашей ситуации.

Ответ 4

В дополнение к ответу Алон Губкин Я бы предложил определить константу, используя выражение Exmediately Invoked Expression, чтобы сохранить путь к script и ввести его в директиву:

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'
    }
});

Ответ 5

Некоторые могут предложить немного "взломать", но я думаю, до тех пор, пока есть только один способ сделать это, все будет хаки. Мне также очень повезло:

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-адреса (за исключением номера вызывающей линии /char).
Затем вы можете просто вытащить первое в массиве, которое будет текущим файлом, в котором работает код.

Это также полезно, если вы хотите централизовать код, а затем вытащите второй ([1]) в массиве, чтобы получить местоположение вызывающего файла

Ответ 6

Как указало несколько пользователей, соответствующие пути не помогают при создании статических файлов, и я бы очень рекомендовал это сделать.

В 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 (в отличие от реагирования), поэтому само по себе это нехорошо. Здесь идет система сборки, которая может читать все шаблонные html файлы и строить js, такие как выше.

Существует несколько модулей html2js для grunt, gulp, webpack, и это основная их идея. Я лично использую gulp много, поэтому особенно люблю gulp-ng-html2js, потому что он делает это очень легко.