שיעור 23 באנגולר ( angular ) – העברת מידע לתוך קומפוננטה

במקרים בהם המשתנים נמצאים בקומפוננטה עליונה בהירככיה, ובתוכה מקוננת קומפוננטה אחרת (או יותר) , אז נרצה להעביר את המידע מאחת לשניה.

עושים זאת באמצעות הפקודה Input@

כלומר צריך להצהיר בתוך הקומפוננטה המקבלת – שמשתנה מסוים מיועד להגיע, ואז אנגולר תדע להעביר אותו מהקומפוננט מעליו.

דוגמא – יש פה 2 קבצים, בקובץ העליון, אני מעביר משתנה בשם question

ובקובץ השני, הפנימי יותר, אני מקבל אותו עם Input@.

------------- First component ----------------
<app-question *ngFor="let question of questions" [question]="question" ></app-question>


-------------- Seconed Component TS file-------------
import { Question } from './../../models/Question';
import { Component, OnInit,Input } from '@angular/core';


@Component({
 selector: 'app-question',
 templateUrl: './question.component.html',
 styleUrls: ['./question.component.css']
})
export class QuestionComponent implements OnInit {

@Input('question') question:Question;

constructor() { }

ngOnInit() {
 }

}

 

שיעור 22 באנגולר ( angular ) – שימוש ב- router

באגנולר, כמו בפרימוורקים אחרים, router הוא הרכיב שדואג לניווט .

כדי להשתמש בו נבצע את השלבים הבאים :

  • נוודא שישנה תגית base
  • נייבא את ה- router ב- app.module.ts
  • נגדיר מערך של ניתובים
  • נוסיף למערך imports, את המערך של הניתובים, דרך המתודה שנקראת forRoot()
  • בתוך app.component.html נוסיף תגית router-outlet
  • ונקשר בתוך הקומפוננוטות במקומות הנחוצים – באמצעות הוספת המאפיין routerLink לתגית ה- a
--- app.module.ts --------
...
...
import { RouterModule,Routes } from '@angular/router';
...
...
import { Home3Component } from './components/home3/home3.component';

const appRoutes: Routes = [
 { path: '', component: Home3Component},
 { path : 'about', component: AboutComponent},
 { path: 'user/:id', component: UserDetailsComponent}
];
...
...
...
imports: [
 BrowserModule,
 FormsModule,
 HttpModule,
 RouterModule.forRoot(appRoutes)
 ],
 

--- app.component.html -------
...
...

<router-outlet></router-outlet>
...
...
---- navbar.component.html ------
 <li class="nav-item active">
 <a class="nav-link" href="#" routerLink="/" >Home <span class="sr-only">(current)</span></a>
 </li>
 <li class="nav-item">
 <a class="nav-link" href="#" routerLink="/about">Link</a>
 </li>

ניתובים (כתובת URL ) עם פרמטר

  • ניתן להוסיף ניתובים עם פרמטרים ב-syntax הבא:    blabla/:myParam
  • כדי לקבל את הכתובות , נייבא אל תוך הקומפוננט את
    Router,ActivatedRoute,Params
  • ובנוסף נזריק ( DI ) בקונסטרקטור את Router,ActivatedRoute
  • כדי שנוכל לקבל את הפרמטר מה-URL, צריכים לרשום = subscribe  את הפרמטר בתוך "צופה"  = observable.

 

---- in app.module.ts routes array ----

const appRoutes: Routes = [
 { path: '', component: Home3Component},
 { path : 'about', component: AboutComponent},
 { path: 'user/:id', component: UserDetails3Component}
];

--- in the component -----
import { Component, OnInit } from '@angular/core';
import { Router,ActivatedRoute,Params } from '@angular/router';

@Component({
 selector: 'app-user-details-3',
 template: `
 <h1> This is User number {{id}}.</h1>
 `,
 
})
export class UserDetails3Component implements OnInit {

 id:number;

 constructor(
 private route:ActivatedRoute,
 private router:Router
 ) 
 {

 this.route.params.subscribe((params:Params) => {
 this.id = params.id;
 });
 }

 ngOnInit() {} 



}

שיעור 21 באנגולר (angular) – קריאות http

קריאת Get באנגולר, והבסיס לקריאות http

כדי להשתמש ברכיב http של אנגולר, צריך קודם כל לייבא אותו ולהגדיר אותו בקובץ app.module.ts

import { HttpModule } from '@angular/http';

...
...
imports: [
 BrowserModule,
 FormsModule,
 HttpModule,
 RouterModule.forRoot(appRoutes)
 ],

שלב נוסף – הוא בתוך ה- service, להזריק אותו אל הקונסטרטור (וכמובן לייבא אותו ).

