คำสั่งเชิงมุม - เวลาและวิธีการใช้คอมไพล์, คอนโทรลเลอร์, พรีลิงค์และโพสต์ลิงค์ [ปิด]


451

เมื่อเขียนคำสั่งเชิงมุมคุณสามารถใช้ฟังก์ชันใด ๆ ต่อไปนี้เพื่อจัดการพฤติกรรม DOM เนื้อหาและรูปลักษณ์ขององค์ประกอบที่ประกาศคำสั่ง:

  • รวบรวม
  • ตัวควบคุม
  • ก่อนการเชื่อมโยง
  • โพสต์ลิงก์

ดูเหมือนจะมีความสับสนว่าฟังก์ชั่นใดที่ควรใช้ คำถามนี้ครอบคลุม:

คำสั่งพื้นฐาน

ฟังก์ชั่นธรรมชาติสิ่งที่ต้องทำและสิ่งที่ไม่ควรทำ

คำถามที่เกี่ยวข้อง:


27
อะไรนะ
haimlit

2
@Ian See: ผู้ให้บริการมากไป โดยพื้นฐานแล้วสิ่งนี้มีไว้สำหรับวิกิชุมชน มีคำตอบสำหรับคำถามที่เกี่ยวข้องมากเกินไปบางส่วนไม่ได้ให้ภาพเต็ม
Izhaki

8
นี่คือเนื้อหาที่ยอดเยี่ยม แต่เราขอให้ทุกสิ่งที่นี่ถูกเก็บไว้ในรูปแบบคำถาม & คำตอบ บางทีคุณอาจต้องการแยกคำถามนี้ออกเป็นคำถามที่ไม่ต่อเนื่องหลายคำถามแล้วเชื่อมโยงกับคำถามเหล่านั้นจากแท็ก wiki หรือไม่
เฟล็กโซ

57
แม้ว่าโพสต์นี้จะอยู่นอกหัวข้อและในรูปแบบบล็อกมันมีประโยชน์มากที่สุดในการให้คำอธิบายเชิงลึกของคำสั่งเชิงมุม โปรดอย่าลบโพสต์นี้ผู้ดูแลระบบ!
อรรถกถา

12
สุจริตฉันไม่ได้รำคาญกับเอกสารต้นฉบับ โพสต์ stackoverflow หรือบล็อกมักจะทำให้ฉันไปภายในไม่กี่วินาทีเทียบกับ 15-30 นาทีของการฉีกผมของฉันพยายามที่จะเข้าใจเอกสารต้นฉบับ
เดวิด

คำตอบ:


168

ลำดับใดที่สั่งการทำงานของคำสั่ง?

สำหรับคำสั่งเดียว

พิจารณาจากplunkต่อไปนี้พิจารณามาร์กอัพ HTML ต่อไปนี้

<body>
    <div log='some-div'></div>
</body>

ด้วยการประกาศคำสั่งต่อไปนี้:

myApp.directive('log', function() {

    return {
        controller: function( $scope, $element, $attrs, $transclude ) {
            console.log( $attrs.log + ' (controller)' );
        },
        compile: function compile( tElement, tAttributes ) {
            console.log( tAttributes.log + ' (compile)'  );
            return {
                pre: function preLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (pre-link)'  );
                },
                post: function postLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (post-link)'  );
                }
            };
         }
     };  

});

เอาต์พุตคอนโซลจะเป็น:

some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)

เราจะเห็นว่าcompileมีการดำเนินการก่อนแล้วcontrollerแล้วและมีที่ผ่านมาpre-linkpost-link

สำหรับคำสั่งซ้อน

หมายเหตุ:ข้อมูลต่อไปนี้ใช้ไม่ได้กับคำสั่งที่แสดงเด็ก ๆ ในฟังก์ชันลิงก์ คำสั่ง Angular ค่อนข้างน้อยทำเช่นนั้น (เช่น ngIf, ngRepeat หรือคำสั่งใด ๆ ที่มีtransclude) สั่งเหล่านี้กำเนิดของพวกเขาจะมีlinkฟังก์ชั่นที่เรียกว่าก่อนที่จะสั่งเด็กของพวกเขาcompileเรียกว่า

มาร์กอัป HTML ดั้งเดิมมักทำจากองค์ประกอบที่ซ้อนกันซึ่งแต่ละรายการมีคำสั่งของตัวเอง ชอบในมาร์กอัปต่อไปนี้ (ดูเสียงดังปัง ):

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>

เอาต์พุตคอนโซลจะมีลักษณะดังนี้:

// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)

// The link phase   
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)

เราสามารถแยกความแตกต่างได้สองเฟสที่นี่ - เฟสคอมไพล์และเฟสลิงค์

ขั้นตอนการคอมไพล์

เมื่อโหลด DOM แล้ว Angular จะเริ่มเฟสคอมไพล์โดยที่ traverses มาร์กอัปจากบนลงล่างและเรียกcompileใช้คำสั่งทั้งหมด กราฟิกเราสามารถแสดงได้อย่างนั้น:

รูปภาพที่แสดงห่วงการรวบรวมสำหรับเด็ก

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

เฟสลิงก์

อินสแตนซ์ DOM มักจะเป็นเพียงผลลัพธ์ของเทมเพลตต้นทางที่แสดงผลไปยัง DOM แต่อาจถูกสร้างโดยng-repeatหรือแนะนำทันที

เมื่อใดก็ตามที่อินสแตนซ์ใหม่ขององค์ประกอบที่มีคำสั่งถูกเรนเดอร์ไปที่ DOM เฟสลิงก์จะเริ่มต้นขึ้น

ในระยะนี้โทรเชิงมุมcontroller, pre-link, iterates เด็กและโทรpost-linkในคำสั่งทั้งหมดเช่นดังนั้น:

ภาพประกอบแสดงขั้นตอนการเชื่อมโยง


5
@lzhaki ผังที่ดูดี ต้องการแบ่งปันชื่อเครื่องมือสร้างแผนภูมิหรือไม่ :)
merlin

1
@merlin ฉันใช้ OmniGraffle (แต่อาจใช้ผู้วาดภาพประกอบหรือ inkscape - นอกจากความเร็วแล้วไม่มีสิ่งใดที่ OmniGraffle ทำได้ดีกว่าเครื่องมือสร้างแผนภูมิอื่น ๆ ที่เกี่ยวข้องกับภาพประกอบนี้)
Izhaki

2
ผู้ขุดของ @ Anant หายไปดังนั้นนี่คือใหม่: plnkr.co/edit/kZZks8HN0iFIY8ZaKJkA?p=previewเปิดคอนโซลคอนโซลเพื่อดูข้อความบันทึก

ทำไมสิ่งนี้ไม่เป็นความจริงเมื่อใช้ ng-repeat สำหรับคำสั่งสำหรับเด็ก ??? ดูเสียงอึกทึก
Luckylooke

