[ng2 - practice] - Github search profile (P1)
Bài đăng này đã không được cập nhật trong 7 năm
Mình cũng đã giới thiệu về ng2 qua 2 bài ng-form và ng-cli. Nhưng học phải đi đôi với hành phải không nào? Hôm nay mình sẽ cùng các bạn build 1 app nhỏ để áp dụng những cái mình học được nhé.
Mình chỉ tự tìm tòi & đọc doc trên trang chủ, chưa có nhiều kinh nghiệm thực tế. Nên chắc chắn sẽ có những thiếu sót, mình rất hoan nghênh/mong muốn nhận được những góp ý của các bạn
1. Init project
Tất nhiên rồi, đầu tiên phải tạo project chứ nhỉ. Chạy ng new github-search
nhé
CSS Preprocessor mặc định sẽ là css. Nếu bạn thích xài scss/sass thì có thể set lại bằng command :
ng set defaults.styleExt scss
Ng2 được build dựa trên các component nên ta cũng phải tạo ra các component trong pj của mình thôi
App của mình là search profile trên github nên ta sẽ phải tạo ra 1 form để search
ng g component form-seach
sẽ giúp mình tạo ra 1 component thật đơn giản
Lưu ý, phần html/css mình sẽ chỉ post những đoạn code chính, bạn có thể vào link github của mình ở dưới bài viết để xem bản full không che nhé
Tạo giao diện cái nhỉ, vào file app/form-search/form-search.component.html
vừa được tạo nhé
<input class="form-control" name="username" placeholder="Enter A Github Username..." autocomplete="off" type="text">
Tạo model
để chứa thông tin user chứ nhỉ
// file app/mode/github-user.ts
export class gitHubUser {
constructor (
public user: any, public repos: any,
public username: any, public items: any,
) { }
}
Tạo service
để search chứ nhỉ. Mình chỉ post code để đó & không nói gì nhé () lát sẽ xài)
// file app/service/user-search.service.ts
@Injectable()
export class UserSearchService {}
Các bạn nhớ import service này vào
app.module.ts
nhé
2. Search user
1. Xây dựng service
Bắt tay vào công việc chính nào
// file app/form-search/form-search.component.html
<input [(ngModel)]="keySearch" id="search-box" (keyup)="search(keySearch)"
class="form-control" name="username" placeholder="Enter A Github Username..."
autocomplete="off" type="text">
Ta add thêm sự kiện (keyup)
cho input
// file app/form-search/form-search.component.ts
export class FormSearchComponent implements OnInit {
protected txtSearch = new Subject<string>();
listUser$: Observable<gitHubUser[]>;
keySearch = '';
search(keySearch: string): void {
this.txtSearch.next(keySearch);
}
ngOnInit() {
this.listUser$ = this.txtSearch
.debounceTime(300) // wait for 300ms pause in events
.distinctUntilChanged() // ignore if next search term is same as previous
.switchMap(term => term // switch to new observable each time
// return the http search observable
? this.userSearchService.searchUser(term)
// or the observable of empty heroes if no search term
: Observable.of<gitHubUser[]>([])
)
.catch(error => {
// TODO: real error handling
console.log(error);
return Observable.of<gitHubUser[]>([]);
});
}
Ta khai báo 1 Subject
với kiểu string
, cái này sẽ sinh ra một observable event stream
và gán cho biến txtSearch
, biên này sẽ sinh ra một Observable of strings
Mỗi lần gọi hàm search
sẽ đẩy một string mới vào stream này qua next()
listUser$
sẽ chứa những user từ api trả về
debounceTime(300)
sẽ delay khoảng 300ms để tránh gọi api quá nhiều lần sẽ bị tốn tài nguyên
distinctUntilChanged()
sẽ chỉ gọi api nếu giá trị được thay đổi
switchMap
chuyển đổi từ sang kiểu observable
trước khi gọi đến api
Giờ ta sẽ đụng đến service nhé
private urlSearch = 'https://api.github.com/search/users?q=';
constructor(private http: Http) { }
searchUser(keySearch: string): Promise<gitHubUser[]> {
return this.http
.get(this.urlSearch + keySearch)
.toPromise()
.then(response => response.json().items)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.log('An error occurred', error);
return Promise.reject(error.message || error);
}
http.get
sẽ trả về một RxJS Observable
, nên ta phải dùng toPromise()
để chuyển từ Observable
về Promise
2. Hiển thị user
Sau 1 hồi quần quật với cái service thì đã đến lúc ta phải show kết quả mà api trả về chứ nhỉ
// file app/form-search/form-search.component.html
<div class="suggestion-container">
<div *ngFor="let user of listUser$ | async" class="suggestion" (click)="findUser(user.login)">
<span>{{user.login}}</span>
</div>
</div>
Các bạn lưu ý là phải chạy qua
async
nhé.PipeAsync
sẽ đăng kí vàoObservable
và chuyển đổi sang array để chongFor
có thể dùng. Mình bị lack chỗ này nên mất cả buổi sáng mới fix được (
Các bạn để í là minh có bind event click vào mỗi div của kết quả trả về nhé. Nên là trong file component mình cũng phải khai báo hàm đó thôi
// file app/form-search/form-search.component.ts
protected userGit: gitHubUser;
findUser(userName) {
this.keySearch = userName;
this.userGit = new gitHubUser('', '', userName, '');
}
Ở trên là mình search LIKE
nên sẽ trả về nhiều user. Mục đích của mình là search cụ thể 1 user mà, nên mình sẽ có 1 biến để lưu user mà client đã chọn
3. Hide result search
Bạn để í là khi mình click chọn 1 user rồi, nhưng list user từ api trả về không bị ẩn (trông xấu mù). Xử nó thôi Mình sẽ có 1 cờ để show/hide bọn đấy
// file app/form-search/form-search.component.ts
hideResultSearch = true;
constructor(private userSearchService: UserSearchService) {
document.addEventListener('click', this.offClickHandler.bind(this));
}
offClickHandler() {
this.hideResultSearch = true;
}
search(keySearch: string): void {
this.hideResultSearch = false;
this.txtSearch.next(keySearch);
}
// file app/form-search/form-search.component.html
<div class="suggestion-container" [hidden]="(listUser$ | async)?.length <= 0 || hideResultSearch">
Khi bạn click bất kì chỗ nào trên màn hình thì biến hideResultSearch
được gán = true => sẽ ẩn list user kia đi
Hết phần 1. Phần sau mình sẽ hướng dẫn tìm/hiên thị thông tin của 1 user mà bạn click link full không che - github
All rights reserved