Angular chỉ thị templateUrl liên quan đến tệp .js


85

Tôi đang xây dựng một chỉ thị góc sẽ được sử dụng ở một số địa điểm khác nhau. Tôi không thể luôn đảm bảo cấu trúc tệp của ứng dụng mà chỉ thị được sử dụng, nhưng tôi có thể buộc người dùng đặt directive.jsdirective.html(không phải tên tệp thực) trong cùng một thư mục.

Khi trang đánh giá directive.js, nó sẽ coi templateUrllà tương đối với chính nó. Có thể đặt giá trị templateUrltương đối với directive.jstệp không?

Hoặc bạn chỉ nên đưa mẫu vào chính chỉ thị.

Tôi nghĩ rằng tôi có thể muốn tải các mẫu khác nhau dựa trên các trường hợp khác nhau, vì vậy tôi muốn có thể sử dụng một đường dẫn tương đối hơn là cập nhật directive.js


4
Hoặc bạn có thể sử dụng grunt-html2js để chuyển đổi các mẫu của bạn thành js mà AngularJs sau đó sẽ lưu vào bộ đệm ẩn mẫu của nó. Đây là những gì các anh chàng góc cạnh làm.
Beyers

Ý tưởng tuyệt vời thưa Luật sư, tôi không biết về grunt-html2js. Sẽ kiểm tra nó ra.
đạp xe vào

Câu trả lời:


76

Tệp script hiện đang thực thi sẽ luôn là tệp cuối cùng trong mảng script, vì vậy bạn có thể dễ dàng tìm thấy đường dẫn của nó:

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

Nếu bạn không chắc tên tập lệnh là gì (ví dụ: nếu bạn đang đóng gói nhiều tập lệnh thành một), hãy sử dụng cách này:

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

Lưu ý : Trong trường hợp sử dụng lệnh đóng, mã của bạn phải ở bên ngoài để đảm bảo rằng currentScript được đánh giá vào đúng thời điểm, chẳng hạn như:

// 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
Ồ, bây giờ thật tuyệt! Thật không may, điều này sẽ phá vỡ với việc đóng gói và thu nhỏ các tệp js của tôi, phải không? Ngoài ra, tại sao 'tệp kịch bản hiện đang thực thi luôn là tệp cuối cùng'? Tôi có ấn tượng rằng tất cả các tệp kịch bản đều được tải vào phạm vi toàn cầu.
đạp xe vào

1
Khi trình duyệt gặp một <script>thẻ, nó sẽ ngay lập tức thực thi nó trước khi tiếp tục hiển thị trang, vì vậy trong phạm vi tập lệnh toàn cục, <script>thẻ cuối cùng luôn là thẻ cuối cùng.
Alon Gubkin

1
Ngoài ra, điều này không nên phá vỡ trên Giảm bớt, nhưng nó phải phá vỡ trên bao bì, vì vậy tôi đã thêm một giải pháp cho rằng
Alon Gubkin

Tôi nghĩ tôi hiểu ý bạn. Bạn đang giả định rằng tôi đang tải mọi thứ trong trang trong một thẻ script. Đó không phải là trường hợp, nhưng tôi có thể giải quyết vấn đề đó. Cảm ơn vì một câu trả lời tuyệt vời và sáng tạo!
đạp xe vào

1
Thay vì thay thế tên của tập lệnh, bạn có thể path = currentScriptPath.split("/"); path.pop(); path.push("directive.html");Điều này không có bất kỳ phụ thuộc nào vào tên tệp và hỗ trợ "chỉ thị.js", "/directive.js" và "đường dẫn / đến / chỉ thị.js". Cuộc thi chơi gôn mã là kết hợp chúng thành một câu lệnh duy nhất (sử dụng splice, et al).
Nathan MacInnes

6

Như bạn đã nói, bạn muốn cung cấp các mẫu khác nhau vào các thời điểm khác nhau cho các lệnh, tại sao không cho phép bản thân mẫu được chuyển tới lệnh dưới dạng một thuộc tính?

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

Sau đó, sử dụng một cái gì đó giống như $compile(template)(scope)bên trong chỉ thị.


Tôi không chắc tại sao tôi chỉ nhận được thông báo về điều này hôm nay MWay, xin lỗi vì đã không phản hồi. Bạn có gợi ý rằng trong đối tượng chỉ thị, tôi có nhiều mẫu không? và sau đó chỉ cần trỏ $compilephương thức đến bất kỳ mẫu nào được thông qua? Tôi có thể phải sử dụng $compileanyway, vì vậy đó có thể là một gợi ý tốt.
pedalpete

4

Ngoài câu trả lời từ Alon Gubkin, tôi khuyên bạn nên xác định một hằng số bằng cách sử dụng Biểu thức hàm được gọi ngay lập tức để lưu trữ đường dẫn của tập lệnh và đưa nó vào lệnh:

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

