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.
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) })
})
1 Answer 1
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);
});
})