(במידה וזה service חדש שעכשיו נוצר, אז כמובן לייבא אותו ב–app.module.ts, ולהכניס אותו למערך ה- providers ).

ואז ב- service, נשתמש במודול http כדי לבצע קריאה, ולמפות את התוצאות ל-json.

כדי למפות את התוצאות צריך לייבא חלק של rxjs, שנקרא map.

התוצאה הסופית נראית כך :

---- The Service file -----

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';


@Injectable()
export class Get2Service {

 apiUrl:string = 'https://jsonplaceholder.typicode.com/users';

 constructor(public http:Http) { }

 getUsers(){
 return this.http.get(this.apiUrl)
 .map(response => response.json());
 }


}


----- The component file ----------

import { Component, OnInit } from '@angular/core';
import { Get2Service } from './../../services/get2.service';

@Component({
 selector: 'app-useget2',
 template: `
 
 <h1>list of users</h1>
 <div *ngFor="let user of users">
 <ul>
 <li>{{user.name}}</li>
 <li>{{user.email}}</li>
 <li>{{user.id}}</li>
 </ul>
 <hr>
 </div>
 
 `
})
export class Useget2Component implements OnInit {

 users:any[];

 constructor( public getService:Get2Service ) {

 this.getService.getUsers().subscribe(users => {
 //console.log(users);
 this.users = users;
 })

 }



 ngOnInit() {
 }

}

קריאת POST באנגולר

בדוגמא הבאה, לקחתי את הדוגמא הקודמת והוספתי לה

  • ב- service התווספה פונקציה שנקראת addUser ומבצעת קריאת POST.
  • ב-component התווסף טופס להוספת יוזרים, שממופה עם ngModel אל רשימת היוזרים, ואל משתנה חדש בשם יוזר ( אובייקט עם 3 מאפיינים).
  • לטופס יש פונקציה שמטפלת ב-submit שלו, שמבצעת גם קריאה ל-service.
---------- The Service File ------------------

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';


@Injectable()
export class Get2Service {

 apiUrl:string = 'https://jsonplaceholder.typicode.com/users';

 constructor(public http:Http) { }

 getUsers(){
 return this.http.get(this.apiUrl)
 .map(response => response.json());
 }

 addUser(user) {
 return this.http.post(this.apiUrl,user).map(response => response.json());
 }


}
--------------- The component file -------------

import { Component, OnInit } from '@angular/core';
import { Get2Service } from './../../services/get2.service';

@Component({
 selector: 'app-useget2',
 template: `
 
 <h1>list of users</h1>
 <form (submit)="onSubmit()">
 <div class="form-group">
 <label>Name:</label>
 <input type="text" name="name" [(ngModel)]="user.name" class="form-control">
 </div>
 <div class="form-group">
 <label>Email:</label>
 <input type="text" name="email" [(ngModel)]="user.email" class="form-control">
 </div>
 <div class="form-group">
 <label>Phone:</label>
 <input type="text" name="phone" [(ngModel)]="user.phone" class="form-control">
 </div>
 <input type="submit" value="click to submit" class="btn btn-success">
 </form>
 <hr>
 <div *ngFor="let user of users">
 <ul>
 <li>{{user.name}}</li>
 <li>{{user.email}}</li>
 <li>{{user.id}}</li>
 </ul>
 <hr>
 </div>
 
 `
})
export class Useget2Component implements OnInit {

 users:any[];
 user = {name: '',
email:'',phone:''};

 constructor( public getService:Get2Service ) {

 this.getService.getUsers().subscribe(users => {
 //console.log(users);
 this.users = users;
 })

 }

 onSubmit(){
 this.getService.addUser(this.user).subscribe(user=> {
 console.log(user);
 this.users.unshift(user);
 });
 }

 ngOnInit() {
 }

}

קריאת Delete  + PUT  באנגולר

המשך הדוגמא שמובאת למעלה – הוספתי כעת גם קריאות put+delete שמוצמדות לכפתורים Edit/Delete.

(הכפתור edit , מעדכן את המודל, וכיוון שהטופס קשור ל- ngModel אז באופן אוטומטי, הטופס מכיל את תוכן המודל).

----- The Service File --------

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';


@Injectable()
export class Get2Service {

 apiUrl:string = 'https://jsonplaceholder.typicode.com/users';

 constructor(public http:Http) { }

 getUsers(){
 return this.http.get(this.apiUrl)
 .map(response => response.json());
 }

 addUser(user) {
 return this.http.post(this.apiUrl,user).map(response => response.json());
 }

 deleteUser(id) {
 return this.http.delete(this.apiUrl + "/" + id).map(response => response.json());
 }

 updateUser(user) {
 return this.http.put(this.apiUrl + '/' + user.id,user).map(response => response.json());
 }

}


