Кутова директива templateUrl щодо файлу .js


85

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

Коли сторінка оцінює directive.js, вона вважає, що вона templateUrlє відносно самої себе. Чи можна встановити, templateUrlщоб воно було відносно directive.jsфайлу?

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

Я думаю, що я можу захотіти завантажувати різні шаблони залежно від різних обставин, тому волів би мати можливість використовувати відносний шлях, а не оновлювати directive.js


4
Або ви можете використовувати grunt-html2js, щоб перетворити ваші шаблони на js, які AngularJs потім кешуватиме в кеш-шаблоні. Це те, що роблять хлопці-куточки.
Бейерс

Чудова ідея Бейерс, я не знав про grunt-html2js. Буде перевіряти.
педальпет

Відповіді:


76

Файл сценарію, що виконується в даний час, завжди буде останнім у масиві скриптів, тому ви можете легко знайти його шлях:

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

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

// 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
О, зараз це просто проклято! На жаль, це призвело б до розриву пакування та мініфікації моїх файлів js, чи не так? Крім того, чому "виконуваний в даний час файл сценарію завжди є останнім"? У мене склалося враження, що всі файли скриптів завантажуються в глобальну область.
педальпет

1
Коли браузер стикається з <script>тегом, він негайно виконує його, перш ніж продовжувати відображати сторінку, тому в загальному обсязі сценарію останній <script>тег завжди є останнім.
Алон Губкін

1
Крім того, це не повинно перерватися на мініфікації, але це повинно призвести до порушення на упаковці, тому я додав рішення до цього
Алон Губкін

Здається, я розумію, що ви маєте на увазі. Ви припускали, що я завантажував все на сторінці в межах одного тегу сценарію. Це не так, але я можу обійти це. Дякуємо за чудову та креативну відповідь!
педальпет

1
Замість того, щоб замінювати ім'я сценарію, ви можете path = currentScriptPath.split("/"); path.pop(); path.push("directive.html");не мати жодних залежностей від імен файлів і підтримує "директиву.js", "/directive.js" та "шлях / до / директиви.js". Конкурс гольф-коду полягає в тому, щоб поєднати їх в одне твердження (за допомогою сплайсингу та ін.)
Натан Макіннес

6

Як ви сказали, що хочете надати різні шаблони в різний час для директив, чому б не дозволити передавати сам шаблон до директиви як атрибут?

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

Потім використовуйте щось на зразок $compile(template)(scope)усередині директиви.


Я не впевнений, чому я лише сьогодні отримав повідомлення про це MWay, вибачте, що не відповів. Ви припускаєте, що в об'єкті директиви у мене є кілька шаблонів? а потім просто вкажіть $compileметод на будь-який шаблон, який передається? Можливо, мені $compileвсе одно доведеться скористатися , так що це може бути гарною порадою.
педальпет

4

На додаток до відповіді Алона Губкіна, я пропоную визначити константу за допомогою виразу функції, що викликається негайно, для збереження шляху сценарію та введення його до директиви:

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

Цей код знаходиться у файлі, який називається 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 породжує my routes.js

У мене був той самий код, який обслуговував веб-фреймворк revel та asp.net mvc. У файлі revel document.getElementsByTagName ("сценарій") створено шлях до мого файлу js bootstrap, а не до маршрутів.js. в ASP.NET MVC він створив шлях до вбудованого елемента сценарію Browser Link, який вводиться туди під час сесій налагодження.

це мій робочий код 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' } 

Ви можете отримати його, скориставшись наведеним нижче кодом зсередини маршрутів.js

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

іноді вам потрібно визначити базовий елемент заздалегідь, іноді вам потрібно динамічно його генерувати. Ваш вибір для вашої ситуації.


2

Деякі можуть запропонувати це трохи "хакі", але я думаю, поки не буде лише 1 способу зробити це, все буде хакі.
Мені багато пощастило з цим:

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 для інновації!
Nathan MacInnes

Як я вже сказав, насправді немає способу це зробити, тому будь-який метод має свої плюси і мінуси. Я насправді не знайшов жодної проблеми з продуктивністю, оскільки він використовується лише один раз на завантаження програми, щоб знайти, де живе програма. Крім того, насправді досить простий у використанні, я просто не показав повний код, який обертає це у постачальника angularjs ... може оновити мою відповідь.
Ден Річардсон,

0

Як зазначали кілька користувачів, відповідні шляхи не є корисними при створенні статичних файлів, і я настійно рекомендую це зробити.

У 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-ng-html2js, оскільки вона робить саме це дуже легко.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.