คุณเคยได้ยินเรื่องราวมาก่อน คุณมีแอปพลิเคชันที่ทำงานบน API แบ็คเอนด์ที่กระจายอำนาจและทำงานได้อย่างสมบูรณ์ และฟรอนต์เอนด์ที่สร้างด้วยชุดเครื่องมือทั่วไป
ตอนนี้คุณต้องการย้ายไปที่ Angular หรือบางทีคุณแค่กำลังมองหาวิธีผสานรวม Angular กับโปรเจ็กต์ Rails ของคุณ เพราะคุณชอบสิ่งนี้มากกว่า เราไม่โทษคุณ
ด้วยวิธีการดังกล่าว คุณสามารถใช้ประโยชน์จากทั้งสองโลกและตัดสินใจว่าคุณต้องการใช้คุณลักษณะจาก Rails หรือ Angular เพื่อจัดรูปแบบสิ่งต่างๆ เป็นต้น
สิ่งที่เราจะสร้าง
ไม่จำเป็นต้องกังวล บทช่วยสอนนี้เขียนขึ้นเพื่อการนี้ เราจะเจาะลึกการสร้างแอปพลิเคชัน CRUD ที่ทำงานได้อย่างสมบูรณ์เหนือโดเมนของผู้ใช้
ในตอนท้ายของบทความ คุณจะได้เรียนรู้แนวคิดพื้นฐานเกี่ยวกับ Angular และวิธีตั้งค่าโครงการส่วนหลังของ Rails ที่ผสานรวมกับ Angular สำหรับ front-end โดยตรงดังที่แสดงด้านล่าง:
CRUD ของผู้ใช้ที่สร้างด้วย Rails และ Angular
แอปพลิเคชันจะจัดการการดำเนินการ CRUD ทั้งสี่บนโดเมนของผู้ใช้ที่ดึงมาจากบริการเว็บทดสอบปลอมภายนอก แอปจะถูกสร้างขึ้นบนสถาปัตยกรรม MVC โดยแต่ละเลเยอร์ของ Angular จะอธิบายโดยละเอียดเพื่อช่วยให้คุณเข้าใจได้ดีขึ้นว่าสิ่งต่าง ๆ เชื่อมโยงกันอย่างไร สไตล์ขึ้นอยู่กับ Bootstrap
ตั้งค่า
อย่างที่คุณอาจเดาได้ เราจำเป็นต้องใช้ซอฟต์แวร์ต่อไปนี้:
- Ruby (ฉันเลือกเวอร์ชัน 2.7.0preview1),
- Ruby and Rails (ฉันใช้เวอร์ชัน 5.0.7.2)
- Node.js (ฉันกำลังใช้ v13.7.0)
- เส้นด้าย (อย่างน้อยเวอร์ชัน 1.22.4)
ตรวจสอบให้แน่ใจว่าได้ติดตั้งทุกอย่างอย่างถูกต้อง จากนั้นเราก็ไปต่อกันที่โครงการ เลือกโฟลเดอร์ที่คุณต้องการและเรียกใช้คำสั่งต่อไปนี้:
rails new crud-rails-angular
รอให้การตั้งค่าเสร็จสิ้นและเปิดโครงการภายใน IDE ที่คุณชื่นชอบ เราจะทำงานกับ VS Code สำหรับบทความนี้เพราะว่าง่าย มีประสิทธิภาพ และรวมทั้งไวยากรณ์ Rails และ Angular อย่างราบรื่น
หากคุณใช้ Rails 5 มาระยะหนึ่งแล้ว คุณอาจสังเกตเห็นว่ามันเป็นnew
คำสั่งสร้างจุดบกพร่องภายใน Gemfile สำหรับการกำหนดค่า SQLite มันมาโดยไม่มีเวอร์ชันขั้นต่ำ และนั่นจะทำให้มันทำงานโดยมีข้อผิดพลาด มาแก้ไขปัญหานี้โดยอัปเดตเป็น
gem 'sqlite3', '~> 1.3.10'
สมบูรณ์แบบ!
การตั้งค่าเว็บแพ็คเกอร์
วิธีที่ดีที่สุดในการจัดการแอปพลิเคชันที่เหมือน JavaScript ใน Rails คือการใช้ Webpacker โดยใช้ประโยชน์จาก Webpack เบื้องหลังเพื่อมอบคุณลักษณะต่างๆ เช่น การประมวลผลล่วงหน้าและการรวมแอป JavaScript เช่น Angular ลงในแอปพลิเคชัน Rails ที่มีอยู่
ในการติดตั้ง เพียงเพิ่มบรรทัดใหม่ใน Gemfile . ของคุณ :
gem 'webpacker', '~> 4.3.x'
ซึ่งจะทำให้มั่นใจได้ว่าคุณจะติดตั้งเวอร์ชันล่าสุด ถัดไป ให้รันคำสั่งต่อไปนี้:
bundle install
bundle exec rake webpacker:install
bundle exec rake webpacker:install:angular
คำสั่งแรกจะดาวน์โหลดและอัปเดตการพึ่งพา Rails ที่เพิ่มเข้ามา
อันที่สองเทียบเท่ากับ npm install
เนื่องจากมันสร้าง node_modules และติดตั้งการขึ้นต่อกันของ Angular ที่จำเป็น เช่น Babel, Sass, Browserlist และ Webpack ตอนนี้ เรามีทั้งแอป Node และ Rails ในโครงการเดียวกัน
ในคำสั่งล่าสุด เรามี npm install angular
ซึ่งจะดาวน์โหลดการพึ่งพาที่ต้องใช้ Angular ทั้งหมดและทำให้ทำงานควบคู่ไปกับโปรเจ็กต์ Rails ของเรา
ที่ส่วนท้ายของคำสั่งเหล่านี้ คุณยังสามารถดู package.json สร้างไฟล์แล้ว การอ้างอิงที่จำเป็นทั้งหมดของเราจะถูกวางไว้ที่นั่น และคุณสามารถเพิ่มสิ่งที่คุณต้องการได้ในอนาคต
นอกจากนี้ บางโฟลเดอร์และไฟล์ยังถูกสร้างขึ้นภายใต้ /app โฟลเดอร์ เช่น /javascript . ใหม่ . ในโฟลเดอร์นี้ คุณมี /hello_angular . อยู่แล้ว โฟลเดอร์ที่สร้างขึ้นเพื่อรองรับการเริ่มต้นการพัฒนาของคุณ
เพื่อให้ได้เวลา ฉันจะขอให้คุณทำมิเรอร์โฟลเดอร์และโครงสร้างไฟล์ของคุณด้วยสิ่งต่อไปนี้:
การปรับเชิงมุมบางส่วน
Webpacker แนะนำชุดการปรับปรุงภายในโครงการ Rails ที่คุณสร้างขึ้น เลยใช้เวลาจัดระเบียบบ้านกันสักหน่อย
ขั้นแรก เปิด application.js . ของคุณ ไฟล์อยู่ใต้ /packs โฟลเดอร์ (ดังรูปด้านบน) และเพิ่มรหัสต่อไปนี้:
import "core-js/stable";
import "regenerator-runtime/runtime";
การนำเข้าเหล่านี้ทำงานเป็นกำลังเสริมเพื่อทำให้สภาพแวดล้อม JavaScript ภายในโปรเจ็กต์ Rails มีความเสถียร
ตอนนี้ เราต้องแจ้ง Rails ว่าต้องเลือกเอาท์พุตไปยังหน้าใด เมื่อ Webpacker แพ็คของเสร็จแล้ว มันจะสร้างไฟล์สแตติกที่สามารถแจกจ่ายได้จำนวนมากที่ Rails ต้องระวัง
ไปที่ application.html.erb ไฟล์ภายใต้ app/views/layout โฟลเดอร์และเปลี่ยน <head>
แท็กเนื้อหาต่อไปนี้:
<head>
<title>CrudRailsAngular</title>
<base href="/" />
<!-- 1 -->
<%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" />
<!-- 2 -->
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application' %>
<!-- 3 -->
</head>
มาแบ่งเรื่องนี้กันสักหน่อย:
- ที่นี่ เรากำลังเพิ่ม
base
ซึ่งบอก Rails ว่าจะต้องดูที่ไหนเมื่อแอปพลิเคชันเริ่มทำงาน - เราจะใช้ Bootstrap เพื่อสรุปรูปแบบให้กับเพจ เพื่อให้เราสามารถมุ่งเน้นที่การใช้งานเท่านั้น
- นี่คือตำแหน่งที่คุณต้องวางแท็ก Webpacker ที่แมปกับ /packs เนื้อหาของโฟลเดอร์ (ซึ่ง Webpacker จะสร้างโดยอัตโนมัติหลังการคอมไพล์ทุกครั้ง)
โมเดลและฐานข้อมูล
ไปที่การตั้งค่าฐานข้อมูล เพื่อให้สิ่งต่าง ๆ เร็วขึ้น เราจะสร้างโมเดลใหม่ที่ชื่อว่า User
. นี่คือคำสั่งที่คุณต้องเรียกใช้เพื่อให้บรรลุ:
rails g scaffold User name:string age:integer address:text && rake db:migrate
มันจะสร้างโฟลเดอร์และไฟล์ของโมเดลทั้งหมดของเรา ซึ่งเราจะต้องทำให้ Rails จัดการข้อมูลฐานข้อมูล (จาก SQLite) และเก็บไว้ในการดำเนินการ CRUD ของเรา
แล้วคุณจะเห็นไฟล์ใหม่ XXX1_create_users.rb ถูกสร้างขึ้นภายใต้ db/migrate/ โฟลเดอร์ เปิดแล้วคุณจะเห็น CreateUsers
. ที่สร้างขึ้นใหม่ บันทึก
ภายใน app/models/ โฟลเดอร์ คุณจะเห็น User
. ที่สร้างขึ้นแล้ว โมเดลที่ user.rb ไฟล์.
ตอนนี้ เปิด db/seeds.rb ไฟล์และเพิ่มรหัสต่อไปนี้:
User.create(name: 'Luke Wan', age: 23, address: '123 Test St.')
User.create(name: 'Mary Poppins', age: 41, address: '123 ABC St.')
User.create(name: 'John Neilman', age: 76, address: '000 Test St.')
รหัสนี้จะเริ่มต้น Users
. ของเรา ตารางที่มีข้อมูลบางส่วนเมื่อเริ่มต้น บันทึกและเรียกใช้คำสั่ง:
rake db:seed
สิ่งนี้จะทำให้ตารางผ่านคำสั่งที่แสดงด้านบน ถัดไป คุณสามารถเข้าสู่ฐานข้อมูล SQLite และตรวจสอบว่าใช้งานได้หรือไม่โดยใช้คำสั่ง:
sqlite3 db/development.sqlite3
จากนั้นเลือกข้อมูลตาราง:
select * from users;
และคุณอาจเห็นผล
ส่วนประกอบผู้ใช้
คุณจะต้องติดตั้งการพึ่งพาอีกสองสามรายการเพื่อช่วยในการแปลง HTML และ CSS เป็นหน้า Rails ของเรา เพิ่มเราเตอร์ Angular, form libs และ ngx-bootstrap ซึ่งเราจะใช้เพื่ออำนวยความสะดวกในการสร้างและจัดการส่วนประกอบ Bootstrap ดังนั้น ให้ออกคำสั่งต่อไปนี้:
yarn add @angular/router @angular/forms html-loader css-loader ngx-bootstrap
อย่างไรก็ตาม ก่อนที่เราจะข้ามไปยังโค้ดส่วนประกอบ มีแนวคิดที่สำคัญบางอย่างที่เราต้องชี้ให้เห็น โดยเริ่มจากกายวิภาคขององค์ประกอบเชิงมุม
ส่วนประกอบคืออะไร
ใน Angular มีส่วนประกอบเพื่อเชื่อมต่อมุมมองของคุณกับตรรกะของแอปพลิเคชันที่สร้างใน TypeScript
กล่าวอีกนัยหนึ่ง คอมโพเนนต์เป็นเหมือนคอนเทนเนอร์สำหรับตรรกะทั้งหมดที่มุมมองของคุณจำเป็นต้องสนับสนุนการทำงาน กำหนดค่าที่มุมมองจะแสดงและควบคุมโฟลว์ของพวกเขา มันเทียบเท่ากับ 'คอนโทรลเลอร์' ในเฟรมเวิร์กที่คล้ายคลึงกัน
ในการสร้างส่วนประกอบ สิ่งที่คุณต้องทำคือกำหนดคลาสใหม่ ใช้งาน OnInit
อินเทอร์เฟซ และใส่คำอธิบายประกอบคลาสด้วย @Component
มัณฑนากร:
export class UserIndexComponent implements OnInit {
constructor() { ... }
ngOnInit() { ... }
}
@Component และ OnInit
@Component
มัณฑนากรมีความสำคัญเนื่องจากจะทำเครื่องหมายคลาสนี้เป็นองค์ประกอบเชิงมุมที่จดจำได้ และจัดเตรียมการกำหนดค่าข้อมูลเมตาที่ช่วยให้ Angular จัดการกับพวกมันเกี่ยวกับการประมวลผล การสร้างอินสแตนซ์ และการใช้งานระหว่างรันไทม์
ใช้การกำหนดค่าข้อมูลเมตาต่อไปนี้:
@Component({
selector: "users",
template: templateString,
})
ที่นี่ selector
บอก Angular ว่าค่าที่ระบุคือตัวเลือก CSS ที่อาจใช้เพื่อระบุคำสั่งปัจจุบันในเทมเพลต ใช่ มันเป็นเทมเพลตเดียวกันกับคุณสมบัติข้อมูลเมตาถัดไป
OnInit
อย่างไรก็ตาม อินเทอร์เฟซเป็นทางเลือก และเป็นวิธีที่ดีในการเริ่มต้นสิ่งต่าง ๆ ก่อนที่ส่วนประกอบจะสิ้นสุดวงจรชีวิต มันทำงานเหมือนวิธีการหลังการสร้าง
การพึ่งพาการฉีด
Angular เป็น DI (Dependency Injection ) เฟรมเวิร์ก ซึ่งเป็นคุณลักษณะที่เพิ่มโมดูลและประสิทธิภาพการทำงาน
การพึ่งพาใน Angular อาจแตกต่างกันไปจากบริการและพื้นที่เก็บข้อมูลของคุณ ไปจนถึงวัตถุธรรมดาประเภทใดก็ตามที่คุณรู้สึกว่าเหมาะสมที่จะถูกฉีดไปที่อื่นในโค้ด
หากต้องการเปลี่ยนคลาส "injectable" คุณจะต้องใส่คำอธิบายประกอบด้วย @Injectable
มัณฑนากร:
@Injectable({
providedIn: "root",
})
export class UserService {
...
}
providedIn
ระบุว่าหัวฉีดใดจะให้หัวฉีดที่คุณกำลังสร้าง root
ค่าบอก Angular ว่าหัวฉีดควรเป็นระดับแอปพลิเคชัน มีอีกมากมายที่คุณสามารถตรวจสอบได้ที่นี่
ในการใส่คลาสลงในส่วนประกอบ เช่น คุณขอให้ Angular ทำใน Constructor ของส่วนประกอบ:
constructor(
private userService: UserService,
) {}
ง่ายนิดเดียว!
ส่วนประกอบสำเร็จรูป
ด้านล่างนี้ คุณจะพบรายการรหัสสุดท้ายสำหรับองค์ประกอบผู้ใช้ของเรา วางลงใน index.component.ts ภายใต้ javascript/hello_angular/app/ โฟลเดอร์
import { Component, OnInit, TemplateRef } from "@angular/core";
import { FormGroup, FormBuilder } from "@angular/forms";
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
import templateString from "./index.component.html";
import { UserService } from "../user.service";
import { User } from "../user.class";
@Component({
selector: "users",
template: templateString,
})
export class UserIndexComponent implements OnInit {
users: User[];
modalRef: BsModalRef;
userForm: FormGroup;
isNew: Boolean;
constructor(public fb: FormBuilder, private userService: UserService, private modalService: BsModalService) {}
public newUser(template: TemplateRef<any>) {
this.reset();
this.modalRef = this.modalService.show(template);
}
public createUser() {
this.userService.create(this.userForm.value).subscribe(() => {
console.log("User created!");
this.reset();
this.modalRef.hide();
});
}
public editUser(user, template: TemplateRef<any>) {
this.isNew = false;
this.userForm = this.fb.group({
id: [user.id],
name: [user.name],
age: [user.age],
address: [user.address],
});
this.modalRef = this.modalService.show(template);
}
public updateUser() {
const { id } = this.userForm.value;
this.userService.update(id, this.userForm.value).subscribe(() => {
console.log("User updated!");
this.reset();
this.modalRef.hide();
});
}
public deleteUser(id) {
if (confirm("Are you sure?")) {
this.userService.delete(id).subscribe(() => {
console.log("User deleted!");
this.reset();
});
}
}
ngOnInit() {
this.reset();
}
public reset() {
this.isNew = true;
this.userService.getUsers().subscribe((users) => {
this.users = users;
});
this.userForm = this.fb.group({
id: [""],
name: [""],
age: [""],
address: [""],
});
}
}
Users
array จะเก็บข้อมูลตารางปัจจุบันที่แสดงบนหน้าจอและดึงมาจาก reset
วิธีที่ในทางกลับกันเรียก Rails API ของเราผ่าน UserService
(กำลังสร้าง)
userForm
เป็นเพียงข้อมูลอ้างอิงเพื่อช่วยสร้างและอัปเดตผู้ใช้ของเรา เนื่องจากจะใช้แบบฟอร์มเดียวกันสำหรับการดำเนินการทั้งสองอย่าง isNew
ช่วยในการระบุกระแสที่เราอยู่ในขณะนี้
ที่นี่ เรามีวิธีการเทียบเท่า CRUD สำหรับการดำเนินการแต่ละครั้ง แต่ละคนเรียก UserService
. ตามลำดับ วิธีการส่งกระบวนการใน Rails API
นอกจากนี้เรายังต้องตั้งค่าโมดูล HTML เพื่อแปลงเทมเพลตของเราเป็น HTML (เราจะเห็นเพิ่มเติมเกี่ยวกับโมดูลในเร็วๆ นี้) ดังนั้น เปิด html.d.ts ไฟล์ภายในโฟลเดอร์เดียวกันและเพิ่ม:
declare module "*.html" {
const content: string;
export default content;
}
บริการและแบบจำลองเชิงมุม
ไปที่ UserService
. ของ Angular กัน การสร้าง Angular เป็นเฟรมเวิร์ก เช่นเดียวกับ Rails ดังนั้นจึงหมายความว่าสามารถปฏิบัติตามกฎแม้ว่าจะหมายถึงการมีโมเดลที่ซ้ำกัน (หรือคล้ายกันมาก) เป็นต้น
โมเดลคืออะไร
โมเดลเชิงมุมเป็นออบเจ็กต์ง่ายๆ ที่เก็บแอตทริบิวต์ข้อมูลที่เหมาะสมร่วมกัน (กล่าวคือ เป็นส่วนประกอบสั้นๆ ของโดเมนของคุณ) เหมือนกับรุ่นอื่นๆ ในภาษาและเฟรมเวิร์กส่วนใหญ่
การรวมข้อมูลของคุณไว้ในที่เดียวช่วยได้มาก แทนที่จะทำซ้ำตลอดทั้งโค้ดเหมือนที่เราทำกับโมเดลผู้ใช้ของเรา:
export class User {
constructor(public id: number, public name: string, public age: number, public address: string) {}
}
โปรดจำไว้ว่านี่คือ TypeScript ดังนั้นแอตทริบิวต์ของโมเดลของคุณต้องมีประเภทที่กำหนดไว้เสมอ
สร้างไฟล์ใหม่ชื่อ user.class.ts ภายใต้ javascript/hello_angular/app/user/ โฟลเดอร์แล้ววางโค้ดด้านบนลงไป
แล้วบริการล่ะ
บริการเป็นแนวคิดที่กว้าง แต่เราสามารถเข้าใจได้ว่าเป็นออบเจกต์ที่มีการกำหนดไว้อย่างดีและมีจุดประสงค์ ช่วยส่วนประกอบที่มีตรรกะที่ซับซ้อนมากขึ้น โดยให้บริการด้วยข้อมูลที่ประมวลผลและแปลงแล้ว ซึ่งมักจะมาจากบริการภายนอกหรือฐานข้อมูล
บริการไม่จำเป็นต้องมีคำอธิบายประกอบหรืออินเทอร์เฟซเฉพาะใดๆ คุณเพียงแค่สร้างคลาสและทำให้ ฉีดได้ อย่างที่เราเคยเห็นมาก่อน จากนั้นคุณสามารถฉีดเข้าไปในส่วนประกอบของคุณได้
บริการที่สังเกตได้
คุณสมบัติที่น่าสนใจอีกอย่างของ Angular คือช่วยให้คุณใช้ RxJS กับคลาสของคุณได้
ตัวอย่างเช่น ไคลเอ็นต์ HTTP เริ่มต้นของ Angular ซึ่งเป็นไคลเอ็นต์เดียวกับที่เราจะใช้เพื่อดึงข้อมูลจากบริการภายนอก ส่งคืน RxJS Observables
. นี่คือเหตุผล เมื่อคุณเรียกใช้ UserService
. ของเรา เมธอดภายในคอมโพเนนต์ผู้ใช้ คุณอาจ subscribe
ไปที่ Observable
ผลลัพธ์:
this.userService.getUsers().subscribe((users) => {
this.users = users;
});
โปรดทราบว่าหากคุณไม่คุ้นเคยกับ RxJS เราขอแนะนำให้คุณอ่านเอกสารประกอบโดยย่อ ไม่ยากขนาดนั้น!;)
อีกครั้งใน javascript/hello_angular/app/user/ ให้สร้างไฟล์ใหม่ชื่อ user.service.ts . นี่คือเนื้อหา:
import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { map } from "rxjs/operators";
import { Observable } from "rxjs";
import { User } from "./user.class";
@Injectable({
providedIn: "root",
})
export class UserService {
constructor(private http: HttpClient) {}
httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
}),
};
getUsers(): Observable<User[]> {
return this.http.get("/users.json").pipe(
map((users: User[]) =>
users.map((user) => {
return new User(user.id, user.name, user.age, user.address);
})
)
);
}
create(user): Observable<User> {
return this.http.post<User>("/users.json", JSON.stringify(user), this.httpOptions);
}
update(id, user): Observable<User> {
return this.http.put<User>("/users/" + id + ".json", JSON.stringify(user), this.httpOptions);
}
delete(id) {
return this.http.delete<User>("/users/" + id + ".json", this.httpOptions);
}
}
คุณเห็นความคล้ายคลึงกันระหว่างองค์ประกอบนี้กับองค์ประกอบที่เราเพิ่งสร้างขึ้นหรือไม่? เนื่องจากเราต้องการการดำเนินการที่เกี่ยวข้องเพื่อสนับสนุนการดำเนินการในองค์ประกอบ
โปรดทราบว่า HttpClient
ต้องฉีดเข้าไปในตัวสร้างของชั้นเรียนด้วย เราจึงสามารถใช้มันร่วมกับชั้นเรียนได้
การดำเนินการแต่ละครั้งจะเรียก HTTP ไปยัง Rails API ซึ่งเป็นการดำเนินการที่สร้างขึ้นโดยอัตโนมัติ
จำนวนการดู
Angular ทำงานร่วมกับเทมเพลตสำหรับมุมมอง เทมเพลตคือการผสมผสานระหว่าง HTML และ JavaScript แบบลำดับชั้นที่บอก Angular ถึงวิธีการแสดงแต่ละองค์ประกอบ
อย่างไรก็ตาม ก่อนที่จะดำเนินการสร้างมุมมองของเราต่อไป เรามาทำความเข้าใจว่า Angular แยกระบบเทมเพลตอย่างไร
คำสั่งเชิงมุม
เนื่องจากเทมเพลตเชิงมุมเป็นไดนามิกที่สำคัญ คำสั่ง . บางส่วน จำเป็นต้องขับเคลื่อน Angular ผ่านวิธีการเรนเดอร์ที่ถูกต้อง
คำสั่งเป็นเพียงคลาสที่มี @Directive
มัณฑนากรเช่นส่วนประกอบ ใช่ @Component
สืบทอดมาจาก @Directive
ดังนั้นจึงเป็นคำสั่งอย่างเป็นทางการด้วย
อย่างไรก็ตาม ยังมีอีกสองประเภท:โครงสร้าง และ แอตทริบิวต์ คำสั่ง
คำสั่งโครงสร้าง
คำสั่งเหล่านี้แสดงถึงโครงสร้างแบบมีเงื่อนไขและแบบวนซ้ำที่แปลจาก JavaScript เป็นเทมเพลตเชิงมุม ช่วยทำให้เทมเพลตมีไดนามิกมากที่สุด เช่น คุณกำลังเขียนโปรแกรมภายในโค้ด JavaScript ของวานิลลา ยกตัวอย่างต่อไปนี้:
<tr *ngFor="let user of users">
<td>{{ user.name }}</td>
</tr>
*ngFor
directive บอกให้ Angular วนซ้ำอาร์เรย์ users
และพิมพ์ชื่อผู้ใช้แต่ละรายไปที่ DOM
คำสั่งแอตทริบิวต์
สิ่งเหล่านี้ทำงานโดยตรงกับลักษณะที่ปรากฏหรือพฤติกรรมขององค์ประกอบ ยกตัวอย่างต่อไปนี้:
<form [formGroup]="userForm" (ngSubmit)="isNew ? createUser() : updateUser()" novalidate></form>
ที่นี่ เรากำลังแก้ไขลักษณะการทำงานของแบบฟอร์มโดยการตั้งค่า submit
. แบบมีเงื่อนไข ฟังก์ชันและการใช้ประโยชน์จาก FormGroup
. ของ Angular เพื่อผูกข้อมูลแต่ละอินพุตของแบบฟอร์ม
การผูกข้อมูล
การสร้างฟอร์มด้วยเฟรมเวิร์กของเว็บอาจเป็นงานที่ยุ่งยากและเกิดข้อผิดพลาดได้ หากไม่มีการเชื่อมโยงข้อมูล
Angular รองรับการเชื่อมโยงข้อมูลแบบสองทาง ซึ่งหมายความว่าคุณสามารถเชื่อมต่อส่วนต่างๆ ของเทมเพลตกับส่วนประกอบได้โดยตรง และในทางกลับกัน
แบบฟอร์มด้านบนเป็นตัวอย่างที่ดีของ FormGroup
อำนาจผูกข้อมูล มันผูกแต่ละฟิลด์ของแบบฟอร์มกับ userForm
. โดยอัตโนมัติ วัตถุที่สร้างขึ้นภายในองค์ประกอบของเรา
ใน editUser
ตัวอย่างเช่น คุณสามารถดูการโยงเวอร์ชันตรงกันข้าม ซึ่ง userForm
ค่าของถูกกำหนดไว้ภายในองค์ประกอบและจะสะท้อนถึงรูปแบบในมุมมอง
การสร้างมุมมองดัชนี
มาแยกย่อยเนื้อหาสำหรับ index.component.html ออกเป็นสองส่วน นี่คืออันแรก:
<div class="container pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
<h1 class="display-4">User's Listing</h1>
<p class="lead">A quick CRUD example of how to integrate Rails with Angular</p>
<table class="table">
<tr>
<th>Id</th>
<th>Name</th>
<th>Age</th>
<th>Address</th>
<th>Actions</th>
</tr>
<tbody>
<tr *ngFor="let user of users">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
<td>{{ user.address }}</td>
<td colspan="2">
<button class="btn btn-secondary" (click)="editUser(user, template)">Edit</button>
|
<button class="btn btn-danger" (click)="deleteUser(user.id)">Delete</button>
</td>
</tr>
</tbody>
</table>
<button class="btn btn-primary float-right mt-4" (click)="newUser(template)">Insert New</button>
</div>
ส่วนใหญ่ประกอบด้วย HTML ธรรมดา เราจะไม่ลงรายละเอียดเกี่ยวกับคลาส Bootstrap
ส่วนสำคัญที่นี่คือ ngFor
คำสั่งในแถวของตาราง มันช่วยวนซ้ำ Users
array (จำได้ไหม?) พิมพ์แต่ละแอตทริบิวต์ไปยังเอาต์พุต HTML ผ่าน {{ … }}
โอเปอเรเตอร์
เมื่อใดก็ตามที่คุณต้องการเพิ่มเหตุการณ์ DOM เช่น onClick เพียงใส่วงเล็บชื่อเหตุการณ์แล้วเพิ่มฟังก์ชันคอมโพเนนต์ที่จะเรียกเมื่อคลิก
การสร้างมุมมองโมดอล
ส่วนที่สองเกี่ยวข้องกับเนื้อหาที่เป็นกิริยาช่วย ดังนั้นให้เพิ่มไว้ด้านล่างส่วนก่อนหน้า:
<ng-template #template>
<div class="modal-header">
<h4 class="modal-title pull-left">{{ isNew ? "New User" : "Update User" }}</h4>
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form [formGroup]="userForm" (ngSubmit)="isNew ? createUser() : updateUser()" novalidate>
<input type="hidden" formControlName="id" class="form-control" />
<div class="form-group">
<label>Name</label>
<input type="text" formControlName="name" class="form-control" />
</div>
<div class="form-group">
<label>Age</label>
<input type="text" formControlName="age" class="form-control" />
</div>
<div class="form-group">
<label>Address</label>
<textarea class="form-control" formControlName="address" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</ng-template>
โปรดทราบว่าเรากำลังใช้ <ng-template>
ซึ่งช่วยให้คุณสามารถยึดองค์ประกอบระหว่าง HTML และ Angular รหัสเทมเพลตจะอยู่หลัง #
ลงชื่อ
ภายในแบบฟอร์ม โปรดทราบว่าเรากำลังใช้ isNew
ตัวแปรส่วนประกอบเพื่อตรวจสอบว่าการใช้งานปัจจุบันของแบบฟอร์มนี้เกี่ยวข้องกับการสร้างหรืออัปเดตของผู้ใช้หรือไม่
สุดท้ายต้องฉีด hello_angular . ทั้งหมด ลงใน Rails index.html.erb หน้าหนังสือ. ดังนั้น เปิดไฟล์นี้ภายใต้ views/users/ โฟลเดอร์และเปลี่ยนเนื้อหาดังต่อไปนี้:
<hello-angular>We're almost done...</hello-angular> <%= javascript_pack_tag 'hello_angular' %>
โมดูลเชิงมุม
ตอนนี้ เราต้องบอก Angular ว่าจะหาข้อมูลได้ที่ไหน มันเกิดขึ้นภายในการกำหนดค่าของโมดูล
เริ่มต้นด้วยการเพิ่มเนื้อหาใน app-bootstrap.module.ts :
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { ModalModule } from "ngx-bootstrap/modal";
@NgModule({
imports: [CommonModule, ModalModule.forRoot()],
exports: [ModalModule],
})
export class AppBootstrapModule {}
สิ่งนี้ถูก จำกัด ไว้ที่ส่วนประกอบ Bootstrap ที่เราได้รับมาจาก ngx-bootstrap องค์ประกอบเดียวที่เราใช้ในตอนนี้คือ Bootstrap modal
จากนั้น เปิด app-routing.module.ts ไฟล์และเปลี่ยนเนื้อหาดังต่อไปนี้:
import { RouterModule, Routes } from "@angular/router";
import { NgModule } from "@angular/core";
import { UserIndexComponent } from "./user/index/index.component";
const appRoutes: Routes = [
{ path: "users", component: UserIndexComponent },
{ path: "", redirectTo: "/users", pathMatch: "full" },
];
@NgModule({
imports: [RouterModule.forRoot(appRoutes, { scrollPositionRestoration: "enabled" })],
exports: [RouterModule],
})
export class AppRoutingModule {}
สิ่งนี้จะช่วยให้มั่นใจได้ว่า Angular ตรงกับองค์ประกอบของ User ที่ถูกต้องเมื่อ /users เส้นทางถูกเรียก
และสุดท้าย ลงทะเบียนทั้งหมดภายใน AppModule
. หลัก ระดับ. เปิด app.module.ts ไฟล์และตรวจสอบว่ามีลักษณะดังนี้:
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { HttpClientModule } from "@angular/common/http";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { AppComponent } from "./app.component";
import { AppRoutingModule } from "./app-routing.module";
import { AppBootstrapModule } from "./app-boostrap.module";
import { UserIndexComponent } from "./user/index/index.component";
@NgModule({
declarations: [AppComponent, UserIndexComponent],
imports: [HttpClientModule, AppRoutingModule, BrowserModule, FormsModule, ReactiveFormsModule, AppBootstrapModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
ที่นี่ทุกอย่างถูกแมป จากแบบฟอร์มของเรา ไคลเอนต์ HTTP และส่วนประกอบผู้ใช้ไปจนถึงการกำหนดค่าโมดูล Bootstrap และการกำหนดเส้นทาง
เสร็จสิ้นการกำหนดค่า
ก่อนที่เราจะเข้าสู่การทดสอบ เราต้องทำให้เสร็จก่อน เริ่มด้วย app.component.ts ไฟล์:
import { Component } from "@angular/core";
@Component({
selector: "hello-angular",
template: "<router-outlet></router-outlet>",
})
export class AppComponent {
name = "Angular!";
}
ส่วนประกอบหลักของแอพจำเป็นต้องรู้วิธีกำหนดเส้นทางของพาธ ดังนั้น RouterOutlet จะทำงาน
จากนั้น เราต้องตรวจสอบให้แน่ใจว่า Webpacker เข้าใจส่วนขยาย HTML ที่เรากำลังดำเนินการอยู่ สำหรับสิ่งนี้ ให้เปิด webpacker.yml ไฟล์และภายใต้ /config โฟลเดอร์ ค้นหา ส่วนขยาย และเพิ่มรายการต่อไปนี้:
- .html
Webpacker รู้จักเฉพาะตัวโหลด TypeScript ในตัวที่มาพร้อมกับ Angular โดยค่าเริ่มต้น เราจำเป็นต้องประมวลผล HTML ซึ่งเป็นสาเหตุที่เราติดตั้ง html-loader . ไว้ก่อนหน้านี้ การพึ่งพาอาศัยกัน หากต้องการตั้งค่า ให้เปิด environment.js ไฟล์ภายใต้ config/webpack โฟลเดอร์ และเพิ่มการกำหนดค่าตัวโหลดต่อไปนี้:
environment.loaders.append("html", {
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
minimize: true,
},
},
],
});
สุดท้ายนี้ เพื่อป้องกันไม่ให้บริการ Angular ของเราได้รับข้อผิดพลาดในการเรียก HTTP เราจำเป็นต้องปิดใช้งานการตรวจสอบโทเค็น CSRF ที่ดำเนินการโดย Rails สำหรับสิ่งนี้ ให้เปิด application_controller.rb ไฟล์ภายใต้ app/controllers โฟลเดอร์และเปลี่ยนเนื้อหาดังต่อไปนี้:
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
end
กำลังทดสอบ
แค่นั้นแหละ! ดูเหมือนยุ่งยากเล็กน้อยเนื่องจากการตั้งค่าต้องการการปรับแต่งจำนวนมาก แต่ผลลัพธ์ก็คุ้มค่า
ในการทดสอบ บันทึกทุกอย่าง และเริ่มเซิร์ฟเวอร์โดยออก rails s
คำสั่ง
จากนั้นไปที่เว็บเบราว์เซอร์ของคุณและพิมพ์ที่อยู่ https://localhost:3000/users ลองใช้เว็บแอปพลิเคชัน CRUD เลย
บทสรุป
เป็นถนนที่ยาวมากในการทำให้ CRUD นี้ใช้งานได้ หลังจากการทดลองใช้ครั้งแรก คุณจะเห็นว่าสิ่งต่างๆ ง่ายขึ้นสำหรับโครงการในอนาคตของคุณ ฉันหวังว่าโครงการนี้จะช่วยในการสร้างจุดเริ่มต้นสำหรับผู้ที่ต้องการเริ่มโครงการอย่างรวดเร็วโดยเข้าร่วมเทคโนโลยีทั้งสอง
แม้ว่าเราจะไม่มีโครงการนั่งร้านแบบโอเพ่นซอร์สเพื่อช่วยในเรื่องนี้ แต่เราก็อาศัยความพยายามของกันและกันในการมีเนื้อหาแบบนั้น ตอนนี้ถึงตาคุณแล้ว แยกโปรเจ็กต์ (หรือสร้างตั้งแต่เริ่มต้น) และเริ่มปรับแต่งของคุณ
ที่เก็บ GitHub สำหรับตัวอย่างนี้สามารถพบได้ที่นี่ ขอให้สนุก!