---- The Component file -----------
import { Component, OnInit } from '@angular/core';
import { Get2Service } from './../../services/get2.service';

@Component({
 selector: 'app-useget2',
 template: `
 
 <h1>list of users</h1>
 <form (submit)="onSubmit(isEdit)">
 <div class="form-group">
 <label>Name:</label>
 <input type="text" name="name" [(ngModel)]="user.name" class="form-control">
 </div>
 <div class="form-group">
 <label>Email:</label>
 <input type="text" name="email" [(ngModel)]="user.email" class="form-control">
 </div>
 <div class="form-group">
 <label>Phone:</label>
 <input type="text" name="phone" [(ngModel)]="user.phone" class="form-control">
 </div>
 <input type="submit" value="click to submit" class="btn btn-success">
 </form>
 <hr>
 <div *ngFor="let user of users">
 <ul>
 <li>{{user.name}}</li>
 <li>{{user.email}}</li>
 <li>{{user.id}}</li>
 </ul>
 <br>
 <button (click)="onDeleteClick(user.id)" class="button btn-danger btn-sm">Delete</button>
 <button (click)="onEditClick(user) " class="button btn-primary btn-sm">Edit</button>
 <hr>
 </div>
 
 `
})
export class Useget2Component implements OnInit {

 users:any[];
 user = {name: '',
email:'',phone:''};
 isEdit:boolean = false;

 constructor( public getService:Get2Service ) {

 this.getService.getUsers().subscribe(users => {
 //console.log(users);
 this.users = users;
 })

 }

 onSubmit(isEdit){
 if (isEdit) {
 this.getService.updateUser(this.user).subscribe(user=> {
 console.log(user);
 for(let i = 0; i< this.users.length ; i++ ){
 if (this.users[i].id == user.id) {
 this.users.splice(i,1);
 
 }
 }
 this.users.unshift(user);
 });
 } else {

 this.getService.addUser(this.user).subscribe(user=> {
 console.log(user);
 this.users.unshift(user);
 });
 }
 }

 ngOnInit() {
 }


 onDeleteClick(id) {
 console.log(id);
 this.getService.deleteUser(id).subscribe(response=> {
 console.log(response);
 for(let i = 0;i< this.users.length; i++) {
 if(this.users[i].id == id) {
 this.users.splice(i,1);
 }
 }
 });
 }

 onEditClick(user) {
 this.isEdit = true;
 this.user = user;
 }
}

 

שיעור 20 באנגולר ( angular) – שימוש ב – obseravables

מהו Observable ?

  • במקור זהו Design Pattern מקובל ( הסבר מוויקיפדיה  )  שיש לו מימושים בהרבה מאוד שפות , כמו שאפשר לראות באתר http://reactivex.io/languages.html 
  • הרעיון של תבנית התיכנות הזו, הוא שיש לנו אובייקט מסוים, שנגדיר אותו בתור "הנושא", ובנוסף אליו אנחנו מגדירים "צופים" שכל הזמן מסתכלים\עוקבים\צופים על הנושא, ברגע שמשהו בנושא משתנה ( בעגה המקצועית – יש שינוי של State , של מצב )
    אז כל הצופים רואים את השינוי באופן אוטומטי.
  • למה זה טוב ?
    • זה לא מצריך להסתבך עם callbacks
    • זה מאפשר לפתוח "ערוץ מידע" קבוע לכל מיני חיבורים למקורות מידע ( דאטאבייס, API, וכדומה ) כך שברגע ש-"נרשמנו" בתור "צופים" על הנושא, אז מרגע זה, אנחנו משוחררים מלבדוק כל הזמן את המצב של הנושא, אלא רק הגדרנו במקום מסוים, מה קורה כאשר הנושא משנה מצב (state), ומשם ואילך – הטיפול הוא באמצעות ה"צופה" שהגדרנו .

המחשה – Observable

  • ניצור service
  • נייבא פנימה גם את Observable מתוך rxjs/Observable
  • ניצור משתנה שיכיל את המידע, מסוג Observable, ונשתמש ב-syntax של <> כדי להצהיר על הסוג הספציפי שיחזור.
    המשתנה הזה, הוא ה-"צופה", כלומר הוא יכיל פונקציה שתדאג לדווח לנו בכל פעם שיש שינוי ב-state של המידע.
  • ניצור פונקציה שמחזירה את המידע
    • בתוך הפונקציה נשים בתוך המשתנה  מסוג Observable שלנו, אובייקט חדש, מסוג Observable, עם פונקציה שמחזירה מידע
    • לצורך הדוגמא בלבד, נשתמש ב-setTimeout, כדי להחזיר מידע חדש כל X זמן, ובעצם להדגים בזה, כיצד ה"צופה" מרגיש שהמידע השתנה, ומוסיף אותו לסט הנתונים.
  • בקומפוננט
    • נייבא את ה-Service שיצרנו
    • ניצור משתנה שיקבל את התוכן
    • ונרשום פקודה, ש-"נרשמת" לצפיה, בפונקציה של ה-service שמחזירה את הצופה.
    • מה שיקרה הוא שהצופה יחזיר כל פעם מידע ברצף
      כלומר בעצם יצרנו אפשרות ליצור רצף של מידע שמגיע.
