AngularJS ng-include: การรวมไฟล์ HTML พร้อมตัวอย่าง

⚡ สรุปอย่างชาญฉลาด

คำสั่ง `ng-include` ใน AngularJS จะดึงข้อมูล รวบรวม และแทรกส่วนของ HTML ภายนอกเข้าไปในหน้าหลัก ทำให้มาร์กอัปที่ใช้ร่วมกัน เช่น ตาราง ส่วนหัว หรือส่วนท้าย อยู่ในไฟล์ย่อยที่นำกลับมาใช้ซ้ำได้ไฟล์เดียว

  • 🔘 บทบาทเชิงสั่งการ: ng-include จะดึงส่วนประกอบภายนอกมาและแทรกเข้าไปเป็นโหนดลูกขององค์ประกอบหลัก
  • ☑️ เส้นทางที่อ้างอิง: ค่า src เป็นนิพจน์ ดังนั้นชื่อไฟล์ที่เป็นตัวอักษรจะต้องมีเครื่องหมายอัญประกาศเดี่ยวอยู่ภายในแอตทริบิวต์
  • ตัวอย่างการทำงาน: Table.html จะแสดงผลผ่าน ng-repeat เมื่อ Main.html แทรก Table.html เข้าไปใน div ของคอนโทรลเลอร์แล้ว
  • 🧪 ขอบเขตของเด็กใหม่: แต่ละคำสั่ง include จะสร้างขอบเขตย่อย ดังนั้นการผูกข้อมูลพื้นฐานจึงต้องใช้สัญลักษณ์จุด
  • 🛠️ ช่องว่างประกอบด้วย: กฎ Same-origin, $sce และการเข้าถึงไฟล์ อธิบายผลลัพธ์ที่ว่างเปล่าส่วนใหญ่ได้
  • 📊 หมายเหตุเกี่ยวกับเวอร์ชัน: การสนับสนุน AngularJS สิ้นสุดลงในเดือนมกราคม 2022; Angular เวอร์ชันใหม่ใช้การประกอบคอมโพเนนต์แทน

คำสั่ง `ng-include` ใน AngularJS ใช้สำหรับแทรกไฟล์ HTML ภายนอกเข้าไปในเทมเพลตหลัก

ตามค่าเริ่มต้น HTML ไม่ได้จัดเตรียมสิ่งอำนวยความสะดวกในการรวมโค้ดฝั่งไคลเอ็นต์จากไฟล์อื่น โดยปกติแล้ว แนวทางปฏิบัติที่ดีในภาษาการเขียนโปรแกรมใดๆ ก็ตามคือการกระจายฟังก์ชันการทำงานไปยังไฟล์ต่างๆ สำหรับแอปพลิเคชันใดๆ

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

โดยปกติจะเป็นแนวคิดของ รวมข้อความ ซึ่งมีจำหน่ายใน การเขียนโปรแกรมภาษา เช่น .Net และ Java.

บทช่วยสอนนี้จะกล่าวถึงวิธีการอื่นๆ ในการจัดการไฟล์ (ไฟล์ที่มีข้อมูลภายนอก) HTML สามารถใส่โค้ดลงในไฟล์ HTML หลักได้

ฝั่งไคลเอ็นต์ประกอบด้วย

หนึ่งในวิธีที่พบบ่อยที่สุดในการแทรกโค้ด HTML คือผ่านทาง Javaต้นฉบับ Javaต้นฉบับ เป็นภาษาโปรแกรมที่สามารถใช้เพื่อจัดการเนื้อหาในหน้า HTML ได้ทันที เพราะฉะนั้น, Javaสคริปต์ยังสามารถใช้เพื่อรวมโค้ดจากไฟล์อื่นได้อีกด้วย

ขั้นตอนด้านล่างนี้แสดงให้เห็นถึงวิธีการดำเนินการดังกล่าว

ขั้นตอน 1) กำหนดไฟล์ชื่อ Sub.html และเพิ่มโค้ดต่อไปนี้ลงในไฟล์

<div>
	This is an included file
</div>

ขั้นตอน 2) สร้างไฟล์ชื่อ Sample.html ซึ่งเป็นไฟล์แอปพลิเคชันหลักของคุณ และเพิ่มข้อมูลโค้ดด้านล่าง

