Bacon.js combine properties with .and() only if prop === true

140 views Asked by At

I've picked up Bacon.js recently and doing some UI with it in which I only enable a 'Sign up' button if the requirements are met, one of them is check that the username is valid.

// Returns true or string with invalid reason
var isUsernameValid = username
    .map(function(value) {
        if(value.length < 4) {
            return 'Must be 4 characters at least';
        }

        if(!config.patterns['username'].test(value)) {
            return 'Can only have letters, numbers, periods, or underscores';
        }

        return true;
    });

It either returns true or why the username is invalid.

Later I combine all the results with this:

password.and(username).and(isUsernameValid)
    .not().assign($(view['signup-submit']), 'toggleClass', 'disabled');

Username and password are only true when there's input, but since isUsernameValid always returns a truthy value the buttons ends up being incorrectly enabled.

Is there a better way to return errors or combine properties by passing a function?

I thought of declaring another stream/property to return if the value === true but I'm trying to avoid that.

2

There are 2 answers

0
kristw On BEST ANSWER

The issue was because your truthy value includes both valid and invalid cases. That function never returns falsy values. How about seperating invalid and valid into truthy and falsy values?

var isUsernameInvalid = username
  .map(function(value) {
    if(value.length < 4) {
      return 'Must be 4 characters at least';
    }

    if(!config.patterns['username'].test(value)) {
      return 'Can only have letters, numbers, periods, or underscores';
    }

    return false;
});

Then, without much change to the original code

password.and(username).and(isUsernameInvalid.not())
  .not().assign($(view['signup-submit']), 'toggleClass', 'disabled');
1
OlliM On

I would do it like this:

var isUsernameLongEnough = username.map(function(value) {
  return value.length >= 4
})
var usernameHasCorrectCharacters = username.map(function(value) {
  return config.patterns['username'].test(value)
})

var isUsernameValid = isUsernameLongEnough.and(usernameHasCorrectCharacters)

password.and(username).and(isUsernameValid)
    .not().assign($(view['signup-submit']), 'toggleClass', 'disabled')

isUsernameLongEnough.not().assign($(view['error-too-short']), 'toggle')
usernameHasCorrectCharacters.not().assign($(view['error-invalid-characters']), 'toggle')

It is better to keep each function small and separate the concerns.