templateUrl คำสั่งเชิงมุมสัมพันธ์กับไฟล์. js


85

ฉันกำลังสร้างคำสั่งเชิงมุมซึ่งจะใช้ในสถานที่ต่างๆ ฉันไม่สามารถรับประกันโครงสร้างไฟล์ของแอปที่ใช้คำสั่งได้เสมอไป แต่ฉันสามารถบังคับให้ผู้ใช้ใส่directive.jsและdirective.html(ไม่ใช่ชื่อไฟล์จริง) ในโฟลเดอร์เดียวกันได้

เมื่อเพจประเมินค่าdirective.jsจะถือว่าเพจนั้นtemplateUrlสัมพันธ์กับตัวมันเอง เป็นไปได้ไหมที่จะตั้งค่าtemplateUrlให้สัมพันธ์กับdirective.jsไฟล์

หรือขอแนะนำให้รวมเทมเพลตไว้ในคำสั่งเท่านั้น

ฉันคิดว่าฉันอาจต้องการโหลดเทมเพลตที่แตกต่างกันขึ้นอยู่กับสถานการณ์ที่แตกต่างกันดังนั้นจึงต้องการใช้เส้นทางแบบสัมพัทธ์แทนการอัปเดต directive.js


4
หรือคุณสามารถใช้ grunt-html2js เพื่อแปลงเทมเพลตของคุณเป็น js ซึ่ง AngularJs จะแคชในแคชเทมเพลต นี่คือสิ่งที่พวก angular-ui ทำ
Beyers

ความคิดที่ยอดเยี่ยมเบเยอร์สฉันไม่รู้เกี่ยวกับคำราม-html2js จะตรวจสอบออก.
pedalpete

คำตอบ:


76

ไฟล์สคริปต์ที่เรียกใช้งานในปัจจุบันจะเป็นไฟล์สุดท้ายในอาร์เรย์ของสคริปต์เสมอดังนั้นคุณสามารถค้นหาพา ธ ได้อย่างง่ายดาย:

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

หากคุณไม่แน่ใจว่าชื่อสคริปต์คืออะไร (ตัวอย่างเช่นหากคุณบรรจุสคริปต์หลาย ๆ สคริปต์ไว้ในตัวเดียว) ให้ใช้สิ่งนี้:

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

หมายเหตุ : ในกรณีที่ใช้การปิดโค้ดของคุณควรอยู่ภายนอกเพื่อให้แน่ใจว่าสคริปต์ปัจจุบันได้รับการประเมินในเวลาที่ถูกต้องเช่น:

// 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
โอ้ตอนนี้เจ๋งมาก! น่าเสียดายที่สิ่งนี้จะทำลายด้วยการบรรจุและการย่อขนาดไฟล์ js ของฉันใช่ไหม นอกจากนี้เหตุใด 'ไฟล์สคริปต์ที่เรียกใช้งานในปัจจุบันจึงเป็นไฟล์สุดท้ายเสมอ' ฉันรู้สึกประทับใจไฟล์สคริปต์ทั้งหมดถูกโหลดเข้าสู่ขอบเขตส่วนกลาง
pedalpete

1
เมื่อเบราว์เซอร์พบ<script>แท็กเบราว์เซอร์จะเรียกใช้งานทันทีก่อนที่จะแสดงผลหน้าต่อไปดังนั้นในขอบเขตสคริปต์ส่วนกลาง<script>แท็กสุดท้ายจะเป็นแท็กสุดท้ายเสมอ
Alon Gubkin

1
นอกจากนี้สิ่งนี้ไม่ควรทำให้การลดขนาดเล็กลง แต่มันควรจะแตกในการบรรจุดังนั้นฉันจึงได้เพิ่มวิธีแก้ปัญหานั้น
Alon Gubkin