ด้านล่างนี้เป็นประเด็นหลักที่ควรทราบเกี่ยวกับโค้ดด้านล่าง

  1. ในแท็ก body จะมีแท็ก div ซึ่งมีรหัสเนื้อหา นี่คือตำแหน่งที่จะแทรกโค้ดจากไฟล์ภายนอก 'Sub.html'
  2. มีการอ้างอิงถึงสคริปต์ jQuery jQuery เป็นภาษาสคริปต์ที่สร้างขึ้นบนพื้นฐานของ Javaสคริปต์ที่ทำให้การจัดการ DOM ง่ายยิ่งขึ้น
  3. ตัว Vortex Indicator ได้ถูกนำเสนอลงในนิตยสาร Javaในฟังก์ชันสคริปต์ มีคำสั่ง '$(“#Content”).load(“Sub.html”);' ซึ่งจะทำให้โค้ดในไฟล์ Sub.html ถูกแทรกเข้าไปในแท็ก div ที่มี id เป็น Content
    <html> 
    	  <head> 
    	    <script src="jquery.js"></script> 
    	    <script> 
    	    $(function(){
    	      $("#Content").load("Sub.html"); 
    	    });
        </script> 
      </head> 
    
    <body> 
         <div id="Content"></div>
      </body> 
    </html>

ฝั่งเซิร์ฟเวอร์รวมอยู่ด้วย

การรวมฝั่งเซิร์ฟเวอร์ยังพร้อมใช้งานสำหรับการรวมโค้ดทั่วไปทั่วทั้งไซต์ โดยปกติจะทำเพื่อรวมเนื้อหาในส่วนด้านล่างของเอกสาร HTML

  1. ส่วนหัวของหน้า
  2. ส่วนท้ายของหน้า
  3. เมนูนำทาง

เพื่อให้เว็บเซิร์ฟเวอร์รู้จักไฟล์ Server Side Include ไฟล์นั้นจะต้องมีนามสกุลพิเศษ ซึ่งโดยทั่วไปแล้วเว็บเซิร์ฟเวอร์จะยอมรับได้ เช่น .shtml, .stm, .shtm, .cgi

คำสั่งที่ใช้ในการรวมเนื้อหาคือ "คำสั่ง include" ตัวอย่างของคำสั่ง include แสดงอยู่ด้านล่าง:

<!--#include virtual="navigation.cgi" -->
  • คำสั่งข้างต้นอนุญาตให้รวมเนื้อหาของเอกสารหนึ่งไว้ในอีกเอกสารหนึ่งได้
  • โค้ดคำสั่ง "เสมือน" ด้านบนใช้เพื่อระบุเป้าหมายที่สัมพันธ์กับรูทโดเมนของแอปพลิเคชัน
  • นอกจากนี้ สำหรับพารามิเตอร์เสมือน ยังมีพารามิเตอร์ไฟล์ที่สามารถใช้ได้อีกด้วย พารามิเตอร์ "ไฟล์" จะใช้เมื่อจำเป็นต้องระบุเส้นทางที่สัมพันธ์กับไดเร็กทอรีของไฟล์ปัจจุบัน

หมายเหตุ

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

วิธีรวมไฟล์ HTML ใน AngularJS

AngularJS มีฟังก์ชันที่ช่วยให้สามารถรวมฟังก์ชันการทำงานจากไฟล์ AngularJS อื่นๆ ได้โดยใช้คำสั่ง `ng-include` คำสั่ง.

จุดประสงค์หลักของ "คำสั่ง ng-include" คือการดึงข้อมูล รวบรวม และรวมส่วนของ HTML ภายนอกเข้ากับแอปพลิเคชัน AngularJS หลัก

หมายเหตุเกี่ยวกับเวอร์ชัน: AngularJS 1.x สิ้นสุดการสนับสนุนระยะยาว (Long Term Support) เมื่อวันที่ 31 ธันวาคม 2021 และทีมผู้พัฒนาเฟรมเวิร์กได้ระบุว่า การสนับสนุน AngularJS สิ้นสุดลงอย่างเป็นทางการแล้ว ในเดือนมกราคม 2022 ดังนั้นจึงไม่มีการอัปเดตหรือแพตช์ความปลอดภัยใดๆ ตามมา ทุกขั้นตอนและบล็อกโค้ดด้านล่างจะยังคงเหมือนเดิมทุกประการ เพื่อเป็นข้อมูลอ้างอิงทางประวัติศาสตร์ Angular เวอร์ชันใหม่ไม่มีตัวทดแทนโดยตรง: โครงสร้างมาร์กอัปประกอบขึ้นจากคอมโพเนนต์ และแฟรกเมนต์ที่เลือกในขณะรันไทม์จะถูกเรนเดอร์ด้วย ng-container และ ngComponentOutlet.