@ Luckylooke เสียงอึกทึกของคุณไม่มีลูกที่มีคำสั่งภายใต้ ng-repeat (เช่นสิ่งที่ถูกทำซ้ำคือแม่แบบที่มีคำสั่งถ้าเป็นเช่นนั้นคุณจะเห็นว่าคอมไพล์ของพวกเขาถูกเรียกหลังจากลิงก์ของ ng-repeat
Izhaki

90

เกิดอะไรขึ้นระหว่างการเรียกใช้ฟังก์ชันเหล่านี้

ฟังก์ชั่นคำสั่งต่างๆจะดำเนินการจากภายในสองฟังก์ชั่นอื่น ๆ ที่เรียกว่าเชิงมุม$compile(ที่สั่งของcompileจะถูกดำเนินการ) และฟังก์ชั่นภายในที่เรียกว่าnodeLinkFn(ที่สั่งของcontroller, preLinkและpostLinkจะดำเนินการ) มีสิ่งต่าง ๆ เกิดขึ้นภายในฟังก์ชันเชิงมุมก่อนและหลังเรียกใช้ฟังก์ชันคำสั่ง บางทีการเรียกร้องเด็กเป็นสิ่งที่สะดุดตาที่สุด ภาพประกอบง่าย ๆ ต่อไปนี้แสดงขั้นตอนสำคัญภายในเฟสการคอมไพล์และลิงค์:

ภาพประกอบแสดงขั้นตอนการคอมไพล์และแองกูลาร์

เพื่อสาธิตขั้นตอนเหล่านี้ลองใช้มาร์กอัป HTML ต่อไปนี้:

<div ng-repeat="i in [0,1,2]">
    <my-element>
        <div>Inner content</div>
    </my-element>
</div>

ด้วยคำสั่งดังต่อไปนี้:

myApp.directive( 'myElement', function() {
    return {
        restrict:   'EA',
        transclude: true,
        template:   '<div>{{label}}<div ng-transclude></div></div>'
    }
});

รวบรวม

compileAPI เพื่อให้ดูเหมือนว่า:

compile: function compile( tElement, tAttributes ) { ... }

บ่อยครั้งที่พารามิเตอร์จะถูกนำหน้าด้วยtเพื่อบ่งบอกถึงองค์ประกอบและคุณสมบัติที่มีให้เป็นของแม่แบบแหล่งที่มามากกว่าที่ของอินสแตนซ์

ก่อนที่จะมีการลบcompileเนื้อหาที่เรียกว่า transcluded (ถ้ามี) และเทมเพลตจะถูกนำไปใช้กับมาร์กอัป ดังนั้นองค์ประกอบที่มีให้กับcompileฟังก์ชันจะมีลักษณะดังนี้:

<my-element>
    <div>
        "{{label}}"
        <div ng-transclude></div>
    </div>
</my-element>

โปรดสังเกตว่าเนื้อหาที่ transcluded จะไม่ถูกแทรกใหม่ในตอนนี้

หลังจากการเรียกไปยังคำสั่ง.compileAngular จะสำรวจองค์ประกอบลูกทั้งหมดรวมถึงองค์ประกอบที่อาจเพิ่งได้รับการแนะนำโดยคำสั่ง (เช่นองค์ประกอบแม่แบบ)

การสร้างอินสแตนซ์

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

ตัวควบคุม

controllerAPI เกี่ยวข้องกับ:

controller: function( $scope, $element, $attrs, $transclude ) { ... }

เมื่อเข้าสู่เฟสลิงก์ตอนนี้ฟังก์ชันลิงก์ที่ส่งคืน$compileจะถูกจัดเตรียมไว้พร้อมขอบเขต

ก่อนอื่นฟังก์ชันลิงก์จะสร้างขอบเขตลูก ( scope: true) หรือขอบเขตแยก ( scope: {...}) หากมีการร้องขอ

จากนั้นคอนโทรลเลอร์จะถูกดำเนินการโดยมีขอบเขตขององค์ประกอบอินสแตนซ์

Pre-การเชื่อมโยง

pre-linkAPI เพื่อให้ดูเหมือนว่า:

function preLink( scope, element, attributes, controller ) { ... }

แทบไม่มีอะไรเกิดขึ้นระหว่างการเรียกไปยังคำสั่ง.controllerและ.preLinkฟังก์ชั่น เชิงมุมยังคงให้คำแนะนำว่าควรใช้งานอย่างไร

หลังจากการ.preLinkเรียกฟังก์ชั่นการเชื่อมโยงจะสำรวจแต่ละองค์ประกอบลูก - เรียกฟังก์ชั่นการเชื่อมโยงที่ถูกต้องและแนบขอบเขตปัจจุบัน (ซึ่งทำหน้าที่เป็นขอบเขตหลักสำหรับองค์ประกอบเด็ก)

โพสต์ลิงค์

post-linkAPI จะคล้ายกับที่ของpre-linkฟังก์ชั่น:

function postLink( scope, element, attributes, controller ) { ... }

อาจสังเกตเห็นว่าเมื่อมี.postLinkการเรียกใช้ฟังก์ชั่นของคำสั่งกระบวนการเชื่อมโยงขององค์ประกอบลูกทั้งหมดได้เสร็จสมบูรณ์แล้วรวมถึง.postLinkฟังก์ชั่นของเด็กทั้งหมด

ซึ่งหมายความว่าเมื่อถึงเวลาที่.postLinkกำหนดเด็ก ๆ จะมีชีวิต 'พร้อม' รวมถึง:

  • การผูกข้อมูล
  • ใช้การแปล
  • แนบขอบเขตแล้ว

เทมเพลตในขั้นตอนนี้จะมีลักษณะดังนี้:

<my-element>
    <div class="ng-binding">
        "{{label}}"
        <div ng-transclude>                
            <div class="ng-scope">Inner content</div>
        </div>
    </div>
</my-element>

3
คุณสร้างภาพวาดนี้ได้อย่างไร
Royi Namir

6
@RoyiNamir Omnigraffle
Izhaki

43

จะประกาศฟังก์ชั่นต่าง ๆ ได้อย่างไร?

รวบรวม, ควบคุม, Pre-link & Post-link

หากมีการใช้ทั้งสี่ฟังก์ชั่นคำสั่งจะเป็นไปตามแบบฟอร์มนี้:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.
            return {
                pre: function preLink( scope, element, attributes, controller, transcludeFn ) {
                    // Pre-link code goes here
                },
                post: function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-link code goes here
                }
            };
        }
    };  
});