ฉันคิดว่าฉันเข้าใจว่าคุณหมายถึงอะไร คุณสมมติว่าฉันกำลังโหลดทุกอย่างในหน้าภายในแท็กสคริปต์เดียว นั่นไม่ใช่กรณีนี้ แต่ฉันสามารถแก้ไขได้ ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมและสร้างสรรค์!
pedalpete

1
แทนที่จะเปลี่ยนชื่อสคริปต์คุณสามารถทำได้path = currentScriptPath.split("/"); path.pop(); path.push("directive.html");สิ่งนี้ไม่มีการอ้างอิงใด ๆ กับชื่อไฟล์และสนับสนุน "directive.js", "/directive.js" และ "path / to / directive.js" การแข่งขันกอล์ฟรหัสคือการรวมสิ่งเหล่านี้ไว้ในคำสั่งเดียว (ใช้ splice และอื่น ๆ )
Nathan MacInnes

6

อย่างที่คุณบอกว่าคุณต้องการจัดเตรียมเทมเพลตที่แตกต่างกันในแต่ละช่วงเวลาให้กับคำสั่งทำไมไม่อนุญาตให้ส่งเทมเพลตนั้นไปยังคำสั่งเป็นแอตทริบิวต์

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

จากนั้นใช้สิ่งที่ต้องการ$compile(template)(scope)ภายในคำสั่ง


ฉันไม่แน่ใจว่าทำไมฉันถึงได้รับแจ้งเรื่องนี้ในวันนี้ MWay ขอโทษที่ไม่ตอบกลับ คุณแนะนำว่าภายในออบเจ็กต์คำสั่งฉันมีเทมเพลตหลายแบบหรือไม่? จากนั้นชี้$compileวิธีไปที่เทมเพลตใดที่ส่งผ่าน? ฉันอาจจะต้องใช้$compileต่อไปดังนั้นนั่นอาจเป็นคำแนะนำที่ดี
pedalpete

4

นอกจากคำตอบจากAlon Gubkinแล้วฉันขอแนะนำให้กำหนดค่าคงที่โดยใช้ทันทีเรียกใช้ฟังก์ชันนิพจน์เพื่อเก็บเส้นทางของสคริปต์และฉีดเข้าไปในคำสั่ง:

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

รหัสนี้อยู่ในไฟล์ชื่อ route.js

สิ่งต่อไปนี้ใช้ไม่ได้สำหรับฉัน:

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

สิ่งต่อไปนี้ทำ:

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

การทดสอบของฉันอ้างอิงจากบล็อกต่อไปนี้เกี่ยวกับการใช้ need to lazy load angular: http://ify.io/lazy-loading-in-angularjs/

require.js ทำให้เกิดการบูต requireConfig

requireConfig เริ่มต้นแอปเชิงมุม js

แอพเชิงมุม js เริ่มต้นเส้นทางของฉัน js

ฉันมีรหัสเดียวกันที่ใช้โดย revel web framework และ asp.net mvc ใน revel document.getElementsByTagName ("script") สร้างเส้นทางไปยังไฟล์ js ที่ต้องการ bootstrap ของฉันและไม่ใช่เส้นทางของฉัน js ใน ASP.NET MVC จะสร้างเส้นทางไปยังองค์ประกอบสคริปต์ Browser Link ของ Visual Studio ที่ใส่ไว้ในระหว่างเซสชันการดีบัก

นี่คือรหัสเส้นทางการทำงานของฉัน 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'
                ]
            }
        }
    };
});

นี่คือเอาต์พุตคอนโซลของฉันเมื่อเรียกใช้จาก 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/ 

สิ่งที่ดีอีกอย่างที่ฉันได้ทำคือการใช้ประโยชน์จากการกำหนดค่าที่ต้องการและใส่การกำหนดค่าที่กำหนดเองลงไป เช่นเพิ่ม

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

จากนั้นคุณสามารถรับได้โดยใช้รหัสต่อไปนี้จากด้านในของ route.js

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

บางครั้งคุณต้องกำหนด baseurl ล่วงหน้าบางครั้งคุณต้องสร้างแบบไดนามิก ทางเลือกของคุณสำหรับสถานการณ์ของคุณ


