瀏覽代碼

Add modal and delete sections, with screenshots

Matt Raible 7 年之前
父節點
當前提交
6e08b05090
共有 3 個檔案被更改,包括 234 行新增3 行删除
  1. 234
    3
      TUTORIAL.md
  2. 二進制
      static/beer-delete.png
  3. 二進制
      static/beer-modal.png

+ 234
- 3
TUTORIAL.md 查看文件

@@ -384,7 +384,7 @@ Modify `beer.html` to
384 384
 </ion-content>
385 385
 ```
386 386
 
387
-Modify `beer.ts` to be
387
+Modify `beer.ts` to import `BeerService` and add as a provider. Call the `getGoodBeers()` method in the `ionViewDidLoad()` lifecycle method.
388 388
 
389 389
 ```typescript
390 390
 import { Component } from '@angular/core';
@@ -431,7 +431,6 @@ export class TabsPage {
431 431
   tab4Root: any = AboutPage;
432 432
 
433 433
   constructor() {
434
-
435 434
   }
436 435
 }
437 436
 ```
@@ -505,7 +504,6 @@ export class BeerPage {
505 504
       }
506 505
     })
507 506
   }
508
-  ...
509 507
 }
510 508
 ```
511 509
 
@@ -526,6 +524,239 @@ If everything works as expected, you should see a page similar to the one below
526 524
 <img src="./static/good-beers-ui.png" width="600" alt="Good Beers UI">
527 525
 </p>
528 526
 
