Question

I have a template (in Emblem) that uses a custom view. The template itself is a simple form where one has to enter (and thereby set) a password. I was using standard Ember.TextField views until now, however, I decided to give the user some information on how good the password actually is.

The custom view I created looks like this:

div.password-strength-view
  Ember.TextField type="password" classNames="form-control" value=view.password placeholder=view.passwordPlaceholder
div.strength-bar class=view.barClass

The javascript behind:

App.PasswordStrengthView = Ember.View.extend({
  templateName: 'views/password-strength-view',

  password: '',
  passwordPlaceholder: '',

  _passwordScore: 0,
  strengthUpdater: function() {
    var calc = function(pw) {
      return ( Math.random() * 4 + 1 ); // TODO: add zxcvbn
    };

    var strength = calc(this.get('password'));
    this.set('_passwordScore', strength);

    return strength;
  }.property('password'),


  barClass: function() {
    var score = this.get('_passwordScore');
    return 'score-' + score;
  }.property('_passwordScore')
});

I now want to update the width of the .strength-bar according to changes of the password input (done with class score-x). I also want to be able to use the view in my templates and set the password property of the view to map (1) to the value of the input and (2) to the property of the controller.

In my template code I tried this:

App.PasswordStrengthView#edituser-pw1 password=newPassword passwordPlaceholder="Super secret"

If I change the newPassword value in my Controller, it is reflected in the view. The view itself does not register any changes. Any help on how to make this happen? Do I listen to the wrong property? view.password doesn't work either.

Was it helpful?

Solution

There are two things I can see at fault here:

Firstly, you have added some extra indirection to your bindings in the view, which I believe is causing problems. You've created a computed property called strengthUpdater that has a side effect of setting the _passwordScore property. Most likely barClass is not seeing that _passwordScore has been updated due to some run loop magic.

_passwordScore: 0,
strengthUpdater: function() {
  // ...
  this.set('_passwordScore', strength); // code smell

  return strength;
}.property('password'),

It is usually not a good idea to change other properties inside a computed property function. Instead, if you need both _passwordScore and strengthUpdater properties to be the same value, use Ember.computed.alias.

This can be simplified to a single computed property:

_passwordScore: function() {
  var calc = function(pw) {
    return ( Math.random() * 4 + 1 );
  };

  return calc(this.get('password'));
}.property('password'),

strengthUpdater: Ember.computed.alias('_passwordScore') // if you still need it

I tried this in a jsBin here and this fixed the most significant part of your issue.

Secondly, the class names are not being applied correctly:

div.strength-bar class=view.barClass

This becomes:

<div class="strength-bar" {{bind-attr class="view.barClass"}}></div>

As you can see, you are declaring a class attribute and then creating a binding on the same attribute. In my testing, the strength-bar class is being overwritten and only score-x is applied. You'll want this:

<div {{bind-attr class=":strength-bar view.barClass"}}></div>

Sorry, I am not sure how Emblem would achieve this syntax.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top