Mã này nằm trong một tệp có tên là route.js

Những điều sau đây không hiệu quả với tôi:

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

sau đây đã làm:

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

Thử nghiệm của tôi dựa trên blog sau về việc sử dụng request to lazy load angle: http://ify.io/lazy-loading-in-angularjs/

request.js tạo ra một bootstrap requestConfig

requestConfig tạo ra một app.js góc cạnh

angle app.js đặt ra các route.js của tôi

Tôi có cùng một mã được cung cấp bởi một khung công tác web revel và asp.net mvc. Trong revel document.getElementsByTagName ("script") đã tạo một đường dẫn đến tệp js bootstrap yêu cầu của tôi chứ KHÔNG PHẢI là các route.js của tôi. trong ASP.NET MVC, nó tạo ra một đường dẫn đến phần tử tập lệnh Liên kết trình duyệt được chèn của Visual Studio được đặt ở đó trong các phiên gỡ lỗi.

đây là mã route.js làm việc của tôi:

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

Đây là đầu ra bảng điều khiển của tôi khi chạy từ 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/ 

Một điều tốt đẹp khác mà tôi đã làm là tận dụng cấu hình yêu cầu và đặt một số cấu hình tùy chỉnh vào đó. tức là thêm

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

sau đó bạn có thể lấy nó bằng cách sử dụng mã sau từ bên trong của route.js

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

đôi khi bạn cần xác định một baseurl trả trước, đôi khi bạn cần tạo động nó. Sự lựa chọn của bạn cho tình huống của bạn.


2

Một số người có thể đề xuất nó hơi "hacky", nhưng tôi nghĩ cho đến khi chỉ còn một cách để làm điều đó, bất cứ điều gì sẽ trở thành hacky.
Tôi đã gặp rất nhiều may mắn khi làm điều này:

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

Sau đó, khi sử dụng mã trong một ứng dụng sau khi bao gồm mô-đun trong ứng dụng.
Trong một chức năng cấu hình ứng dụng:

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

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

Và trong bộ điều khiển ứng dụng (nếu cần):

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

Nó tạo ra một lỗi javascript mới và kéo ra dấu vết ngăn xếp. Sau đó, nó phân tích cú pháp tất cả các url (không bao gồm số đường dây gọi / ký tự).
Sau đó, bạn có thể chỉ cần kéo ra đầu tiên trong mảng sẽ là tệp hiện tại nơi mã đang chạy.

Điều này cũng hữu ích nếu bạn muốn tập trung mã và sau đó kéo ra thứ hai ( [1]) trong mảng, để lấy vị trí tệp đang gọi


Điều này sẽ chậm, khó sử dụng và khá khó chịu khi thực sự sử dụng, nhưng hãy +1 cho sự đổi mới!
Nathan MacInnes

Như tôi đã nói, không có cách nào thực sự làm được điều đó, vì vậy bất kỳ phương pháp nào cũng sẽ có ưu và nhược điểm. Tôi thực sự không tìm thấy bất kỳ vấn đề hiệu suất nào vì nó chỉ được sử dụng một lần mỗi lần tải ứng dụng để tìm vị trí của ứng dụng. Ngoài ra, nó thực sự khá dễ sử dụng, tôi chỉ không hiển thị mã đầy đủ bao bọc điều này vào một nhà cung cấp anglejs ... có thể cập nhật câu trả lời của tôi.
dan richardson

0

Như một số người dùng đã chỉ ra, các đường dẫn liên quan không hữu ích khi tạo tệp tĩnh và tôi thực sự khuyên bạn nên làm như vậy.

Có một tính năng tiện lợi trong Angular được gọi là $ templateCache , tính năng này ít nhiều sẽ lưu vào bộ đệm các tệp mẫu và lần tới, góc đó yêu cầu một tính năng, thay vì đưa ra yêu cầu thực tế, nó sẽ cung cấp phiên bản đã lưu trong bộ nhớ cache. Đây là một cách điển hình để sử dụng nó:

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

Vì vậy, bằng cách này, bạn vừa giải quyết được vấn đề về url tương đối vừa đạt được hiệu suất.

Tất nhiên, chúng tôi yêu thích ý tưởng có các tệp html mẫu riêng biệt (ngược lại để phản ứng), vì vậy việc riêng ở trên là không tốt. Ở đây có hệ thống xây dựng, có thể đọc tất cả các tệp html mẫu và xây dựng một js như ở trên.

Có một số mô-đun html2js cho grunt, gulp, webpack và đây là ý tưởng chính đằng sau chúng. Cá nhân tôi sử dụng gulp rất nhiều, vì vậy tôi đặc biệt thích gulp-ng-html2js vì nó thực hiện chính xác điều này rất dễ dàng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.