2
\$\begingroup\$

This is my first time using RxJS (or any observable library). It seems to work okay but I'm not sure about best practices.

The thing that I am most unsure about is subscribing to the observable multiple times. I couldn't decide if it would be better to subscribe to it once with one big function, or to use many smaller subscribers.

CodePen Demo

Markup

<div class="field">
  <label class="label">Password</label>
  <div class="control has-icons-left">
    <input id="password" class="input" type="password" />
    <span class="icon is-small is-left">
      <i class="fa fa-lock"></i>
    </span>
  </div>
  <p id="passwordHelp" class="help is-danger"></p>
</div>
<div class="field">
  <label class="label">Password Strength</label>
  <progress id="passwordStrength" class="progress is-danger" value="0" max="5" />
</div>

Script

const { fromEvent } = rxjs
const { map, filter } = rxjs.operators

document.addEventListener('DOMContentLoaded', () => {
  const passwordInput = document.getElementById('password')
  const passwordHelp = document.getElementById('passwordHelp')
  const passwordStrength = document.getElementById('passwordStrength')

  const passwordChanged$ = fromEvent(passwordInput, 'input')
    .pipe(
      map(x => x.target.value),
      map(zxcvbn)
    )

  passwordChanged$
    .pipe(map(password => password.score > 2 ? 'success' : 'danger'))
    .subscribe(state => {
      passwordInput.className = `input is-${state}`
      passwordStrength.className = `progress is-${state}`
    })

  passwordChanged$
    .subscribe(password => { passwordHelp.innerHTML = password.feedback.warning })

  passwordChanged$
    .subscribe(password => { passwordStrength.setAttribute('value', password.score + 1) })
})
\$\endgroup\$
1
\$\begingroup\$

Each subscribe(...) invocation results in creating and returning a new Subscription unless special measures are taken (like .publish(), .share()...)

Subscribing to observables

Remember, observables are lazy. If you don’t subscribe nothing is going to happen. It’s good to know that when you subscribe to an observer, each call of subscribe() will trigger it’s own independent execution for that given observer. Subscribe calls are not shared among multiple subscribers to the same observable. — Luuk Gruijs

Each Subscription

  • eats up a bit of resources;
  • is processed separately from the others;
  • and needs to be unsubscribed separately to release the resources.

Therefore, if you have no special reasons, you should subscribe the least number of times. In your case, a single subscription is sufficient. You can still use separate functions to delegate them the actual work (setClassNames(), setWarning(), setStrength()).


const { fromEvent } = rxjs
const { map, filter } = rxjs.operators

document.addEventListener('DOMContentLoaded', () => {
  const passwordInput = document.getElementById('password')
  const passwordHelp = document.getElementById('passwordHelp')
  const passwordStrength = document.getElementById('passwordStrength')

  const passwordChanged$ = fromEvent(passwordInput, 'input')
    .pipe(
      map(x => x.target.value),
      map(zxcvbn)
    )

  const setClassNames = password => {
    const state = password.score > 2 ? 'success' : 'danger';
    passwordInput.className = `input is-${state}`
    passwordStrength.className = `progress is-${state}`
  };
  const setWarning = password => { passwordHelp.innerHTML = password.feedback.warning };
  const setStrength = password => { passwordStrength.setAttribute('value', password.score + 1) };

  passwordChanged$
    .subscribe(password => {
      setClassNames(password);
      setWarning(password);
      setStrength(password);
    });
})
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.