โปรดสังเกตว่าการคอมไพล์ส่งคืนออบเจกต์ที่มีทั้งฟังก์ชั่น pre-link และ post-link ในเชิงมุมศัพท์แสงที่เราบอกว่าฟังก์ชั่นรวบรวมส่งกลับฟังก์ชั่นแม่แบบ

รวบรวมควบคุมและโพสต์ลิงค์

หากpre-linkไม่จำเป็นฟังก์ชันคอมไพล์สามารถส่งคืนฟังก์ชันโพสต์ลิงก์แทนวัตถุนิยามได้เช่น:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.
            return function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-link code goes here                 
            };
        }
    };  
});

บางครั้งมีความปรารถนาที่จะเพิ่มcompileวิธีการหลังจากกำหนด (โพสต์) linkวิธี สำหรับสิ่งนี้หนึ่งสามารถใช้:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.

            return this.link;
        },
        link: function( scope, element, attributes, controller, transcludeFn ) {
            // Post-link code goes here
        }

    };  
});

ตัวควบคุมและโพสต์ลิงค์

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

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});

ไม่มีตัวควบคุม

ในตัวอย่างใด ๆ ข้างต้นคุณสามารถลบcontrollerฟังก์ชั่นได้ถ้าไม่ต้องการ ตัวอย่างเช่นหากpost-linkจำเป็นต้องใช้ฟังก์ชั่นเดียวสามารถใช้:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});

31

ความแตกต่างระหว่างแม่แบบแหล่งที่มาและแม่แบบอินสแตนซ์คืออะไร?

ความจริงที่ว่า Angular อนุญาตให้ใช้การจัดการ DOM หมายความว่ามาร์กอัปอินพุตในกระบวนการรวบรวมบางครั้งแตกต่างจากเอาต์พุต โดยเฉพาะมาร์กอัพอินพุตบางรายการอาจถูกโคลนสองสามครั้ง (เช่นเดียวกับng-repeat) ก่อนที่จะถูกเรนเดอร์ไปยัง DOM

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

  • เท็มเพลตซอร์ส - มาร์กอัพที่จะถูกโคลนหากจำเป็น หากโคลนมาร์กอัปนี้จะไม่แสดงผลใน DOM
  • แม่แบบอินสแตนซ์ - มาร์กอัปจริงที่จะสร้างการแสดงผลไปยัง DOM หากเกี่ยวข้องกับการโคลนแต่ละตัวอย่างจะเป็นโคลน

มาร์กอัพต่อไปนี้สาธิตสิ่งนี้:

<div ng-repeat="i in [0,1,2]">
    <my-directive>{{i}}</my-directive>
</div>

html แหล่งที่มากำหนด

    <my-directive>{{i}}</my-directive>

ซึ่งทำหน้าที่เป็นเทมเพลตแหล่งที่มา

แต่เนื่องจากถูกห่อหุ้มไว้ในng-repeatคำสั่งเทมเพลตแหล่งข้อมูลนี้จะถูกโคลน (3 ครั้งในกรณีของเรา) โคลนเหล่านี้เป็นแม่แบบอินสแตนซ์แต่ละอันจะปรากฏใน DOM และถูกผูกไว้กับขอบเขตที่เกี่ยวข้อง


23

รวบรวมฟังก์ชั่น

compileฟังก์ชั่นของแต่ละคำสั่งจะเรียกเพียงครั้งเดียวเมื่อบูตเชิงมุม

อย่างเป็นทางการนี้เป็นสถานที่ในการดำเนินการจัดการแม่แบบ (ที่มา) ที่ไม่เกี่ยวข้องกับขอบเขตหรือข้อมูลที่มีผลผูกพัน

หลักนี้จะทำเพื่อการเพิ่มประสิทธิภาพ; พิจารณามาร์กอัปต่อไปนี้:

<tr ng-repeat="raw in raws">
    <my-raw></my-raw>
</tr>

<my-raw>สั่งจะทำให้ชุดใดชุดหนึ่งของ DOM มาร์กอัป ดังนั้นเราสามารถ:

  • อนุญาตให้ng-repeatทำซ้ำเท็มเพลตแหล่งที่มา ( <my-raw>) จากนั้นแก้ไขมาร์กอัพของแต่ละอินสแตนซ์เทมเพลต (นอกcompileฟังก์ชั่น)
  • แก้ไขเท็มเพลตซอร์สเพื่อให้เกี่ยวข้องกับมาร์กอัพที่ต้องการ (ในcompileฟังก์ชัน) จากนั้นอนุญาตให้ng-repeatทำซ้ำ

หากมี 1,000 รายการในrawsคอลเลกชันตัวเลือกหลังอาจเร็วกว่ารายการก่อนหน้า

ทำ:

  • จัดการมาร์กอัพเพื่อทำหน้าที่เป็นเทมเพลตกับอินสแตนซ์ (โคลน)

อย่า

  • แนบตัวจัดการเหตุการณ์
  • ตรวจสอบองค์ประกอบของเด็ก
  • ตั้งค่าการสังเกตเกี่ยวกับคุณลักษณะ
  • ตั้งค่านาฬิกาบนขอบเขต

20

ฟังก์ชั่นควบคุม

controllerฟังก์ชั่นของแต่ละคำสั่งจะถูกเรียกเมื่อใดก็ตามที่องค์ประกอบใหม่ที่เกี่ยวข้องถูกสร้างขึ้น

อย่างเป็นทางการที่controllerฟังก์ชั่นที่หนึ่ง:

  • กำหนดตรรกะควบคุม (วิธีการ) ที่อาจใช้ร่วมกันระหว่างตัวควบคุม
  • เริ่มต้นตัวแปรขอบเขต

อีกครั้งสิ่งสำคัญคือต้องจำไว้ว่าหากคำสั่งเกี่ยวข้องกับขอบเขตที่แยกคุณสมบัติใด ๆ ที่อยู่ภายในที่สืบทอดมาจากขอบเขตหลักยังไม่พร้อมใช้งาน

ทำ:

  • กำหนดตรรกะของตัวควบคุม
  • เริ่มต้นตัวแปรขอบเขต

อย่า:

  • ตรวจสอบองค์ประกอบของเด็ก (อาจยังไม่ได้แสดงผลถูกผูกมัดกับขอบเขต ฯลฯ )

ดีใจที่คุณกล่าวถึงตัวควบคุมภายในคำสั่งเป็นสถานที่ที่ดีในการเริ่มต้นขอบเขต ฉันมีเวลายากที่จะค้นพบสิ่งนั้น
jsbisht

1
ตัวควบคุมไม่ได้ "เริ่มต้นขอบเขต" มันเข้าถึงเฉพาะขอบเขตที่เริ่มต้นแล้วเป็นอิสระจากมัน
Dmitri Zaitsev

@DmitriZaitsev ใส่ใจในรายละเอียด ฉันแก้ไขข้อความ
Izhaki

19

ฟังก์ชั่นโพสต์ลิงค์

เมื่อpost-linkเรียกใช้งานฟังก์ชั่นขั้นตอนก่อนหน้านี้ทั้งหมดเกิดขึ้น - การเชื่อมโยงการแปลงกลับเป็นต้น

โดยทั่วไปจะเป็นที่สำหรับจัดการ DOM ที่แสดงผลเพิ่มเติม

ทำ:

  • จัดการองค์ประกอบ DOM (แสดงผลและสร้างอินสแตนซ์)
  • แนบตัวจัดการเหตุการณ์
  • ตรวจสอบองค์ประกอบของเด็ก
  • ตั้งค่าการสังเกตเกี่ยวกับคุณลักษณะ
  • ตั้งค่านาฬิกาบนขอบเขต

9
ในกรณีที่ทุกคนกำลังใช้ฟังก์ชั่นลิงค์ (โดยไม่ต้องพรีลิงค์หรือโพสต์ลิงค์) คุณควรรู้ว่ามันเทียบเท่ากับโพสต์ลิงค์
Asaf David

15

ฟังก์ชันเชื่อมโยงล่วงหน้า

pre-linkฟังก์ชั่นของแต่ละคำสั่งจะถูกเรียกเมื่อใดก็ตามที่องค์ประกอบใหม่ที่เกี่ยวข้องถูกสร้างขึ้น

ดังที่เห็นก่อนหน้านี้ในส่วนของการรวบรวมคำสั่งpre-linkฟังก์ชันจะถูกเรียกว่า parent-then-child ในขณะที่post-linkฟังก์ชันถูกเรียกchild-then-parentใช้

pre-linkฟังก์ชั่นแทบจะไม่เคยใช้ แต่จะมีประโยชน์ในสถานการณ์พิเศษ ตัวอย่างเช่นเมื่อตัวควบคุมเด็กลงทะเบียนตัวเองกับตัวควบคุมหลัก แต่การลงทะเบียนจะต้องเป็นไปตามparent-then-childแฟชั่น ( ngModelControllerทำสิ่งนี้ด้วยวิธี)

อย่า:

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