มาดูโค้ดด้านล่างนี้และอธิบายวิธีการที่จะทำให้สำเร็จโดยใช้ AngularJS กัน

ขั้นตอน 1) มาเขียนโค้ดด้านล่างนี้ในไฟล์ชื่อ Table.html นี่คือไฟล์ที่จะถูกแทรกลงในไฟล์แอปพลิเคชันหลักของเราโดยใช้คำสั่ง ng-include

โค้ดตัวอย่างด้านล่างนี้สมมติว่ามีตัวแปรขอบเขตชื่อ “tutorial” อยู่ จากนั้นจึงใช้ตัวแปรดังกล่าว ng-ซ้ำ คำสั่งนี้จะวนไปทีละหัวข้อในตัวแปร “tutorial” และแสดงค่าสำหรับ 'Name' และ 'Descriptคู่คีย์-ค่าของ ion

<table>
    <tr ng-repeat="Topic in tutorial">
        <td>{{ Topic.Name }}</td>
        <td>{{ Topic.Description }}</td>
    </tr>
</table>

ขั้นตอน 2) มาเขียนโค้ดด้านล่างนี้ลงในไฟล์ชื่อ Main.html กัน นี่คือแอปพลิเคชัน AngularJS อย่างง่ายที่มีลักษณะดังต่อไปนี้

  1. ใช้คำสั่ง “ng-include” เพื่อแทรกโค้ดในไฟล์ภายนอก 'Table.html' ข้อความดังกล่าวได้รับการเน้นด้วยตัวหนาในโค้ดด้านล่าง ดังนั้นแท็ก div ' - จะถูกแทนที่ด้วยโค้ดทั้งหมดในไฟล์ 'Table.html'
  2. ตัว Vortex Indicator ได้ถูกนำเสนอลงในนิตยสาร ตัวควบคุมตัวแปร “tutorial” จะถูกสร้างขึ้นเป็นส่วนหนึ่งของออบเจ็กต์ $scope ตัวแปรนี้ประกอบด้วยรายการคู่คีย์-ค่า

ในตัวอย่างของเรา คู่ค่าคีย์คือ

  1. ชื่อ – หมายถึงชื่อของหัวข้อ เช่น ตัวควบคุม โมเดล และคำสั่ง
  2. Descriptไอออน – อธิบายหัวข้อแต่ละหัวข้อ

ตัวแปรบทช่วยสอนยังเข้าถึงได้ในไฟล์ 'Table.html'

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Event Registration</title>
        <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body ng-app="sampleApp">
<div ng-controller="AngularController">
    <h3> Guru99 Global Event</h3>
		<div ng-include="'Table.html'"></div>
</div>
<script>

    var sampleApp = angular.module('sampleApp',[]);
    sampleApp.controller('AngularController', function($scope) {
        $scope.tutorial =[
            {Name: "Controllers" , Description : "Controllers in action"},
            {Name: "Models" , Description : "Models and binding data"},
            {Name: "Directives" , Description : "Flexibility of Directives"}
        ];

    });
</script>
</body>
</html>

เมื่อคุณรันโค้ดข้างต้นคุณจะได้รับผลลัพธ์ดังต่อไปนี้

เอาท์พุต:

ภาพหน้าจอด้านล่างแสดงผลลัพธ์ที่แสดงผล: แท็บเบราว์เซอร์มีชื่อว่า "การลงทะเบียนกิจกรรม" หัวข้ออยู่เหนือตารางที่แทรกเข้ามา และสามแถวถูกดึงมาจากอาร์เรย์ตัวอย่างในไฟล์ Main.html ผ่านทาง partial ภาพหน้าจอที่มีคำอธิบายประกอบแสดงหัวข้อว่า Gur99 ในขณะที่โค้ดเขียนว่า Guru99.

ผลลัพธ์ที่แสดงในเบราว์เซอร์ของตัวอย่าง AngularJS ng-include ซึ่งแสดงตารางหัวข้อที่แทรกมาจาก Table.html

แอตทริบิวต์ของ ng-include ได้แก่ src, onload และ autoscroll