2

บางคนอาจแนะนำว่า "แฮ็ก" เล็กน้อย แต่ฉันคิดว่าจนกว่าจะมีเพียง 1 วิธีที่จะทำอะไรก็จะแฮ็คได้
ฉันโชคดีมากที่ได้ทำสิ่งนี้:

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

จากนั้นเมื่อใช้รหัสในแอปพลิเคชันหลังจากรวมโมดูลในแอปแล้ว
ในฟังก์ชัน config ของแอพ:

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

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

และในตัวควบคุมแอป (หากจำเป็น):

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

มันสร้างข้อผิดพลาดจาวาสคริปต์ใหม่และดึงการติดตามสแต็กออกมา จากนั้นจะแยกวิเคราะห์ URL ทั้งหมด (ไม่รวมหมายเลขสายโทร / หมายเลขอักขระ)
จากนั้นคุณสามารถดึงไฟล์แรกในอาร์เรย์ออกมาซึ่งจะเป็นไฟล์ปัจจุบันที่โค้ดกำลังทำงานอยู่

สิ่งนี้มีประโยชน์เช่นกันหากคุณต้องการรวมรหัสไว้ที่ส่วนกลางจากนั้นดึงวินาที ( [1]) ในอาร์เรย์เพื่อรับตำแหน่งไฟล์การโทร


สิ่งนี้จะช้าแฮ็กและค่อนข้างน่ารำคาญในการใช้งานจริง แต่ +1 สำหรับนวัตกรรม!
Nathan MacInnes

อย่างที่บอกไม่มีวิธีที่จะทำได้จริงดังนั้นวิธีการใด ๆ ที่จะมีข้อดีข้อเสีย ฉันไม่พบปัญหาด้านประสิทธิภาพจริงๆเนื่องจากใช้เพียงครั้งเดียวต่อการโหลดแอปพลิเคชันเพื่อค้นหาว่าแอปอยู่ที่ไหน นอกจากนี้มันค่อนข้างใช้งานง่ายจริงๆฉันไม่ได้แสดงโค้ดทั้งหมดที่รวมสิ่งนี้ไว้ในผู้ให้บริการ angularjs ... อาจอัปเดตคำตอบของฉัน
dan richardson

0

ตามที่ผู้ใช้หลายคนได้ชี้ให้เห็นเส้นทางที่เกี่ยวข้องไม่เป็นประโยชน์เมื่อสร้างไฟล์คงที่และฉันขอแนะนำให้ทำเช่นนั้น

มีคุณสมบัติที่ดีใน Angular ที่เรียกว่า$ templateCacheซึ่งจะแคชไฟล์เทมเพลตมากขึ้นหรือน้อยลงและในครั้งต่อไปที่ต้องใช้เชิงมุมแทนที่จะส่งคำขอจริง แต่จะมีเวอร์ชันแคช นี่เป็นวิธีการใช้งานทั่วไป:

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

ด้วยวิธีนี้คุณทั้งสองจะจัดการกับปัญหาของ URL สัมพัทธ์และคุณได้รับประสิทธิภาพ

แน่นอนว่าเราชอบความคิดที่จะมีไฟล์ html เทมเพลตแยกต่างหาก (ในทางตรงกันข้ามกับการตอบสนอง) ดังนั้นสิ่งที่กล่าวมาข้างต้นจึงไม่ดี นี่คือระบบการสร้างซึ่งสามารถอ่านไฟล์ html เทมเพลตทั้งหมดและสร้าง js ดังที่กล่าวมา

มีโมดูล html2js หลายโมดูลสำหรับคำราม, อึก, webpack และนี่คือแนวคิดหลักที่อยู่เบื้องหลัง โดยส่วนตัวฉันใช้อึกมากดังนั้นฉันจึงชอบgulp-ng-html2js เป็นพิเศษเพราะมันทำได้ง่ายมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.