527
+### Add a Modal for Editing
528
+
529
+Change the header in `beer.html` to have a button that opens a modal to add a new beer.
530
+
531
+```html
532
+<ion-header>
533
+  <ion-navbar>
534
+    <ion-title>Good Beers</ion-title>
535
+    <ion-buttons end>
536
+      <button ion-button icon-only (click)="openModal()" color="primary">
537
+        <ion-icon name="add-circle"></ion-icon>
538
+        <ion-icon name="beer"></ion-icon>
539
+      </button>
540
+    </ion-buttons>
541
+  </ion-navbar>
542
+```
543
+
544
+In this same file, change `<ion-item>` to have a click handler for opening the modal for the current item.
545
+
546
+```html
547
+<ion-item (click)="openModal({id: beer.id})">
548
+```
549
+
550
+Add `ModalController` as a dependency in `BeerPage` and add an `openModal()` method.
551
+
552
+```typescript
553
+export class BeerPage {
554
+  private beers: Array<any>;
555
+
556
+  constructor(public beerService: BeerService, public giphyService: GiphyService,
557
+              public modalCtrl: ModalController) {
558
+  }
559
+
560
+  // ionViewDidLoad method
561
+
562
+  openModal(beerId) {
563
+    let modal = this.modalCtrl.create(BeerModalPage, beerId);
564
+    modal.present();
565
+    // refresh data after modal dismissed
566
+    modal.onDidDismiss(() => this.ionViewDidLoad())
567
+  }
568
+}
569
+```
570
+
571
+This won't compile because `BeerModalPage` doesn't exist. Create `beer-modal.ts` in the same directory. This page will retrieve the beer from the `beerId` that's passed in. It will render the name, allow it to be edited, and show the Giphy image found for the name.
572
+
573
+```typescript
574
+import { BeerService } from '../../providers/beer-service';
575
+import { Component, ViewChild } from '@angular/core';
576
+import { GiphyService } from '../../providers/giphy-service';
577
+import { NavParams, ViewController, ToastController, NavController } from 'ionic-angular';
578
+import { NgForm } from '@angular/forms';
579
+
580
+@Component({
581
+  templateUrl: './beer-modal.html'
582
+})
583
+export class BeerModalPage {
584
+  @ViewChild('name') name;
585
+  beer: any = {};
586
+  error: any;
587
+
588
+  constructor(public beerService: BeerService,
589
+              public giphyService: GiphyService,
590
+              public params: NavParams,
591
+              public viewCtrl: ViewController,
592
+              public toastCtrl: ToastController,
593
+              public navCtrl: NavController) {
594
+    if (this.params.data.id) {
595
+      this.beerService.get(this.params.get('id')).subscribe(beer => {
596
+        this.beer = beer;
597
+        this.beer.href = beer._links.self.href;
598
+        this.giphyService.get(beer.name).subscribe(url => beer.giphyUrl = url);
599
+      });
600
+    }
601
+  }
602
+
603
+  dismiss() {
604
+    this.viewCtrl.dismiss();
605
+  }
606
+
607
+  save(form: NgForm) {
608
+    let update: boolean = form['href'];
609
+    this.beerService.save(form).subscribe(result => {
610
+      let toast = this.toastCtrl.create({
611
+        message: 'Beer "' + form.name + '" ' + ((update) ? 'updated' : 'added') + '.',
612
+        duration: 2000
613
+      });
614
+      toast.present();
615
+      this.dismiss();
616
+    }, error => this.error = error)
617
+  }
618
+
619
+  ionViewDidLoad() {
620
+    setTimeout(() => {
621
+      this.name.setFocus();
622
+    },150);
623
+  }
624
+}
625
+```
626
+
627
+Create `beer-modal.html` as a template for this page.
628
+
629
+```html
630
+<ion-header>
631
+  <ion-toolbar>
632
+    <ion-title>
633
+      {{beer ? 'Beer Details' : 'Add Beer'}}
634
+    </ion-title>
635
+    <ion-buttons start>
636
+      <button ion-button (click)="dismiss()">
637
+        <span ion-text color="primary" showWhen="ios,core">Cancel</span>
638
+        <ion-icon name="md-close" showWhen="android,windows"></ion-icon>
639
+      </button>
640
+    </ion-buttons>
641
+  </ion-toolbar>
642
+</ion-header>
643
+<ion-content padding>
644
+  <form #beerForm="ngForm" (ngSubmit)="save(beerForm.value)">
645
+    <input type="hidden" name="href" [(ngModel)]="beer.href">
646
+    <ion-row>
647
+      <ion-col>
648
+        <ion-list inset>
649
+          <ion-item>
650
+            <ion-input placeholder="Beer Name" name="name" type="text"
651
+                       required [(ngModel)]="beer.name" #name></ion-input>
652
+          </ion-item>
653
+        </ion-list>
654
+      </ion-col>
655
+    </ion-row>
656
+    <ion-row>
657
+      <ion-col *ngIf="beer" text-center>
658
+        <img src="{{beer.giphyUrl}}">
659
+      </ion-col>
660
+    </ion-row>
661
+    <ion-row>
662
+      <ion-col>
663
+        <div *ngIf="error" class="alert alert-danger">{{error}}</div>
664
+        <button ion-button color="primary" full type="submit"
665
+                [disabled]="!beerForm.form.valid">Save</button>
666
+      </ion-col>
667
+    </ion-row>
668
+  </form>
669
+</ion-content>
670
+```
671
+
672
+Add `BeerModalPage` to the `declarations` and `entryComponent` lists in `app.module.ts`.
673
+
674
+You'll also need to modify `beer-service.ts` to have `get()` and `save()` methods.
675
+
676
+```typescript
677
+get(id: string) {
678
+  return this.http.get(this.BEER_API + '/' + id)
679
+    .map((response: Response) => response.json());
680
+}
681
+
682
+save(beer: any): Observable<any> {
683
+  let result: Observable<Response>;
684
+  if (beer['href']) {
685
+    result = this.http.put(beer.href, beer);
686
+  } else {
687
+    result = this.http.post(this.BEER_API, beer)
688
+  }
689
+  return result.map((response: Response) => response.json())
690
+    .catch(error => Observable.throw(error));
691
+}
692
+```
693
+
694
+### Add Swipe to Delete
695
+
696
+To add swipe-to-delete functionality on the list of beers, open `beer.html` and make it so `<ion-item-sliding>` wraps `<ion-item>` and contains the `*ngFor`. Add a delete button using `<ion-item-options>`.
697
+
698
+```html
699
+<ion-content padding>
700
+  <ion-list>
701
+    <ion-item-sliding *ngFor="let beer of beers" >
702
+      <ion-item (click)="openModal({id: beer.id})">
703
+        <ion-avatar item-left>
704
+          <img src="{{beer.giphyUrl}}">
705
+        </ion-avatar>
706
+        <h2>{{beer.name}}</h2>
707
+      </ion-item>
708
+      <ion-item-options>
709
+        <button ion-button color="danger" (click)="remove(beer)"><ion-icon name="trash"></ion-icon> Delete</button>
710
+      </ion-item-options>
711
+    </ion-item-sliding>
712
+  </ion-list>
713
+</ion-content>
714
+```
715
+
716
+Add a `remove()` method to `beer.ts`. 
717
+
718
+```typescript
719
+remove(beer) {
720
+  this.beerService.remove(beer.id).subscribe(response => {
721
+    for (let i = 0; i < this.beers.length; i++) {
722
+      if (this.beers[i] === beer) {
723
+        this.beers.splice(i, 1);
724
+        let toast = this.toastCtrl.create({
725
+          message: 'Beer "' + beer.name + '" deleted.',
726
+          duration: 2000,
727
+          position: 'top'
728
+        });
729
+        toast.present();
730
+      }
731
+    }
732
+  });
733
+}
734
+```
735
+
736
+Add `toastCtrl` as a dependency in the constructor so everything compiles.
737
+
738
+```typescript
739
+constructor(public beerService: BeerService, public giphyService: GiphyService,
740
+          public modalCtrl: ModalController, public toastCtrl: ToastController) {
741
+}
742
+```
743
+
744
+You'll also need to modify `beer-service.ts` to have a `remove()` method.
745
+
746
+```typescript
747
+remove(id: string) {
748
+  return this.http.delete(this.BEER_API + '/' + id)
749
+    .map((response: Response) => response.json());
750
+}
751
+```
752
+
753
+After making these additions, you should be able to add, edit and delete beers.
754
+
755
+<p align="center">
756
+<img src="./static/beer-modal.png" width="350">&nbsp;&nbsp;
757
+<img src="./static/beer-delete.png" width="350">
758
+</div>
759
+
529 760
 ## PWAs with Ionic
530 761
 
531 762
 Ionic 2 ships with support for creating progressive web apps (PWAs). If you’d like to learn more about what PWAs are, see [Navigating the World of Progressive Web Apps with Ionic 2](http://blog.ionic.io/navigating-the-world-of-progressive-web-apps-with-ionic-2/). 

二進制
static/beer-delete.png 查看文件


二進制
static/beer-modal.png 查看文件