นอกเหนือจากชื่อไฟล์แล้ว คำสั่งนี้ยังรับแอตทริบิวต์เสริมอีกสองรายการ และสามารถเขียนได้สามวิธี ได้แก่ เป็นแอตทริบิวต์บนองค์ประกอบใดๆ หรือเป็น <ng-include> องค์ประกอบ หรือเป็นคลาส CSS ข้อมูลอ้างอิงด้านล่างนี้เป็นไปตามมาตรฐานอย่างเป็นทางการ เอกสารประกอบ ngInclude.

คุณลักษณะ ต้อง สิ่งที่มันไม่
src หรือ ng-include มี (ใบกำกับภาษีเต็มรูปแบบ) นิพจน์ที่ประเมินค่าได้ตรงกับเทมเพลต URL
กำลังโหลด ไม่ นิพจน์จะถูกประเมินทุกครั้งที่การโหลดส่วนย่อยใหม่เสร็จสิ้น
การเลื่อนอัตโนมัติ ไม่ เรียกใช้ $anchorScroll หลังจากโหลดเนื้อหาเสร็จแล้ว การละเว้นแอตทริบิวต์นี้จะปิดใช้งานการเลื่อน
<div ng-include="'Table.html'" onload="loaded = true" autoscroll></div>
<ng-include src="templateUrl"></ng-include>

ข้อผิดพลาดที่พบบ่อยที่สุดมาจากแถวแรก: src ใช้เวลาและ การแสดงออกไม่ใช่สตริงธรรมดา ดังนั้นเส้นทางตัวอักษรจึงต้องมีเครื่องหมายคำพูดอยู่ภายในแอตทริบิวต์ ซึ่งเป็นเหตุผลว่าทำไมตัวอย่างข้างต้นจึงอ่านว่า ng-include="'Table.html'". การเขียน ng-include="Table.html" ทำให้ AngularJS ประเมินผล Table ในฐานะคุณสมบัติของขอบเขต ให้ค้นหาไม่พบอะไรเลย และปล่อยให้องค์ประกอบนั้นว่างเปล่า

เหตุใด ng-include จึงสร้างขอบเขตย่อยใหม่

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

การอ่านทำงานได้ตามที่คาดไว้ ข้อความบางส่วนด้านบนอ่านได้ดังนี้ tutorial มาจากผู้ปกครองโดยตรง ซึ่งเป็นเหตุผลว่าทำไม ng-repeat พบอาร์เรย์ ปัญหาอยู่ที่การเขียน: เมื่อการกำหนดค่าบางส่วนให้กับประเภทข้อมูลพื้นฐาน เช่นเดียวกับ ng-model="topic"AngularJS สร้าง topic ในขอบเขตของเด็ก สำเนาของผู้ปกครองจะไม่เปลี่ยนแปลง

มีสองพฤติกรรมที่จะช่วยหลีกเลี่ยงกับดักนี้:

  • ผูกผ่านวัตถุ เช่น ng-model="data.topic"ดังนั้นทั้งผู้ปกครองและเด็กจึงเข้าถึงข้อมูลอ้างอิงเดียวกัน
  • เก็บสถานะไว้ในคอนโทรลเลอร์หรือเซอร์วิส แล้วเปิดเผยฟังก์ชันแทนที่จะใช้ค่าพื้นฐานแบบเปล่าๆ

ส่วนประกอบนี้ยังปล่อยสัญญาณออกมาด้วย $includeContentRequested, $includeContentLoaded และ $includeContentErrorดังนั้นผู้ปกครองจึงสามารถตอบสนองได้ผ่านทาง $scope.$on โดยไม่เข้าไปยุ่งเกี่ยวกับเด็กเลยแม้แต่น้อย

วิธีแก้ไขปัญหา ng-include ว่างเปล่าหรือล้มเหลว