--- The service file -----

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';


@Injectable()

export class BasicService {
 data:Observable<Array<number>>;

 constructor() {
 }

 getData() {
 this.data = new Observable(observer => {
 setTimeout(()=> {
 observer.next(1);
 },1000);
 setTimeout(()=> {
 observer.next(2);
 },2000);
 setTimeout(()=> {
 observer.next(3);
 },3000);
 setTimeout(()=> {
 observer.next(4);
 },4000);
 setTimeout(()=> {
 observer.complete();
 },5000);
 });

 return this.data;
 }
 
}



----- the Component file -------
import { Component, OnInit } from '@angular/core';
import { BasicService } from './../../services/basicService2/basic.service';


@Component({
 selector: 'app-use-observable2',
 template: `
 <h1>Observable Example</h1>
 <ul>
 <li *ngFor="let i of data">
 {{ i }}
 </li>
 </ul>
 
 `
})
export class UseObservable2Component implements OnInit {

 data:any[] = [];

 constructor(public basicService:BasicService) {
 this.basicService.getData().subscribe(returnedData => {
 console.log(returnedData);
 this.data.push(returnedData);
 });
 }

 ngOnInit() {
 }

}

שיעור 19 באנגולר – Services

מהו Service באנגולר ?

  • סרויס הוא סוג של קלאס שמאפשר להשתמש במידע בתוך קומפוננטות רבות.
    • מה שמאפשר לשמור את הקופוננט נקי ורזה, ולא להתעסק בו בקריאות למידע.
    • וגם מאפשר לא לחזור על אותן קריאות למידע שוב ושוב מכל קומפוננט. ( DRY – אל תחזור על עצמך ).

איך יוצרים service באנגולר ?

  • ניצור מחלקה חדשה, מקובל לשים services בתיקיה שנקראת services
  • כדי ה- service שלנו יוכל להיות מוזרק לקומפוננטות ( Depency Injection ) , אז אנחנו צריכים לעשות import   של Injectable , שמאפשר למחלקה להיות מוזרקת בהמשך.
  • ובסופו של דבר, נרשום את ה- service ברשימת הספקים הקיימים, שנמצאת במערך ngModule
  • אם עשינו את כל אלו – נוכל להזריק ולהשתמש ב- service בכל קומפוננטה שנרצה.

איך יוצרים service באמצעות כלי שורת הפקודה angular cli ?

  • נקליד :
ng g service MY_SERVICE_NAME

דוגמא בסיסית ל-service באנגולר

  • בקטעי קוד הבאים רואים service פשוט שמחזיר מערך של מידע.
  • ה-service נרשם כספק במערך ה-providers בקובץ app.module.ts ( וכמובן מיובא שם )
  • ה- service מוזרק לתוך קומפוננט ( וכמובן מיובא שם )  בקונסטרקטור
  • המידע מה-service עובר למשתנה בקומפוננט, ומוצג על המסך עם לולאה של אנגולר.
---------- the service file --------------
import { Injectable } from '@angular/core';

@Injectable()

export class BasicService {
 users:string[];

 constructor() {
 this.users = ["Bubu","Koko","Moko"];
 }

 getUsers(){
 return this.users;
 }
}


---------- app.module.ts -----------------

...
...
import { BasicService } from './services/basicService2/basic.service';

...
...
providers: [
 ...
 ...
 
 BasicService
 ],

---- קובץ הקומפוננט --------------------

import { Component, OnInit } from '@angular/core';
import { BasicService } from './../../services/basicService2/basic.service';

@Component({
 selector: 'app-just-another-component',
 template:` 
 
 <h1>Hi</h1>
 <ul>
 <li *ngFor="let user of users">
 {{ user}}
 </li>
 </ul>
 
 `
})
export class JustAnotherComponentComponent implements OnInit {

 users:string[];

 constructor(public basicService:BasicService ) { 

 this.users = this.basicService.getUsers();

 }

 ngOnInit() {
 }

}

 

שיעור 17 ב-ionic, שימוש ב toggleButton

כחלק מרכיבי ה-CSS של ionic , יש גם toggle button, כפתור דו מצבי נחמד.

