Angular Direktive templateUrl relativ zur .js-Datei

85

Ich baue eine Winkelrichtlinie, die an verschiedenen Orten verwendet wird. Ich kann die Dateistruktur der App, in der die Direktive verwendet wird, nicht immer garantieren, aber ich kann den Benutzer zwingen, das directive.jsund directive.html(nicht die tatsächlichen Dateinamen) in denselben Ordner zu legen .

Wenn die Seite das auswertet directive.js, betrachtet sie das templateUrlals relativ zu sich selbst. Ist es möglich, das templateUrlrelativ zur directive.jsDatei festzulegen?

Oder wird empfohlen, die Vorlage einfach in die Direktive selbst aufzunehmen.

Ich denke, ich möchte möglicherweise unterschiedliche Vorlagen basierend auf unterschiedlichen Umständen laden. Daher würde ich es vorziehen, einen relativen Pfad zu verwenden, anstatt den zu aktualisieren directive.js

Pedalpete
quelle
4
Oder Sie können grunt-html2js verwenden, um Ihre Vorlagen in js zu konvertieren, die AngularJs dann in seinem Vorlagencache zwischenspeichert. Das machen die Angular-UI-Jungs.
Beyers
Tolle Idee Beyers, ich wusste nichts über grunt-html2js. Werde es überprüfen.
Pedalpete

Antworten:

76

Die aktuell ausgeführte Skriptdatei ist immer die letzte im Skriptarray, sodass Sie den Pfad leicht finden können:

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

Wenn Sie sich nicht sicher sind, wie der Skriptname lautet (z. B. wenn Sie mehrere Skripte in eines packen), verwenden Sie Folgendes:

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

Hinweis : In Fällen, in denen ein Abschluss verwendet wird, sollte sich Ihr Code außerhalb befinden, um sicherzustellen, dass das aktuelle Skript zum richtigen Zeitpunkt ausgewertet wird, z.

// 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;
    })()
);
Alon Gubkin
quelle
2
Oh, das ist verdammt cool! Leider würde dies mit dem Packen und Minimieren meiner JS-Dateien brechen, nicht wahr? Warum ist die aktuell ausgeführte Skriptdatei immer die letzte? Ich hatte den Eindruck, dass alle Skriptdateien in den globalen Bereich geladen werden.
Pedalpete
1
Wenn der Browser auf ein <script>Tag stößt , führt er es sofort aus, bevor die Seite weiter gerendert wird. In einem globalen Skriptbereich ist das letzte <script>Tag immer das letzte.
Alon Gubkin
1
Auch sollte dies beim Minimieren nicht brechen, aber es sollte beim Packen brechen, also habe ich eine Lösung hinzugefügt
Alon Gubkin
Ich glaube ich verstehe was du meinst. Sie haben angenommen, dass ich alles auf der Seite in einem einzigen Skript-Tag geladen habe. Das ist nicht der Fall, aber ich kann das umgehen. Vielen Dank für eine tolle und kreative Antwort!
Pedalpete
1
Anstatt den Namen des Skripts zu ersetzen, können Sie path = currentScriptPath.split("/"); path.pop(); path.push("directive.html");dies tun. Dies hat keine Abhängigkeiten von Dateinamen und unterstützt "directive.js", "/directive.js" und "path / to / directive.js". Der Code-Golf-Wettbewerb besteht darin, diese in einer einzigen Aussage zu kombinieren (unter Verwendung von Splice et al.).
Nathan MacInnes
6

Wie Sie sagten, wollten Sie den Direktiven zu unterschiedlichen Zeiten unterschiedliche Vorlagen zur Verfügung stellen. Warum nicht zulassen, dass die Vorlage selbst als Attribut an die Direktive übergeben wird?

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

Verwenden Sie dann so etwas wie $compile(template)(scope)innerhalb der Richtlinie.

Matt Way
quelle
Ich bin mir nicht sicher, warum ich heute nur darüber informiert wurde. Tut mir leid, dass ich nicht geantwortet habe. Schlagen Sie vor, dass ich innerhalb des Direktivenobjekts mehrere Vorlagen habe? und dann einfach die $compileMethode auf die Vorlage zeigen, die übergeben wird? Ich muss es vielleicht $compiletrotzdem verwenden, das kann also ein guter Vorschlag sein.
Pedalpete
4

Zusätzlich zu der Antwort von Alon Gubkin würde ich vorschlagen, eine Konstante mithilfe eines sofort aufgerufenen Funktionsausdrucks zu definieren, um den Pfad des Skripts zu speichern und in die Direktive einzufügen:

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'
    }
});
Michael Grath
quelle
3

