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 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
como sendo relativo a si mesmo. É possível definir o templateUrl
para ser relativo ao directive.js
arquivo?
Ou é recomendável incluir apenas o modelo na própria diretiva.
Estou pensando que posso querer carregar diferentes modelos com base em diferentes circunstâncias, então preferiria poder usar um caminho relativo em vez de atualizar o directive.js
javascript
angularjs
path
pedalpete
fonte
fonte
Respostas:
O arquivo de script em execução no momento será sempre o último na matriz de scripts, portanto, você pode 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 estiver compactando vários scripts em um), use este:
return { templateUrl: currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1) + 'directive.html' };
Observação : nos casos em que um encerramento é usado, seu código deve estar externo 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; })() );
fonte
<script>
tag, ele a executa imediatamente antes de continuar a renderizar a página, portanto, em um escopo de script global, a última<script>
tag é sempre a última.path = currentScriptPath.split("/"); path.pop(); path.push("directive.html");
Isso não tem nenhuma dependência de nomes de arquivo e oferece suporte a "diretiva.js", "/diretiva.js" e "caminho / para / diretiva.js". O código da competição de golfe é combiná-los em uma única instrução (usando splice, et al).Como você disse que queria fornecer modelos diferentes em momentos diferentes para as diretivas, por que não permitir que o próprio modelo seja passado para a diretiva como um atributo?
<div my-directive my-template="template"></div>
Em seguida, use algo como
$compile(template)(scope)
dentro da diretiva.fonte
$compile
método para qualquer modelo que seja passado? Posso ter que usar de$compile
qualquer maneira, então essa pode ser uma boa sugestã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' } });
fonte
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 require para lazy load angular: http://ify.io/lazy-loading-in-angularjs/
require.js gera um bootstrap requireConfig
requireConfig gera um app.js angular
app.js angular gera meu routes.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 js de bootstrap necessário e NÃO para o meu routes.js. na ASP.NET MVC, ele produziu um caminho para o elemento de script do link do navegador injetado no Visual Studio que é colocado lá durante as sessões de depuração.
este é 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 o 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 tirar proveito da configuração necessária e colocar algumas configurações personalizadas nela. ie adicionar
customConfig: { baseRouteUrl: '/AngularLazyBaseLine/Home/Content' }
você pode obtê-lo usando o seguinte código de dentro de routes.js
var requireConfig = requirejs.s.contexts._.config; console.log('requireConfig.customConfig.baseRouteUrl:' + requireConfig.customConfig.baseRouteUrl);
às vezes você precisa definir uma baseurl antecipadamente, às vezes você precisa gerá-la dinamicamente. Sua escolha para sua situação.
fonte
Alguns podem sugerir um pouco "hacky", mas acho que até que haja apenas uma maneira de fazer isso, qualquer coisa vai ser hacky.
Tive muita sorte também fazendo 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 após incluir o módulo no aplicativo.
Em uma função 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 da pilha. Em seguida, analisa todos os urls (excluindo a linha de chamada / número de caractere).
Você pode então simplesmente retirar o primeiro do array, que será o arquivo atual onde o código está sendo executado.
Isso também é útil se você deseja centralizar o código e, em seguida, extrair o segundo (
[1]
) na matriz, para obter a localização do arquivo de chamadafonte
Como vários usuários apontaram, os caminhos relevantes não são úteis ao construir os arquivos estáticos, e eu recomendo fortemente fazer isso.
Existe 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 uma solicitação 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', '<div>blabla</div>'); }]); })();
Assim, você enfrenta o problema dos urls relativos e ganha em desempenho.
É claro que amamos a ideia de ter arquivos HTML de template separados (em contraste com o react), então o que foi dito acima não é bom. Aí vem o sistema de compilação, que pode ler todos os arquivos html de template e construir um js como o acima.
Existem vários módulos html2js para grunt, gulp, webpack, e esta é a ideia principal por trás deles. Eu pessoalmente uso muito o gulp, então gosto particularmente do gulp-ng-html2js porque ele faz exatamente isso com muita facilidade.
fonte