בקוד הבא אפשר לראות :

  • שימוש בתגית ion-toggle, כדי לשים כפתור דו מצבי על המסך.
  • שימוש ב-[checked] על מנת לקשור אליו משתנה\פונקציה כדי שערכו ישמר.
  • על הדרך , אך ללא קשר, השתמשתי ב- ion-grid – שמאוד דומה לבוטסטראפ, וב-ion-label שמאפשר לשים תויות פשוטות ונחמדות.
<ion-content padding>
 <ion-grid>
 <ion-row>
 <ion-col>
 <ion-label>
 רקע שונה
 </ion-label>
 </ion-col>
 <ion-col>
 <ion-toggle 
 (ionChange)="onToggle($event)" 
 [checked]="checkAltBackground()" ></ion-toggle>
 </ion-col>
 </ion-row>
 </ion-grid>
</ion-content>

 

שיעור 16 ב-ionic – הוספת תפריט צד, שימוש ב MenuController

  • הוספת תפריט צד בנוסף לטאבים, היא מהלך מעט מורכב
  • אציין אותו שלב אחר שלב, ולבסוף אביא את הקוד
  • בתוך app.html נוסיף תגית ion-menu, ובתוכו את הכפתורים הרלוונטים.
  • התגית ion-menu צריכה לבוא עם הגדרה שאומרת לה "היכן להחליף את העמודים" , כדי להגדיר זאת
    • נוסיף לתגית ion-nav סימון רגיל של תבנית אנגולר, כלומר למשל #nav
    • ולתגית ion-menu נוסיף הגדרה [content]="nav"
  • כדי לתת מראה אחיד בתוך התפריט, נשתמש בתגית ion-list, כאשר לכל אחד מהכפתורים נוסיף הגדרת ion-item
  • כמובן צריך להוסיף אירוע click לכל אחד מהכפתורים ב app.component.ts
  • כדי לשלוט בתוך האירוע בעמוד שיוצג, שהוא למעשה עמוד ה-root,
    נשאלת השאלה – איך נקבע את עמוד ה-root ?
    הרי בשלב זה , אנחנו בתוך הקומפוננט הראשון, ה- app.html, אז אנחנו עדין לא יכולים להזריק את NavController, כיוון שהוא עדין לא נוצר ( למרות שיש לו דווקא מתודה נחמדה של setRoot שהייתה יכולה לעזור לנו).
    אז כדי לתת פתרון אחר למטרה שלנו להחליף את עמוד ה-root , אנחנו נשתמש בפונקציה של אנגולר שנקראת ViewChild@,  זו שיטה באנגולר שבה אפשר לגשת לתבנית מסויימת, אחרי שהיא נוצרה, במקרה שלנו, אנחנו ניגש לתפריט אחרי שהוא נוצר, ושם כמובן, נוכל לגשת ל-NavController ולהשתמש במתודה שתאפשר לנו  להגדיר את ה-root.
    כלומר זהו פתרון עוקף שיאפשר לנו להשתמש ב-nav controller.
    הפתרון הזה מתואר בדוקמונטציה בקישור הזה.
  • אחרי שהמשתמש לוחץ על כפתור, אזי אנחנו צריכים לסגור את התפריט, לצורך כך נשתמש באובייקט MenuController, ספציפית במתודה שלו close.
  • כדי להוסיף בעמודים השונים (pages) את סימן התפריט, נוסיף כפתור , עם icon מובנה של ionic שנקרא menu.
  • את הכפתור ב-html, כדאי לעטוף בתגית של ionic שמיועדת לסידור של כפתורים בשורה. היא נקראת ion-buttons, ואפשר להוסיף לה את המאפיין start או end כדי להחליט האם הכפתור ישב בצד ימין או שמאל.
  • כדי להקפיץ את התפריט, נזריק לקלאס הרלוונטי את MenuController , ונשתמש במתודה שלו שנקראת open.
  • אך זה יכול להיות מעצבן מאוד, לו נצטרך בכל עמוד להוסיף את הכפתור, וליצור מתודה עבורו, לצורך כך ישנו קיצור דרך : להוסיף לכפתור התפריט את ההגדרה menuToggle . ואז לא צריך ליצור מתודה, ולייבא וכו'…
---- app.html file -----------------

<ion-menu [content]="nav" >
 <ion-header>
 <ion-toolbar>
 <ion-title>Menu</ion-title>
 </ion-toolbar>
 </ion-header>
 <ion-content>
 <ion-list>
 <button (click)="onLoad(tabsPage)" ion-item>
 <ion-icon name="quote" item-left></ion-icon>
 Quotes
 </button>
 <button ion-item (click)="onLoad(settingsPage)">
 <ion-icon name="settings" item-left></ion-icon>
 Settings
 </button>
 </ion-list>
 </ion-content>
</ion-menu>

<ion-nav [root]="tabsPage" #nav ></ion-nav>



------- app.component.ts file ---------------------

