Diretriz angular templateUrl relativa ao arquivo .js

Estou construindo uma diretiva angular que será usada em alguns locais diferentes. Nem sempre posso garantir a estrutura de arquivos do aplicativo em que a diretiva é usada, mas posso forçar o usuário a colocar o directive.js e directive.html (não os nomes reais dos arquivos) na mesma pasta.

Quando a página avalia o directive.js , ele considera o templateUrl relativo a ele mesmo. É possível definir o templateUrl como relativo ao arquivo directive.js ?

Ou é recomendado apenas include o modelo na própria diretiva.

Eu estou pensando que eu posso querer carregar modelos diferentes com base em diferentes circunstâncias, por isso preferiria ser capaz de usar um caminho relativo em vez de atualizar o directive.js

O arquivo de script em execução será sempre o último da matriz de scripts, para que você possa encontrar facilmente seu caminho:

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

Se você não tiver certeza de qual é o nome do script (por exemplo, se você estiver empacotando vários scripts em um), use isto:

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

Nota : Nos casos em que um encerramento é usado, seu código deve estar fora para garantir que o currentScript seja avaliado no momento correto, como:

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

Como você disse que queria fornecer modelos diferentes em momentos diferentes para as diretivas, por que não permitir que o modelo seja passado para a diretiva como um atributo?

Em seguida, use algo como $compile(template)(scope) dentro da diretiva.

Este código está em um arquivo chamado routes.js

O seguinte não funcionou para mim:

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

o seguinte fez:

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

Meu teste é baseado no seguinte blog sobre o uso de exigir a carga lenta angular: http://ify.io/lazy-loading-in-angularjs/

require.js gera um bootstrap requireConfig

requireConfig gera um app.js angular

app.js angular gera minhas rotas.js

Eu tinha o mesmo código sendo servido por um framework web revel e asp.net mvc. Em revel document.getElementsByTagName (“script”) produziu um caminho para o meu arquivo bootstrap js e NÃO o meu routes.js. no ASP.NET MVC, ele produziu um caminho para o elemento de script do Link do navegador injetado do Visual Studio que é colocado lá durante as sessões de debugging.

este é o meu código de trabalho 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' ] } } }; }); 

Esta é a saída do meu console ao executar a partir do 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/ 

Outra coisa legal que fiz foi aproveitar a configuração require e colocar algumas configurações customizadas nela. ou seja, adicione

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

você pode obtê-lo usando o seguinte código dentro de routes.js

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

às vezes você precisa definir uma base inicial, às vezes você precisa gerar dinamicamente. Sua escolha para sua situação.

Além da resposta de Alon Gubkin , sugiro definir uma constante usando uma Expressão de Função Invocada Imediatamente para armazenar o caminho do script e injetá-lo na diretiva:

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

Alguns podem sugerir um pouco “hacky”, mas acho que até que haja apenas uma maneira de fazer isso, qualquer coisa será hacky.
Eu tive muita sorte em também fazer isso:

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

Então, ao usar o código em um aplicativo depois de include o módulo no aplicativo.
Em uma function de configuração de aplicativo:

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

E em um controlador de aplicativo (se necessário):

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

Ele cria um novo erro de javascript e extrai o rastreamento de pilha. Em seguida, ele analisa todos os URLs (excluindo o número da linha de chamada / caractere).
Você pode então simplesmente extrair o primeiro no array que será o arquivo atual onde o código está rodando.

Isso também é útil se você quiser centralizar o código e, em seguida, extrair o segundo ( [1] ) na matriz, para obter o local do arquivo de chamada

Como vários usuários apontaram, os caminhos relevantes não são úteis ao criar os arquivos estáticos, e eu recomendo isso.

Há um recurso bacana no Angular chamado $ templateCache , que mais ou menos armazena em cache os arquivos de modelo, e da próxima vez que o angular requer um, em vez de fazer um pedido real, ele fornece a versão em cache. Esta é uma maneira típica de usá-lo:

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

Assim, desta forma, você aborda o problema de URLs relativas e ganha desempenho.

É claro que nós amamos a idéia de ter arquivos html de template separados (ao contrário de reagir), então o acima por si só não é bom. Aí vem o sistema de compilation, que pode ler todos os arquivos html do template e construir um js como o acima.

Existem vários módulos html2js para grunhido, gulp, webpack, e esta é a idéia principal por trás deles. Eu pessoalmente uso muito gulp, então eu particularmente gosto de gulp-ng-html2js porque ele faz exatamente isso com muita facilidade.