Dieser Code befindet sich in einer Datei namens route.js

Folgendes hat bei mir nicht funktioniert:

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

Folgendes tat:

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

Mein Test basiert auf dem folgenden Blog über die Verwendung von Angle Load Load Angular: http://ify.io/lazy-loading-in-angularjs/

require.js erzeugt einen requireConfig-Bootstrap

requireConfig erzeugt eine eckige app.js.

eckige app.js erzeugt meine route.js

Ich hatte den gleichen Code, der von einem Revel-Webframework und asp.net mvc bereitgestellt wurde. In schwelgen document.getElementsByTagName ("script") wurde ein Pfad zu meiner erforderlichen Bootstrap-JS-Datei und NICHT zu meiner Routen.js erstellt. In ASP.NET MVC wurde ein Pfad zum injizierten Browser Link-Skriptelement von Visual Studio erstellt, das während der Debugging-Sitzungen dort abgelegt wird.

Dies ist mein Arbeitsrouten.js Code:

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

Dies ist meine Konsolenausgabe, wenn ich von Revel aus laufe.

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/ 

Eine andere nette Sache, die ich getan habe, ist, die erforderliche Konfiguration zu nutzen und einige benutzerdefinierte Konfigurationen darin zu platzieren. dh hinzufügen

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

Sie können es dann erhalten, indem Sie den folgenden Code innerhalb von route.js verwenden

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

Manchmal müssen Sie eine Baseurl im Voraus definieren, manchmal müssen Sie sie dynamisch generieren. Ihre Wahl für Ihre Situation.

Herb Stahl
quelle
2

Einige mögen es als etwas "hackig" bezeichnen, aber ich denke, bis es nur noch einen Weg gibt, wird alles hackig sein.
Ich hatte viel Glück damit:

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

Wenn Sie dann den Code in einer Anwendung verwenden, nachdem Sie das Modul in die App aufgenommen haben.
In einer App-Konfigurationsfunktion:

.config(['$routeProvider', '$appUrlProvider', function ($routeProvider, $appUrlProvider) {

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

Und in einem App-Controller (falls erforderlich):

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

Es verursacht einen neuen Javascript-Fehler und zieht den Stack-Trace heraus. Anschließend werden alle URLs analysiert (mit Ausnahme der anrufenden Leitung / Char-Nummer).
Sie können dann einfach die erste Datei im Array herausziehen, die die aktuelle Datei ist, in der der Code ausgeführt wird.

Dies ist auch hilfreich, wenn Sie den Code zentralisieren und dann die zweite ( [1]) im Array herausziehen möchten , um den Speicherort der aufrufenden Datei abzurufen

dan richardson
quelle
Dies wird langsam, hackig und ziemlich nervig sein, aber +1 für die Innovation!
Nathan MacInnes
Wie ich schon sagte, es gibt keine Möglichkeit, dies tatsächlich zu tun, also wird jede Methode ihre Vor- und Nachteile haben. Ich habe keine wirklichen Leistungsprobleme festgestellt, da es nur einmal pro Anwendungslast verwendet wird, um herauszufinden, wo sich die App befindet. Außerdem ist es eigentlich recht einfach zu bedienen. Ich habe nur nicht den vollständigen Code angezeigt, der dies in einen AngularJS-Anbieter einwickelt. Möglicherweise wird meine Antwort aktualisiert.
Dan Richardson
0

Wie mehrere Benutzer bereits betont haben, sind relevante Pfade beim Erstellen der statischen Dateien nicht hilfreich, und ich würde dies wärmstens empfehlen.

In Angular gibt es eine raffinierte Funktion namens $ templateCache , mit der Vorlagendateien mehr oder weniger zwischengespeichert werden. Wenn Angular das nächste Mal eine benötigt, wird anstelle einer tatsächlichen Anforderung die zwischengespeicherte Version bereitgestellt. Dies ist eine typische Verwendung:

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

Auf diese Weise lösen Sie beide das Problem der relativen URLs und gewinnen an Leistung.

Natürlich lieben wir die Idee, separate HTML-Vorlagen-Dateien zu haben (im Gegensatz zum Reagieren), daher ist das oben Genannte allein nicht gut. Hier kommt das Build-System, das alle HTML-Vorlagen-Dateien lesen und ein js wie das oben genannte erstellen kann.

Es gibt mehrere html2js-Module für Grunzen, Schlucken, Webpack, und dies ist die Hauptidee dahinter. Ich persönlich benutze gulp sehr oft, deshalb mag ich gulp-ng-html2js besonders, weil es genau das sehr einfach macht.

Wtower
quelle