Disable pay button page

Learn how to combine the latest values of two RxJS observables with the combineLatest operator.

Video

Who has time to read? This video covers the content on this page. Watch fullscreen.

The problem

In this section, we will:

  • disable the Pay button until the card, expiry, and cvc are valid.

How to solve this problem

  • Create a this.isCardInvalid$ property that publishes true if either this.cardError$, this.expiryError$ or this.cvcError$ are truthy.
  • Create an isCardInvalid function that can be passed the this.cardError$, this.expiryError$ and this.cvcError$ observables and returns the this.isCardInvalid$ observable.

What you need to know

  • The combineLatest static method combines the values from several observables into a single array that emits whenever any of the input observables emits:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.4.0/rxjs.umd.min.js"></script>
    <script type="typescript">
      const { of, zip, timer, from, combineLatest, map } = rxjs;
    
      function sequentially(value, dueTime, period) {
        return zip(
          from(value),
          timer(dueTime, period),
          value => value
        );
      }
    
      const first = sequentially(["Justin", "Ramiya"], 0, 1000);
      const last = sequentially(["Shah", "Meyer"], 500, 1000);
    
      // first: ---Justin---RamiyaX
      // last:  ------Shah__---Meyer_X
      const fullName = combineLatest([first, last]).pipe(
        map(([first, last]) => {
          return first + " " + last;
        })
      );
    
      fullName.subscribe(console.log);
      // fullName: ---Justin Shah
      //             -Ramiya Shah
      //             -Ramiya MeyerX
    </script>
  • [property]="value" can set an element property or attribute from another value:

    <button [disabled]="value"></button>

The solution

Click to see the solution

<script type="typescript">
  // app.js
  const { Component, VERSION } = ng.core;
  const { BehaviorSubject, Subject, merge, combineLatest } = rxjs;
  const { map, tap, scan } = rxjs.operators;

  const cleanCardNumber = map((card) => {
    }
  });

  function isCardInvalid(cardError$, expiryError$, cvcError$) {
    return combineLatest([cardError$, expiryError$, cvcError$]).pipe(
      map(([cardError, expiryError, cvcError]) => {
        return !!(cardError || expiryError || cvcError);
      })
    );
  }

  @Component({
    selector: 'my-app',
          [class.is-error]="showCVCError$ | async"
        />

        <button [disabled]="isCardInvalid$ | async">
          PAY
        </button>
      </form>
      this.cvcError$ = this.cvc$.pipe(validateCVC);
      this.showCVCError$ = showOnlyWhenBlurredOnce(this.cvcError$, this.userCVCBlurred$);

      this.isCardInvalid$ = isCardInvalid(this.cardError$, this.expiryError$, this.cvcError$);
    }
  }

Previous Lesson: CVCNext Lesson: Request payment