import { Component, ViewChild } from '@angular/core';
import { Platform, NavController, MenuController } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

import { TabsPage } from './../pages/tabs/tabs';
import { SettingsPage } from './../pages/settings/settings';

@Component({
 templateUrl: 'app.html'
})
export class MyApp {
 //rootPage:any = TabsPage;
 tabsPage:any = TabsPage;
 settingsPage:any = SettingsPage;
 @ViewChild('nav') nav:NavController;

 constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen
 ,private mnuCtrl:MenuController) {
 platform.ready().then(() => {
 // Okay, so the platform is ready and our plugins are available.
 // Here you can do any higher level native things you might need.1
 statusBar.styleDefault();
 splashScreen.hide();
 });
 }

 onLoad(page:any) {
 this.nav.setRoot(page);
 this.mnuCtrl.close();
 }
}

--------- בכל דף שבו מעוניינים בתפריט נוסיף משהו דומה -------------


<ion-header>

 <ion-navbar>
 <ion-buttons end >
 <button ion-buton menuToggle >
 <ion-icon name="menu"></ion-icon>
 </button>
 </ion-buttons>
 <ion-title>favorites</ion-title>
 </ion-navbar>

</ion-header>

 

שיעור 18 באנגולר (angular) – טפסים, ngModel, ואירועי מקלדת

  • בפוסט הזה אני רוצה להדגים יכולת חשובה של אנגולר – כיצד לטפל בטפסים.
  • לפני שאני מציג את הטפסים, נציג המחשה קטנה של אירועי מקלדת, כי את אותו רעיון אני אציג אחר כך עם הטפסים.
  • הרעיון הוא להציג את הקלט המוקלד, תוך כדי הקלדה.

המחשה של אירועי מקלדת באנגולר

הקוד הבא מציג את הטקסט המוקלד בשדה, תוך כדי הקלדה.

הוא משתמש באירוע keyup, וב-data binding על מנת לעשות זאת.

import { Component } from '@angular/core';

@Component({
 selector: 'app-keyup2',
 template: `
 <h1> keyup demo</h1>
 <input type="text"
 (keyup)="changeText($event)">
 <hr>
 <h1>Your Text is : {{ text }}</h1>
 
 
 `
})
export class Keyup2Component {
 text:string = '';

 changeText(event) {
 this.text = event.target.value;
 }
}

טפסים ושימוש ב-ngModel באנגולר

  • הדרך הקודמת שהצגתי הייתה מעט גולמית, היות וממש טיפלנו באירוע המקלדת
  • יש דרך יותר אלגנטית לעשות זאת, אם נרתום לטובתינו את מודל הטפסים של אנגולר.
  • בקוד הבא, אני מייבא את ngModel ( גם ב app.module.ts, וגם בקלאס של הקומפוננט שלי )
  • לאחר מכן , בכל שדה של טופס שאני יוצר, אני קושר אליו משתנה מסויים במחלקה באמצעות [(ngModel)]
  • וכך אנחנו נהנים מ-data binding ל-2 הכיוונים.
---- in app.module.ts file -----------

import {FormsModule } from '@angular/forms';
...
...
 imports: [
 BrowserModule,
 FormsModule,
 HttpModule,
 RouterModule.forRoot(appRoutes)
 ],

 
---- in my component ts file -------------------
import { Component, OnInit } from '@angular/core';

@Component({
 selector: 'app-ngmodel22',
 template:`
 <h1> ng model demo</h1>

 <form>
 <label>your name : </label> 
 <input type="text" name="name" [(ngModel)]="name">

 <br><br>
 <label>your age : </label> 
 <input type="text" name="age" [(ngModel)]="age">
 <br><br>
 <input type="submit" value="submit">
 </form>
 
 <hr>
 <h1>Your Name : {{name}}</h1>
 <h1>Your Age : {{age }}</h1>
 `
})
export class Ngmodel22Component implements OnInit {

 name:string = 'Eyal';
 age:number = 20;

 constructor() { }

 ngOnInit() {
 }

}

אירוע של Sumbit עבור טופס

  • בדוגמא הזו ניתן לראות שאירוע ה-submit מטופל בתוספת קטנה בתגית ה–form שמפנה אל פונקציה בקלאס.
  • אומנם בדוגמא הנוכחית, התוכן לא נשלח לשום מקום, אלא רק מתווסף אל רשימה בתחתית המסך.
import { Component, OnInit } from '@angular/core';

@Component({
 selector: 'app-formss2',
 template:`
 <form (submit)="onSubmit()">
 <div class="form-group">
 <label>Name</label>
 <input type="text" class="form-control" 
 [(ngModel)]="name" name="name">
 </div>
 <input type="submit" class="btn btn-success" value="Submit">
 </form>
 <hr>
 <ul class="list-group">
 <li class="list-group-item"
 *ngFor="let user of users">{{ user}}</li>
 </ul>
 
 
 `
})

