AngularJS 2 Nedir? Bölüm 4
Selamlar,
Bu bölümde Angular2 konusuna kaldığımız yerden validation ve hata mesajları ile devam edeceğiz. Önemli olduğı için Angular 2’de validation ile alkalı her ne var ise bu makalede tartışacağız. Öncelikle Visual Studio Code’da Angular2 için yandaki extension’ı yüklenir. https://github.com/johnpapa/vscode-angular2-snippets . “Angular2 Typescript Snippets” by John Papa. Burada amaç daha hızlı kod yazmak ve her Compenent’i oluştururken ne yazıyoduk diye düşünmemek:) Ben ençok aşağıdaki snippetleri kullanıyorum. İnanın çok zaman kazandırıyor. Teşekkürler John Papa.
- ng2-component // Angular 2 component
- ng2-pipe // Angular 2 pipe
- ng2-service
contact-form.component.ts: Başvuru formu aşağıdaki gibi oluşturulur. “templateUrl” olarak yeni oluşturulacak olan “contact-form.component.html” tanımlanır.
1 2 3 4 5 6 7 8 9 10 11 12 |
import { Component, OnInit } from 'angular2/core'; @Component({ selector: 'contact-form', templateUrl: 'contact-form.component.html' }) export class ComponentNameComponent implements OnInit { constructor() { } ngOnInit() { } } |
Ben html için Zen Coding kullandım. Ne olduğu ile ilgili olarak önceki makalem‘den faydalana bilirsiniz. Html’in regular expression’ı diyebiliriz:) Visual Studio Code’da Zen Coding kullanmak için aşağıda görülen extension’ın yüklenmesi gerekmektedir.
Aşağıdaki kod yazılıp tab tuşuna basıldığında yine açılımı aşağıdaki gibi gözükmektedir.
form>div.form-group>label+input.form-control[type=’text’]
1 2 3 |
<form action=""> <div class="form-group"><label for=""></label><input type="text" class="form-control"></div> </form> |
Yukarıdaki açılımın formatlı görülebilmesi için, platforma göre ilgili tuş kombinasyonuna “extension” bölümünden Windows’da [shift+control+p] yazılarak erişilir. İlgili alanda “Format Code” aranarak, karşımıza çıkan “Shift+Alt+F” tuşlarına basılarak istenen formatlama işlemi yapılabilir.
Formatlı hali:
1 2 3 4 5 6 |
<form action=""> <div class="form-group"> <label for=""></label> <input type="text" class="form-control"> </div> </form> |
Şimdi sıra geldi yorum kısmının yazılmasına. 2. bir zen code örnek olarak aşağıdaki kod satırı oluşturulmuştur.
div.form-group>label[for=’comment’]+textarea.form-control[id=’comment’]
button.btn.btn-primary[type=’submit’]
Tab tuşuna basıldığında oluşan kodun formatlı hali, aşağıdaki gibidir:
contact-form.component.html: Aşağıda görüldüğü gibi ilgili yorum formu oluşturulmuştur.
1 2 3 4 5 6 7 8 9 10 11 |
<form action=""> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" type="text" class="form-control"> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" id="comment" cols="30" rows="10" class="form-control"></textarea> </div> <button class="btn btn-primary" type="submit">Submit</button> </form> |
Validation: Gelin girilen alanlar için ilk validation’ı yazalım: ngControl ile validate yapılacak nesne bir Control’a atanır. ngForm ile de ilgili control nesnesine erişilir. Aslında ngForm bir Control kümesine karşlık gelmektedir. Burada sadece bir Control vardır. O da firstName‘dir. Aşağıdaki örnekde “firstName” bir Control’a atanmış ve daha sonra ilgili control’a ngForm ile erişilip “#firstName” değişkenine atanmıştır. Aynı işlemler “textarea” için de yapılmıştır.
contact-form.component.html:
1 2 3 4 5 6 7 8 9 10 11 |
<form action=""> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" ngControl="firstName" #firstName="ngForm" (change)="log(firstName)" type="text" class="form-control"> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" ngControl="comment" #comment="ngForm" id="comment" cols="30" rows="10" class="form-control"></textarea> </div> <button class="btn btn-primary" type="submit">Submit</button> </form> |
Zorunlu tutulacak “firstName” required olarak atanır ve ilgili event kaldırılır. Ayrıca hatanın olması durumunda içine yazılacağı div aşağıdaki gibi zen coding ile yazılmıştır. Altında da açılmış hali gözükmektedir. Hata mesajının”*ngIf” ile ilgili “firstName Controller‘”ın “valid” olmaması durumunda gözükmesi sağlanmıştır. Sayfa ilk yüklendiğinde ilgili error mesajların hemen gelmemesi için “firstName.touched” property’sine bakılıp ilgili alana girilip girilmediği koşulu tanımlanmıştır. Tüm işlemler comment için de yapılmıştır.
div.alert.alert-danger[*ngIf=”firstName.touched && !firstName.valid”]>
contact-form.component.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<form action=""> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" ngControl="firstName" #firstName="ngForm" type="text" class="form-control" required> <div class="alert alert-danger" *ngIf="firstName.touched && !firstName.valid">İsim Alanı Zorunludura</div> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" ngControl="comment" id="comment" #comment="ngForm" cols="30" rows="10" class="form-control" required></textarea> <div class="alert alert-danger" *ngIf="comment.touched && !comment.valid">Yorum Alanı Zorunludur</div> </div> <button class="btn btn-primary" type="submit">Submit</button> </form> |
app.component.ts: Aşşağıdaki değişiklik yapılarak “template” yapıda “<contact-form></contact-form>” kullanılarak ilgili html form “index.html“‘e basılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { Component, OnInit } from 'angular2/core'; import { ContactFormComponent } from './contact-form.component'; @Component({ selector: 'my-app', directives:[ContactFormComponent], template: '<contact-form></contact-form>' }) export class AppComponent implements OnInit { constructor() { } ngOnInit() { } } |
Örnek Ekran Çıktısı:
style.css:
1 2 3 |
.ng-touched.ng-invalid { border: 1px solid red; } |
index.html‘e de alttaki css dosyası eklenir. Böylece hatalı alanların kırmızı bir çerçeve ile de çevrilmesi sağlanır.
1 |
<link rel="stylesheet" href="app/style.css"> |
Multi Validation ve Validation Group Mantığı:
Eğer İsim alanın zorunlu olması haricinde en az 3 karakter olması da istenir ise: Öncelik ile ilgili element’e minlength=”3″ ifadesinin eklenmesi gerekmektedir.
- “*ngIf=”comment.touched && comment.errors” ifadesi ile container bir div yaratılarak diğer hata mesajlarının yazılacağı divler bunun içine konur.
- “*ngIf=”comment.errors.requried && !comment.valid” ile zorunlu alan hata mesajı yazılır.
- “*ngIf=”comment.errors.minlength” ile minimum uzunluk hata mesajı yazılır.
- Control’a ait tüm hata mesajları “{{firstName.errors | json}}” && “{{comment.errors | json}}” ile ekrana basılabilir.
- Hata mesajındaki minimum karakter sayısı dinamik olarak yandaki gibi yazılabilir. “İsim Alanı En az {{firstName.errors.minlength.requiredLength}} karakter olmalıdır!”
contact-form.component.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<form action=""> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" ngControl="firstName" #firstName="ngForm" type="text" class="form-control" required minlength="3"> <div *ngIf="firstName.touched && firstName.errors"> {{firstName.errors | json}} <div class="alert alert-danger" *ngIf="firstName.errors.requried">İsim Alanı Zorunludur!</div> <div class="alert alert-danger" *ngIf="firstName.errors.minlength">İsim Alanı En az {{firstName.errors.minlength.requiredLength}} karakter olmalıdır!</div> </div> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" ngControl="comment" id="comment" #comment="ngForm" cols="30" rows="10" class="form-control" required minlength="10"></textarea> <div *ngIf="comment.touched && comment.errors"> {{comment.errors | json}} <div class="alert alert-danger" *ngIf="comment.errors.requried && !comment.valid">Yorum Alanı Zorunludur</div> <div class="alert alert-danger" *ngIf="comment.errors.minlength ">Yorum Alanı en az {{comment.errors.minlength.requiredLength}} karakterdir.</div> </div> </div> <button class="btn btn-primary" type="submit">Submit</button> </form> |
Artık gelin isterseniz bu formu nasıl kaydedilir ona bakalım.
Form Elemanlarını “Kaydetme Methoduna” Gönderme:
Öncelikle şuna açıklık getirelim.
- ngControl : Tek bir Control’a denk gelir. Örnek ‘ngControl=”firstName”‘
- ngForm : Control kümesine karşlık gelir. Yani tüm kontrolleri barındırır.
- <form #f=”ngForm” (ngSubmit)=”onSubmit(f.form)”> : Tüm Controller yani ngForm ControlGroup ‘#f‘ değişkenine aktarılır.
- formun “(ngSubmit)” eventinde “onSubmit(f.form)” methodu ilgili ControlGroup’a ait form parametresi ile beraber çalıştırılır.
- <button class=”btn btn-primary” type=”submit” [disabled]=”!f.valid”>Submit</button> : Gönder buttonun aktif olması için “[disabled]” parametresine “!f.valid” atanarak form içindeki tüm controllerin belirlenen validation’lara uyması durumunda tıklanabilir olması sağlanmıştır.
contact-form.component.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<form #f="ngForm" (ngSubmit)="onSubmit(f.form)"> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" ngControl="firstName" #firstName="ngForm" type="text" class="form-control" required minlength="3"> <div *ngIf="firstName.touched && firstName.errors"> {{firstName.errors | json}} <div class="alert alert-danger" *ngIf="firstName.errors.requried">İsim Alanı Zorunludur!</div> <div class="alert alert-danger" *ngIf="firstName.errors.minlength">İsim Alanı En az {{firstName.errors.minlength.requiredLength}} karakter olmalıdır!</div> </div> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" ngControl="comment" id="comment" #comment="ngForm" cols="30" rows="10" class="form-control" required minlength="10"></textarea> <div *ngIf="comment.touched && comment.errors"> {{comment.errors | json}} <div class="alert alert-danger" *ngIf="comment.errors.requried && !comment.valid">Yorum Alanı Zorunludur</div> <div class="alert alert-danger" *ngIf="comment.errors.minlength ">Yorum Alanı en az {{comment.errors.minlength.requiredLength}} karakterdir.</div> </div> </div> <button class="btn btn-primary" type="submit" [disabled]="!f.valid">Submit</button> </form> |
contact-form.component.ts: “onSubmit(form)” methodunda gelen formdan firstName değeri console’a yazdırılmıştır. Böylece “onSubmit()” methoduna gelen formu’un Html Controllerlarına erişilmiş olunur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import { Component, OnInit } from 'angular2/core'; @Component({ selector: 'contact-form', templateUrl: 'app/contact-form.component.html' }) export class ContactFormComponent { log(value) { console.log(value); } onSubmit(form){ console.log(form.value.firstName) } } |
Custom Validation : Gelin bir de yaptığımız bu validationları model bazlı yapalım.
contact-form.component.ts:
- form adında yeni bir “ControlGroup” yaratılır.
- “name” ve “comment” 2 Control elemanı ve constructer’ında ilgili Validators tipi aşağıda görüldüğü gibi verilmektedir.
- Yukarıda görülen “Control”, “ControlGroup” ve “Validators” gibi apilerin kullanılması için ilgili modullerin import edilmesi gerekir. “import { Control, ControlGroup,Validators } from ‘angular2/common‘;”
- Her module’ün nerede olduğunu bilmke zordur. Nasıl bulabileceğiniz aşağıda gösterilmiştir.
- Not: Değişkenlere atanan isimlendirmeler çok önemlidir “form”,”name” ve “comment” html sayfadaki ile aynı olmak zorundadır.
- Form’un onSubmit() methodunda “this.form.value.firstName” firstname değeri console’a basılır.
contact-form.component.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import { Component, OnInit } from 'angular2/core'; import { Control, ControlGroup,Validators } from 'angular2/common'; @Component({ selector: 'contact-form', templateUrl: 'app/contact-form.component.html' }) export class ContactFormComponent { form =new ControlGroup({ firstName : new Control('',Validators.required), comment : new Control('',Validators.required) }); onSubmit(){ console.log(this.form.value.firstName) } } |
contact-form.component.html(Custom Validation):
- “<form>”‘a ==> “[ngFormModel]” property’sine “form” atanmıştır. Bu yukarıda tanımlanan form “ControlGroup“‘a karşılık gelmektedir.
- form (ngSubmit) eventinde “onSubmit()” methodu parametreye ihtiyaç duyulmadan çağrılmıştır.
- Zor yol: “firstName”‘in hata mesajı ‘*ngIf=”!form.controls[‘firstName’].valid“‘ ile valid olup olmadığına bakılabilir. Ama ilgili “form ControlGroup” içindeki “firstName“‘in bulunması uzun yoldur.
- Kısa yol: “comment” ilgili comment control’u ‘#comment=”ngForm”‘ ile ilgili değişkene atanır. “*ngIf=”!comment.valid” ile belirtilen custom validationlara uygun olup olmadığna bakılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<form [ngFormModel]="form" (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" ngControl="firstName" type="text" class="form-control"> <div class="alert alert-danger" *ngIf="!form.controls['firstName'].valid">İsim Alanı Zorunludur!</div> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" ngControl="comment" id="comment" #comment="ngForm" cols="30" rows="10" class="form-control"></textarea> <div class="alert alert-danger" *ngIf="!comment.valid">Yorum Alanı Zorunludur</div> </div> <button class="btn btn-primary" type="submit">Submit</button> </form> |
FromBuilder ile CustomValidation: Ayrıca istenir ise “FormBuilder” kullanılarak aşağıda görüldüğü gibi çok daha kolay custom validationlar yazılabilir.
contact-form.component.ts(Custom Validation 2):
- “ControlGroup” tipinde bir form tanımlanmıştır.
- Constructor’da parametre olarak “FormBuilder” tipinde fb nesnesi alınmıştır. Ayrıca FormBuilder module’ü de import olarak sayfanın başında eklenmiştir.
- form değişkenine “fb.group({})” ile “firstName” ve “comment” controlleri default değerleri boş ve validator’ı required (”,Validators.required) olarak oluşturulmuştur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import { Component, OnInit } from 'angular2/core'; import { Control, ControlGroup,Validators,FormBuilder } from 'angular2/common'; @Component({ selector: 'contact-form', templateUrl: 'app/contact-form.component.html' }) export class ContactFormComponent { /*form =new ControlGroup({ firstName : new Control('',Validators.required), comment : new Control('',Validators.required) });*/ form:ControlGroup; constructor(fb:FormBuilder){ this.form=fb.group({ firstName:['',Validators.required], comment:['',Validators.required] }); } onSubmit(){ console.log(this.form.value.firstName) } } |
Specific Custom Validators: Sıra geldi olmayan bir validator yazmaya. Diyelimki isim alanında boşluk olmasını istemiyoruz. Öncelikle aşağıdaki gibi CommentValidators adında bir sınıf yaratılır.
commentValidators.ts: Aşağıdaki örnekde görüldüğü gibi “noSpace()” adında “Control” tipinde parametre alan, static bir method yaratılmıştır. Gelen control’ün value değerinde ‘ ‘ boşluk olup olmamasına göre boolen değer dönülmektedir.
1 2 3 4 5 6 7 8 9 10 11 |
import { Control } from 'angular2/common'; export class CommentValidators { static noSpace(control : Control) { if(control.value.indexOf(' ')>=0){ return{noSpace: true}; } return null; } } |
Şimdi sıra geldi bu validator’ı da firstName Control’üne eklemeye.
contact-form.component.ts:
- Yaratılan “CommentValidators” sayfanın başında import ile eklenir.
- firstName control’una “Validators.compose([])” methodu ile ilgili validatorlar dizi şeklinde eklenir.
- firstName control’una “required” validator’unun yanına yazdığımız custom “CommentValidators.noSpace” validator’ı da eklenir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import { Component, OnInit } from 'angular2/core'; import { Control, ControlGroup,Validators,FormBuilder } from 'angular2/common'; import { CommentValidators} from './commentValidators' @Component({ selector: 'contact-form', templateUrl: 'app/contact-form.component.html' }) export class ContactFormComponent { form:ControlGroup; constructor(fb:FormBuilder){ this.form=fb.group({ firstName:['', Validators.compose([ Validators.required, CommentValidators.noSpace ])], comment:['',Validators.required] }); } onSubmit(){ console.log(this.form.value.firstName) } } |
Şimdi sıra geldi ilgili html’i düzenlemeye.
contact-form.component.html: Yazılmış olan “noSpace()” custom validation’ına ait hata mesajının ilgili duruma göre kontrol edilip, ekrana basılması bu sayfa üzerinde yapılmaktadır.
- Öncelikle hata divleri her 2 control için genel bir <div> gurubu altına alınır. Amaç control’a girilmiş ise [“firstName.touched“] ve control’de hata var ise [“firstName.errors“] control’a ait özel hata mesajları gösterilsin.
- firstName için özel tanımlanan “noSpace” validation’ı “*ngIf=”firstName.errors.noSpace” satırı ile div’in içine konur. Böylece ilgili isim içerisinde boşluk olur ise ilgili hata karşımıza çıkar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<form [ngFormModel]="form" (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" ngControl="firstName" #firstName="ngForm" type="text" class="form-control"> <div *ngIf="firstName.touched && firstName.errors"> <div class="alert alert-danger" *ngIf="firstName.errors.required">İsim Alanı Zorunludur!</div> <div class="alert alert-danger" *ngIf="firstName.errors.noSpace">İsimde boşluk karakteri olamaz</div> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" ngControl="comment" id="comment" #comment="ngForm" cols="30" rows="10" class="form-control"></textarea> <div class="alert alert-danger" *ngIf="comment.touched && !comment.valid">Yorum Alanı Zorunludur</div> </div> <button class="btn btn-primary" type="submit">Submit</button> </div> </form> |
Async Custom Validators: Var olan CommentValidators’a “uniqeName” adında yeni static bir method ekliyoruz. Burada amaç belli bir zaman alan kontrol işlemini arkada çalıştırmak. Bu örnek’de girilen username’in önceden kullanılıp kullanılmadığına bakılıp bir result dönülecektir. Async validate işlemler için, aşağıda görülen “Promis()=>” methodu kullanılır. Bu lamda function 2 parametre alabilmektedir. 1-) “resolve()” generic tipte zorunlu olmayan value ya da yeni bir PromiseLike alan, void dönüş tipi olan bir başka functiondır. Aynı şekilde 2-) “reject()” gene void dönen isteğe bağlı parametre alan diğer bir functiondır. Aşağıdaki örnekde servis konusuna henüz girilmediği için timer nesnesi ile 1sn bekleme zamanı yapılmıştır. İlgili isim “borsoft” ise, “uniqeName” hatası dönülmüştür.
commentValidators.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import { Control } from 'angular2/common'; export class CommentValidators { static uniqeName(control: Control) { return new Promise((resolve, reject) => { setTimeout(function () { if(control.value=="borsoft") resolve({uniqeName:true}); else{ resolve(null); } }, 1000); }); } static noSpace(control: Control) { if (control.value.indexOf(' ') >= 0) { return { noSpace: true }; } return null; } } |
contact-form.components (Async): Burada diğer validation işlemlerinin yanı sıra 3. bir paramtere olarak tekil isim kontrolü olan “CommentValidators.uniqeName” eklenmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import { Component, OnInit } from 'angular2/core'; import { Control, ControlGroup,Validators,FormBuilder } from 'angular2/common'; import { CommentValidators} from './commentValidators' @Component({ selector: 'contact-form', templateUrl: 'app/contact-form.component.html' }) export class ContactFormComponent { form:ControlGroup; constructor(fb:FormBuilder){ this.form=fb.group({ firstName:['', Validators.compose([ Validators.required, CommentValidators.noSpace ]),CommentValidators.uniqeName], comment:['',Validators.required] }); } onSubmit(){ console.log(this.form.value.firstName) } } |
contact-form.component.html: Html sayfada ilgili “uniqeName” için oluşacak hata divi de aşağıdaki gibi eklenmiştir. Ayrıca bir çeşit Loading ekranı yapılmıştır. Eğer “firstName.control.pending” durumunda ise “İsim Kontrol Ediliyor” ibaresi ekranda gösterilmektedir. İstenir ise bir Loading.gif’i de konabilirdi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<form [ngFormModel]="form" (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="firstName">İsim</label> <input id="firstName" ngControl="firstName" #firstName="ngForm" type="text" class="form-control"> <div *ngIf="firstName.control.pending">İsim Kontrol Ediliyor</div> <div *ngIf="firstName.touched && firstName.errors"> <div class="alert alert-danger" *ngIf="firstName.errors.required">İsim Alanı Zorunludur!</div> <div class="alert alert-danger" *ngIf="firstName.errors.noSpace">İsimde boşluk karakteri olamaz</div> <div class="alert alert-danger" *ngIf="firstName.errors.uniqeName">Bu isim Zaten Kullanılmıştır.</div> </div> <div class="form-group"> <label for="comment">Yorum</label> <textarea name="" ngControl="comment" id="comment" #comment="ngForm" cols="30" rows="10" class="form-control"></textarea> <div class="alert alert-danger" *ngIf="comment.touched && !comment.valid">Yorum Alanı Zorunludur</div> </div> <button class="btn btn-primary" type="submit">Submit</button> </div> </form> |
Böylece uzun da olsa Angular 2’de Validation konusuna detaylıca girmiş olduk. Böylece geldik bir makalenin daha sonuna. Bir sonraki makalede görüşmek üzere hoşçakalın.
Kaynaklar : https://angular.io/, https://egghead.io, Mosh Hamedani(Angular 2 with TypeScript for Beginners The Pragmatic Guide), ng-book2 The Complete Book on AngularJS 2 …
İyi günler Bora bey.
Öncelikle angular seriniz için ellerinize sağlık.
Ben Visual Studio da online template içerisinden angular2 templateini kullanarak proje oluşturdum.
verdiğiniz örnekleri yaptım fakat sayfa yüklenmesi ile ilgili listenin ekrana basılması arasında en az 7-8 sn geçeiyor.
Sayfa yüklenirken Network trafiğine baktığımda zone.js tarafından onlarca node modülü js doyasına istek gönderiliyor.
7-8 sn lik bu süreyi kısaltmanın yolu varmı acaba?