functionBaseUser1(constructor:Function) {console.log(`Đây là hàm BaseUser 1`);}functionBaseUser2(constructor:Function) {console.log(`Đây là hàm BaseUser 2`);}@BaseUser1 @BaseUser2classUser {constructor(public name:string) { }}let u1 =newUser('Lượm');console.log(u1);
E 5 Decorator factory
Đây là một hàm trả về chính hàm decorator. Cụ thể, decorator factory bao bọc quanh 1 hàm decorator để truyền tham số cho nó. Nhờ đó mà việc sử dụng các decorator sẽ uyển chuyển hơn.
Đây là 1 hàm gắn với 1 property trong class nên gọi là property decorator. Hàm decorator này hoạt động với hai tham số là constructor của class và tên thuộc tính. Để sử dụng property decorator, mở file tsconfig.json và khai báo thêm lệnh: “useDefineForClassFields”: false
Decorator – trang trí, đính kèm, bổ sung – là cách thức gắn kèm 1 hàm với class, method, property, accessor. Mục đích của việc gắn vào là để chạy hàm trong runtime mỗi khi các khai báo được sử dụng. Hàm decorator có nhiệm vụ thay đổi, bổ sung cho đối tượng được decorate.
Cài đặt decorator
Để dùng được các decorator, bạn khai báo giá trị true cho thuộc tính experimentalDecorators trong file tsconfig.json
Có nhiều loại decorator như class, factory, property, accessor, parameter…
Class decorator
Loại decorator được chỉ định ngay trước khai báo class. Class decorator gắn vào constructor của class và sử dụng để can thệp vào class như sửa đổi, bổ sung , thay thế một số định nghĩa trong class. Hàm class decorator được gọi lúc class được khai báo chứ không phải lúc 1 instance của class được tạo.
Ví dụ dùng class decorator
function ThuCungEx(constructor: Function) {
console.log("Đây là hàm ThuCung Ex");
}
@ThuCungEx
class ThuCung {
constructor(private ten:string, private tuoi:number){}
}
Một ví dụ khác dùng class decorator
Dùng class decorator để thêm thuộc tính
function BaseHV(constructor: Function) {
constructor.prototype.phai = true;
constructor.prototype.ngaytao = new Date().toLocaleString('vi');
}
@BaseHV
class HocVien { constructor(public ht: string) {} }
let hv1= new HocVien('Tèo'); console.log(hv1);
Thêm thuộc tính dùng class decorator
function themTT<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
mauxe:string = 'Xanh';
};
}
@themTT
class XeMay {
constructor( private tx:string, private gia:number){ }
}
const x1 = new XeMay('Vision 125', 39.5);
console.log(x1);
Ví dụ dùng class decorator đổi nội dung class
function ChangeHS(constructor: Function):any {
return class {
private hoten:string; phai:boolean;
constructor(h:string){
this.hoten = h;
this.phai = true;
}
}
}
@ChangeHS
class HocSinh {
public name: string;
constructor( h:string ) { this.name=h;}
}
let u1= new HocSinh("Tèo"); console.log(u1);
Sử dụng kết hợp nhiều class decorator
function BaseUser1(constructor: Function) {
console.log(`Đây là hàm BaseUser 1`);
}
function BaseUser2(constructor: Function) {
console.log(`Đây là hàm BaseUser 2`);
}
@BaseUser1 @BaseUser2
class User { constructor(public name: string) {} }
let u1 = new User('Lượm');
console.log(u1);
Decorator factory
Đây là một hàm trả về chính hàm decorator. Cụ thể, decorator factory bao bọc quanh 1 hàm decorator để truyền tham số cho nó. Nhờ đó mà việc sử dụng các decorator sẽ uyển chuyển hơn.
Ví dụ 1 dùng decorator factory
function addUserStatus( st:number){
return function(constructor:Function){
constructor.prototype.status= st;
}
}
@addUserStatus(4)
class User { constructor(public name: string) {} }
let u1 = new User('Lượm'); console.log(u1);
Ví dụ 2 dùng decorator factory
function ChangeHS( st:number){
return function (constructor: Function):any {
return class {
private hoten:string; tuoi:number;
constructor(h:string){
this.hoten = h;
this.tuoi = st;
}
}
}
}
@ChangeHS(20)
class HocSinh {
public name: string;
constructor( h:string ) { this.name=h;}
}
let u1= new HocSinh("Tèo"); console.log(u1);
Property decorator
Đây là 1 hàm gắn với 1 property trong class nên gọi là property decorator. Hàm decorator này hoạt động với hai tham số là constructor của class và tên thuộc tính. Để sử dụng property decorator, mở file tsconfig.json và khai báo thêm lệnh: “useDefineForClassFields”: false
Property decorator giúp thay đổi, thêm property mới, gán lại data, theo dõi sử dụng properry…
Quảng cáo
Ôn tập : dùng set get để theo dõi truy cập thuộc tính của class
Gán cấp độ truy xuất cho thuộc tính là private
Định nghĩa hàm set có 1 tham số để nhận giá trị mới khi gán và theo dõi giá trị mới có hợp lệ hay không.
Định nghĩa hàm get (nên cùng tên với hàm set) để trả về giá trị và theo dõi truy cập thuộc tính.
class User {
private username:string;
private password: string;
constructor(u:string, p:string){
this.username = u;
this.password = p;
}
get pass() {
console.log(`Lấy pass lúc ${new Date().toLocaleString('vi')} `);
return this.password;
}
set pass(p: string) {
console.log(`Gán pass ${p} lúc ${new Date().toLocaleString('vi')}`)
this.password =p;
}
}
let u1= new User('teo','huadianh');
console.log(u1);
u1.pass='anhsehua';
let p = u1.pass;
Ôn tập : Cách kiểm soát giá trị gán vào cho thuộc tính , báo lỗi nếu giá trị không hợp lệ
class User {
private username:string; private password: string;
constructor(u:string, p:string){
this.username = u; this.password = p;
}
get pass() { return this.password; }
set pass(p: string) {
this.password = p;
if (p.length<8){
alert(`Pass ${p} quá ngắn. Không chịu nhoa`);
this.password=undefined;
}
}
}
let u1= new User('teo','huadianh');
u1.pass='okhua'; //gán pass quá ngắn, báo lỗi như bên dưới
Kiểm soát truy cập thuộc tính bằng cách dùng property decorator
Chỉ định tên hàm decorator ngay trước thuộc tính cần kiểm soát. Viết theo cú pháp @tênhàm Ví dụ: @TheoDoiMin(7) Truyền các tham số cần thiết cho hàm, nếu bạn cần.
Định nghĩa hàm decorator. Hàm này được viết như decoration factory.
Thực hiện:
– Tạo class và chỉ định hàm decorator trước thuộc tính password
class User {
public username:string;
@TheoDoiMin(7)
public password: string;
constructor(u:string, p:string){
this.username = u;
this.password = p;
}
}
let u1 = new User('teo','huadi');
u1.password = 'anhhua'; //Báo lỗi vì quá ngắn <=7 ký tự
u1.password = 'anhxinhua'; //Không báo lỗi vì gán hợp lệ
let un = u1.username;
let pw = u1.password; //Thông báo lấy pass
– Định nghĩa hàm property decorator.
function TheoDoiMin(sokytu: number) {
return function( constructor: Object, tenthuoctinh: string) {
let value : string;
const laygiatri = function() {
let now = new Date().toLocaleString('vi');
console.log(`Lấy ${tenthuoctinh} lúc ${now}`);
return value;
};
const gangiatri = function( newVal: string) {
value = newVal;
if(newVal.length <= sokytu)
console.log(`${tenthuoctinh} ${newVal} ngắn quá,>${sokytu} ký tự`);
};
// Khai báo 2 method setter getter để kiểm soát
Object.defineProperty(constructor, tenthuoctinh, {
get: laygiatri,
set: gangiatri
});
}
}
Ưu điểm của property decorator
Nhờ decorator mà các thuộc tính của class được kiểm soát chặt chẽ. Trọng tâm là khai báo hàm decorator với lệnh Object.defineProperty để khai báo lại 2 hàm set get nhằm theo dõi các giá trị
Một hàm property decorator có thể gắn cho nhiều thuộc tính, ví dụ có thể gắn cho username và pass
class User {
@TheoDoiMin(10) public username:string;
@TheoDoiMin(7) public password: string;
constructor(u:string, p:string){
this.username = u;
this.password = p;
}
}
Method decorator
Loại decorator này dùng để gắn với method trong class. Gắn hàm decorator ngay trước tên method. Mục đích gắn hàm này vào method là để can thiệp sửa đổi, thay thế method cần theo dõi. Method decorator sẽ được gọi chạy lúc runtime với 3 tham số:
Target: là hàm constructor của class
propertyKey: là tên của method
PropertyDescriptor các thông tin của method, cấu túc như sau:
{ writable: true, enumerable: true, configurable: true, value: ƒ } Trong đó:
valuelà giá trị của method (bản thân hàm)
writablenếu true thì value có thể thay đổi còn false thì value chỉ đọc read-only.
enumerablelà true thì method được liệt kê khi lặp còn false thì method không được liệt kê.
configurablelà true tức là có thể bị xóa, writable và enumerable có thể thay đổi còn false là không thể
function methodDecorator( chuc: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
//viết code ở đây
};
}
Ví dụ sử dụng method decorator
class LoiChao {
chao: string;
constructor( str: string) { this.chao = str; }
@đổichào("Chúc an lành")
hienloichao() { return "Xin chào! " + this.chao; }
}
function đổichào( chuc: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.value = function(){ return chuc.toUpperCase() };
};
}
let a = new LoiChao("Khỏe không");
console.log(a);
console.log(a.hienloichao());
Accessor decorator
Loại accessor decorator thì giống method decorator nhưng chỉ dùng đế áp dụng cho setter , getter. Typescript không cho dùng 1 decorator trên 1 member (ví dụ pass) cho 2 hành động getter setter. Hàm access decorator sẽ được tự động gọi chạy lúc runtime. Hàm dùng 3 tham số sau
Hàm constructor của class.
Tên của member.
Property Descriptor – thông tin mô tả của member.
let role:number = 0;
class User {
private _un: string;
private _pass: string;
constructor(u:string,p:string) { this._un = u; this._pass = p;}
@layPass(role) get pass() { return this._pass; }
}
function layPass( role: number) {
return function (target:Object, propertyKey:string, descriptor:PropertyDescriptor) {
if (role==0) descriptor.get = () => 'Không đưa nha';
};
}
let kh1 = new User("Tèo",'123');
console.log(" Pass =", kh1.pass);
Parameter decorator
Loại parameter decorator dùng cho khai báo cho tham số của method, constructor. Hàm parameter decorator cũng dùng 3 tham số
target: là hàm constructor của class
Tên của parameter được decorator
Index của param trong list các tham số của function
Parameter decorator chỉ được sử dụng để kiểm tra sự tồn tại của params trong function , và thường được dùng kết hợp với method decorator hoặc accessor decorator.
Một parameter decorator chỉ có thể dùng để theo dõi tham số xem nó đã được khai báo trong method hay chưa. Giá trị trả về của parameter decorator sẽ bị bỏ qua
class SanPham {
private tensp:string;
private gia: number;//usd
constructor( t:string, g:number ){ this.tensp=t; this.gia=g; }
tienVND(soluong: number, @logTygia tygia:number) {
return this.gia*soluong*tygia
}
}
function logTygia(target: Object, methodKey: string, parameterIndex: number) {
console.log(target, methodKey, parameterIndex);
}
var c = new SanPham("Gạo", 5);
console.log(c.tienVND(2,20000)); // 200000
Khai báo class User gồm 2 thuộc tính username, password
Định nghĩa hàm property decorator có tên TheoDoiPass với 2 tham sớ là sokytumin và sokytumax. Hàm báo lỗi nếu độ dài pass < sokytumin hoặc >sokytumax.
Gắn hàm vừa định nghĩa vào thuộc tính password với tham số TheoDoiPass(7,20)
Tạo đối tượng và gán thuộc tính, hiện kết quả
Mời bạn làm thêm: hiện ra trong trang web cho đẹp
Bài 3: Dùng method decorator để kiểm soát method trong class
Tham khảo ví dụ này để thực hiện yêu cầu sau
a. Tạo class SinhVienThi có thuộc tính diem (number) và hàm hienketqua (nội dung tùy ý) có dùng method decorator cho hàm này với 2 tham số 0, 10
b. Định nghĩa hàm kiemtradiem với 2 tham số min,max
Kiểm tra điểm >=max thi gán là max, nếu diem<min thì gán làm min
function kiemtradiem( min: number, max:number) {
…
}
c. Cải thiện thêm: Bổ sung method khác và decorator cho method