คำสั่ง include ที่ไม่สร้างผลลัพธ์ใดๆ มักล้มเหลวด้วยเหตุผลบางประการ ลองตรวจสอบทีละข้อตามลำดับนี้

  1. ตรวจสอบใบเสนอราคา ชื่อไฟล์ที่ถูกต้องจะต้องมีเครื่องหมายอัญประกาศเดี่ยวอยู่ภายในค่าแอตริบิวต์ หากไม่มีเครื่องหมายอัญประกาศเดี่ยว ค่าที่ได้จะเป็น undefined และจะไม่มีการส่งคำขอใดๆ
  2. แสดงผลหน้าเว็บผ่าน HTTP คำสั่งดังกล่าวจะส่งคำขอ XHR สำหรับไฟล์ย่อย ดังนั้นการเปิดไฟล์ Main.html โดยตรงจากดิสก์ผ่านที่อยู่ไฟล์จึงล้มเหลวในเบราว์เซอร์ส่วนใหญ่ แต่เว็บเซิร์ฟเวอร์ภายในเครื่องจะแก้ไขปัญหานี้ได้
  3. ตรวจสอบว่าเส้นทางถูกต้องหรือไม่ สังเกตแผงเครือข่ายเพื่อดูคำขอ หากพบรหัส 404 หมายความว่าเส้นทางนั้นเป็นแบบสัมพัทธ์กับหน้าเว็บที่โฮสต์ไฟล์ที่รวมเข้ามา ไม่ใช่กับไฟล์ย่อยที่อ้างอิงถึงไฟล์นั้น
  4. เคารพกฎว่าด้วยแหล่งกำเนิดเดียวกัน โดยค่าเริ่มต้น เทมเพลต URL ต้องตรงกับโดเมนและโปรโตคอลของเอกสารแอปพลิเคชัน เนื่องจาก AngularJS จะส่งผ่านไป $sce.getTrustedResourceUrlแม่แบบจากแหล่งอื่นจำเป็นต้องมีรายการในแหล่งข้อมูลที่เชื่อถือได้ URL หรือใช้ลิสต์ หรือตัวห่อ trustAsResourceUrl ที่ระบุอย่างชัดเจน และเซิร์ฟเวอร์ระยะไกลยังคงต้องส่งส่วนหัว CORS อยู่ดี
  5. จงฟังเสียงแห่งความล้มเหลว $includeContentError จะทำงานเมื่อใดก็ตามที่สถานะการตอบกลับอยู่นอกช่วง 200 ถึง 299 โดยจะเปลี่ยนพื้นที่ว่างเปล่าที่ไม่มีการแจ้งเตือนใดๆ ให้กลายเป็นข้อความที่ควรบันทึกไว้

อีกสาเหตุหนึ่งที่มองข้ามได้ง่ายคือ AngularJS จะเก็บ partial ที่ดึงมาทั้งหมดไว้ใน $templateCache ดังนั้นไฟล์ที่แก้ไขแล้วอาจยังคงแสดงมาร์กอัปที่ล้าสมัยต่อไปจนกว่ารายการในแคชจะถูกลบออกหรือหน้าเว็บจะถูกโหลดใหม่จากซอร์สโค้ด เมื่อ fragment ต้องการพฤติกรรมของตัวเองมากกว่าแค่มาร์กอัปอย่างเดียว... คำสั่งแบบกำหนดเอง เครื่องมือที่มีเทมเพลตเฉพาะและขอบเขตการทำงานที่แยกต่างหากนั้นเป็นเครื่องมือที่ดีกว่า

คำถามที่พบบ่อย

`ng-include` จะแทรก fragment ที่คุณตั้งชื่อเองลงไปในส่วนใดก็ได้ของหน้าเว็บ ส่วน `ng-view` จะแสดงผลเทมเพลตใดก็ตามที่ `ng-view` กำหนด เราเตอร์ แผนที่ไปยังปัจจุบัน URLและในเลย์เอาต์หนึ่งๆ จะมี ng-view เพียงหนึ่งเดียวเท่านั้น

ไม่ ส่วนประกอบย่อยจะเก็บเพียงเศษเสี้ยวเท่านั้น ห่อหุ้มping การใช้แท็ก head หรือ body ใน HTML จะทำให้มีองค์ประกอบที่ไม่ต้องการหลงเหลืออยู่ เนื่องจากมาร์กอัปจะถูกแทรกเป็นโหนดลูกขององค์ประกอบหลัก แทนที่จะถูกแยกวิเคราะห์เป็นเอกสาร

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

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

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

ไม่มีสิ่งใดเทียบเท่าได้ มาร์กอัปที่นำกลับมาใช้ใหม่จะกลายเป็นคอมโพเนนต์ และแฟรกเมนต์ที่เลือกในขณะรันไทม์จะถูกเรนเดอร์ผ่าน ngComponentOutlet หรือ ngTemplateOutlet ที่อยู่ภายใน ng-container

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

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

สรุปโพสต์นี้ด้วย: