Je construis une directive angulaire qui sera utilisée dans quelques endroits différents. Je ne peux pas toujours garantir la structure de fichier de l'application dans laquelle la directive est utilisée, mais je peux forcer l'utilisateur à placer le directive.js
et directive.html
(pas les vrais noms de fichier) dans le même dossier.
Lorsque la page évalue le directive.js
, elle considère que le templateUrl
est relatif à lui-même. Est-il possible de définir le templateUrl
pour qu'il soit relatif au directive.js
fichier?
Ou est-il recommandé de simplement inclure le modèle dans la directive elle-même.
Je pense que je souhaiterais peut-être charger différents modèles en fonction de circonstances différentes, je préférerais donc pouvoir utiliser un chemin relatif plutôt que de mettre à jour le directive.js
la source
Réponses:
Le fichier de script en cours d'exécution sera toujours le dernier dans le tableau des scripts, vous pouvez donc facilement trouver son chemin:
// 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') }; });
Si vous n'êtes pas sûr du nom du script (par exemple, si vous regroupez plusieurs scripts en un seul), utilisez ceci:
return { templateUrl: currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1) + 'directive.html' };
Remarque : dans les cas où une fermeture est utilisée, votre code doit être à l'extérieur pour garantir que le script courant est évalué au bon moment, par exemple:
// 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; })() );
la source
<script>
balise, il l'exécute immédiatement avant de continuer à rendre la page, donc dans une portée de script global, la dernière<script>
balise est toujours la dernière.path = currentScriptPath.split("/"); path.pop(); path.push("directive.html");
Cela n'a pas de dépendances sur les noms de fichiers et prend en charge "directive.js", "/directive.js" et "chemin / vers / directive.js". La compétition de golf de code consiste à les combiner en une seule déclaration (en utilisant splice, et al).Comme vous avez dit que vous vouliez fournir différents modèles à des moments différents aux directives, pourquoi ne pas autoriser le modèle lui-même à être transmis à la directive en tant qu'attribut?
<div my-directive my-template="template"></div>
Ensuite, utilisez quelque chose comme
$compile(template)(scope)
à l'intérieur de la directive.la source
$compile
méthode vers le modèle qui est passé? Je devrai peut-être utiliser de$compile
toute façon, donc cela peut être une bonne suggestion.En plus de la réponse d' Alon Gubkin, je suggère de définir une constante à l'aide d'une expression de fonction immédiatement invoquée pour stocker le chemin du script et l'injecter dans la directive:
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' } });
la source
Ce code se trouve dans un fichier appelé routes.js
Ce qui suit n'a pas fonctionné pour moi:
var scripts = document.getElementsByTagName("script") var currentScriptPath = scripts[scripts.length-1].src; var baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
ce qui suit a fait:
var bu2 = document.querySelector("script[src$='routes.js']"); currentScriptPath = bu2.src; baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
Mon test est basé sur le blog suivant sur l'utilisation de require to lazy load angular: http://ify.io/lazy-loading-in-angularjs/
require.js engendre un bootstrap requireConfig
requireConfig engendre un app.js angulaire
angular app.js engendre mes routes.js
J'ai eu le même code servi par un framework web revel et asp.net mvc. Dans revel document.getElementsByTagName ("script") a produit un chemin vers mon fichier js de bootstrap requis et PAS vers mon routes.js. dans ASP.NET MVC, il a produit un chemin vers l'élément de script de lien de navigateur injecté de Visual Studio qui y est placé pendant les sessions de débogage.
c'est mon code de travail 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' ] } } }; });
Ceci est ma sortie de console lors de l'exécution de 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/
Une autre bonne chose que j'ai faite est de profiter de la configuration requise et d'y mettre des configurations personnalisées. ie ajouter
customConfig: { baseRouteUrl: '/AngularLazyBaseLine/Home/Content' }
vous pouvez ensuite l'obtenir en utilisant le code suivant à l'intérieur de routes.js
var requireConfig = requirejs.s.contexts._.config; console.log('requireConfig.customConfig.baseRouteUrl:' + requireConfig.customConfig.baseRouteUrl);
parfois vous devez définir un baseurl à l'avance, parfois vous devez le générer dynamiquement. Votre choix pour votre situation.
la source
Certains pourraient le suggérer légèrement "hacky", mais je pense que tant qu'il n'y aura qu'une seule façon de le faire, tout sera piraté.
J'ai eu beaucoup de chance de faire aussi ceci:
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; } });
Ensuite, lors de l'utilisation du code dans une application après avoir inclus le module dans l'application.
Dans une fonction de configuration d'application:
.config(['$routeProvider', '$appUrlProvider', function ($routeProvider, $appUrlProvider) { $routeProvider .when('/path:folder_path*', { controller: 'BrowseFolderCntrl', templateUrl: $appUrlProvider.route('views/browse-folder.html') }); }]);
Et dans un contrôleur d'application (si nécessaire):
var MyCntrl = function ($scope, $appUrl) { $scope.templateUrl = $appUrl('views/my-angular-view.html'); };
Il crée une nouvelle erreur javascript et extrait la trace de la pile. Il analyse ensuite toutes les URL (à l'exclusion de la ligne / numéro de caractère appelant).
Vous pouvez ensuite simplement extraire le premier du tableau qui sera le fichier actuel dans lequel le code s'exécute.
Ceci est également utile si vous souhaitez centraliser le code, puis extraire le second (
[1]
) du tableau, pour obtenir l'emplacement du fichier appelantla source
Comme plusieurs utilisateurs l'ont souligné, les chemins pertinents ne sont pas utiles lors de la création des fichiers statiques, et je vous recommande vivement de le faire.
Il existe une fonctionnalité astucieuse dans Angular appelée $ templateCache , qui met plus ou moins en cache les fichiers de modèle, et la prochaine fois que Angular en a besoin, au lieu de faire une demande réelle, il fournit la version en cache. Voici une manière typique de l'utiliser:
module = angular.module('myModule'); module.run(['$templateCache', function($templateCache) { $templateCache.put('as/specified/in/templateurl/file.html', '<div>blabla</div>'); }]); })();
Ainsi, vous abordez à la fois le problème des URL relatives et vous gagnez en performances.
Bien sûr, nous adorons l'idée d'avoir des fichiers HTML de modèle séparés (contrairement à réagir), donc ce qui précède n'est pas bon en soi. Voici le système de construction, qui peut lire tous les fichiers HTML de modèle et construire un js tel que celui ci-dessus.
Il existe plusieurs modules html2js pour grunt, gulp, webpack, et c'est l'idée principale derrière eux. Personnellement, j'utilise beaucoup gulp , donc j'aime particulièrement gulp-ng-html2js car il fait exactement cela très facilement.
la source