export class Formss2Component implements OnInit {

 name:string = '';
 users:string[] = ['Bubu','Momo','Koko'];
 constructor() { }

 ngOnInit() {
 }

 onSubmit(){
 // console.log(this.name);
 this.users.push(this.name);
 this.name = '';
 }
}

שימוש בטפסים מבוססי תבנית באנגולר + וולידציה של טפסים באנגולר

בטופס הבא צריך לשים לב לכמה נקודות:

    • בכל שדה הגדרנו את השיוך ל ngModel
    • וגם הגדרנו עבורו id שמפנה ל ngModel
    • וגם הגדרנו בראש הטופס #f  עבור הטופס כולו – שמוצמד ל ngForm
  • בנוגע לוולידציה :
    • הגדרנו div מיוחדים עם הודעות השגיאה, שבאמצעות ngIf* יודעים להגיב לשגיאות.
  • בנוגע לפעולת ה-submit של הטופס, קיבלנו אובייקט שמכיל 2 משתנים,משתנה אחד הוא ערך בוליאני – האם הוולידציה עברה או לא, ומשתנה שני מכיל אובייקט עם תוכן הטופס.
 import { Component, OnInit } from '@angular/core';

 @Component({
 selector: 'app-forms-model33',
 template:`
 <form novalidate #f="ngForm" (ngSubmit)="onSubmit(f)">
 <div class="form-group">
 <label>Name</label>
 <input class="form-control" type="text" [(ngModel)]="user.name"
 name="name"
 #userName="ngModel"
 minlength="2"
 required
 >
 </div>
 <div *ngIf="userName.errors?.required && userName.touched" class="alert alert-danger">שם נדרש</div>
 <div *ngIf="userName.errors?.minlength && userName.touched" class="alert alert-danger">לפחות 2 אותיות נדרשות</div>
 <div class="form-group">
 <label>Email</label>
 <input class="form-control" type="text" [(ngModel)]="user.email"
 name="email"
 #userEmail="ngModel"
 
 required
 >
 <div *ngIf="userEmail.errors?.required && userEmail.touched" class="alert alert-danger">אימייל נדרש</div>
 
 </div>
 <div class="form-group">
 <label>Phone</label>
 <input class="form-control" type="text" [(ngModel)]="user.phone"
 name="phone"
 #userPhone="ngModel"
 minlength="10"
 >
 <div *ngIf="userPhone.errors?.minlength && userPhone.touched" class="alert alert-danger">טלפון של 10 ספרות לפחות </div>
 
 </div>

 <input type="submit" value="Submit" class="btn btn-success">
 </form>
 
 
 `
 })
 export class FormsModel33Component implements OnInit {
 
 user = {name: '' , email: '' , phone : 0 };

 constructor() { }

 ngOnInit() {
 }
 
 onSubmit({value,valid}) {
 if(valid){
 console.log(value);
 }else{
 console.log("Form is invalid");

 }
 }

 }

 

שיעור 15 ב-ionic – יכולת slide מובנית לפריטי רשימה

תכונה מעניינת ב-ionic, היא שבמידה ואנחנו מציגים אלמנטים ברשימה, אפשר להציג אותה עם יכולת "הזזה" הצידה, ואז בהזזה להציג אפשרויות נוספות ( כפתורים וכדומה).

כדי לעשות זאת, אנחנו צריכים לעטוף את אלמנט ion-item, באלמנט ion-item-sliding.

במידה ונרצה להכניס כפתורים וכדומה שמופיעים רק בעת ה-slide, נכניס אותם בתוך ion-item-options.

אז המבנה הבסיסי הוא :

  • ion-list
    • ion-item-sliding — בדרך כלל עם *ngFor
      • ion-item
      • ion-item-options

להלן דוגמא פשוטה :

<ion-header>

 <ion-navbar>
 <ion-title>favorites</ion-title>
 </ion-navbar>

</ion-header>


<ion-content padding>
 <ion-list>
 <ion-item-sliding *ngFor="let quote of quotes">
 <ion-item 
 (click)="onViewQuote(quote)"
 >
 <h2>{{quote.person}}</h2> 
 <p>{{quote.text}}</p>
 </ion-item>
 <ion-item-options>
 <button
 ion-button
 color="danger"
 (click)="onRemoveFromFavorites(quote)">
 <ion-icon name="trash"
 
 >Delete</ion-icon>
 </button>
 </ion-item-options>
 </ion-item-sliding>
 </ion-list>
</ion-content>

 

שיעור 14 ב- ionic – חלון קופץ Modal

כדי להציג חלון קופץ, מה שנקרא Modal, נעשה כך :

  • נייבא את ModalController
  • נוסיף לקונסטרקטור משתנה מסוג ModalController
  • בפונקציה המתאימה, נקרא  למתודה create של ModalController, ונעביר לה כפרמטר את הקלאס של page שאנחנו רוצים להציג כחלון קופץ.
  • וכדי להציג, נשתמש במתודה present.

המחשה –

בדוגמא הבאה, הדף שאני מציג בתור חלון קופץ, נקרא QuotePage :

import { Component } from '@angular/core';
import { IonicPage, ModalController} from 'ionic-angular';
import { QuoteService } from './../../services/quote.service';
import { Quote } from './../../data/quote.interface';
import { QuotePage } from './../quote/quote';

@IonicPage()
@Component({
 selector: 'page-favorites',
 templateUrl: 'favorites.html',
})
export class FavoritesPage {
 quotes:Quote[];

 constructor(private quotesService:QuoteService,
 private modalCtrl:ModalController) {
 }

 ionViewDidLoad() {
 
 }
 ionViewWillEnter(){
 this.quotes = this.quotesService.getFavoriteQuotes();
 }

 onViewQuote(quote:Quote) {
 const modal = this.modalCtrl.create(QuotePage);
 modal.present();
 }


}

 

אלא שיש בעיה קטנה …

אם נציץ בתצוגת mobile, בחלון הקופץ שבנינו, נראה שאנחנו לא יכולים לצאת ממנו.

כדי ליצור פונקציה שסוגרת, עם אירוע, וכו', נשתמש במחלקה של ionic שאחראית על ה-View המוצג ברגע זה, מחלקה זו נקראת ViewController.

בכל פעם שמזריקים את ViewController למחלקה, זה מאפשר לנו לגשת לעמוד הפעיל, העמוד שכרגע המשתמש נמצא בו.

המתודה שנשתמש בה היא dismiss, וכשמה כן היא – היא פשוט סוגרת את ה-view האקטיבי. (צריך להיזהר לא להפעיל אותה על root page 🙂 )

------ on the html file ------------


 <button ion-button color="danger"
 (click)="onClose()"
 >Close</button>

------ on the TS file --------------


import { Component } from '@angular/core';
import { IonicPage, ViewController } from 'ionic-angular';


@IonicPage()
@Component({
 selector: 'page-quote',
 templateUrl: 'quote.html',
})
export class QuotePage {
 constructor(private viewCtrl:ViewController) {}

 onClose() {
 this.viewCtrl.dismiss();
 }

}

איך להעביר מידע לחלון הקופץ ?

  • את המידע אנחנו מעבירים בפרמטר השני של יצירת האובייקט .
  • ואנחנו שולפים את המידע ( בעמוד הנפרד של החלון הקופץ )  דרך אובייקט navParams שאותו כבר הכרנו באחד השיעורים הקודמים
  • נקודה מעניינת לשים לב, היא שאני שולף את המידע באירוע המיוחד של ionic שנקרא ionViewDidLoad , והאירוע הזה מתרחש כל פעם מחדש, ולא נעשה לו cache.
העברת המידע בפרמטר השני



onViewQuote(quote:Quote) {

constmodal=this.modalCtrl.create(QuotePage,quote);

modal.present();

}

שליפת המידע בקלאס של הדף חלון שקופץ
ionViewDidLoad(){this.person=this.navParams.get('person');
this.text=this.navParams.get('text');
}

 העברת מידע מהחלון הקופץ, בחזרה אל החלון שהקפיץ אותו

  •  כדי לבצע זאת, נעביר פרמטר במתודה dismiss שסוגרת את החלון הקופץ
  • כדי לשלוף את הפרמטר שהעברנו , בחלון שהקפיץ , "נאזין" לאירוע הסגירה של ה-modal, באמצעות onDidDismiss, ושם נגדיר callback שמקבל פרמטר , וזהו הפרמטר שהעברנו מהחלון הקופץ.
  • חשוב לזכור ! העמוד שמתחת ל-modal לא מתרענן לאחר שה-modal נסגר, זאת אומרת שאם ביצענו דרך ה-modal שינוי כלשהוא שאמור להשפיע על הדף שמתחתיו – אז צריך לדאוג לבצע את השינוי (או לרענן את הדף).
----- on the modal page TS file -------

onClose(remove = false) {
 
 this.viewCtrl.dismiss(remove);
 
 
 }
 
 
----- on the under-laying page TS file -----
 onViewQuote(quote:Quote) {
 const modal = this.modalCtrl.create(QuotePage,quote);
 modal.present();
 modal.onDidDismiss((remove:boolean) =>{
 if(remove) {
 this.quotesService.removeQuoteFromFavorites(quote);
 }
 
 } );
 }

 

מילה על view hooks