Search code examples
javascriptangularjsangularjs-components

Can't seem to get AngularJS one-way binding to work


I recently started using the (not so new) components for an old angular app. I'm trying to make some dumb components for trivial stuff like <button/>s and the like.

For some reason, I can't seem to get one way bindings to work!

The level binding in the difficultyButton component (in difficult-button.js) always returns undefined, but the onLevelChosen binding (again, in difficulty-button.js) seems to have the callback that the options component passed to it.

Do any of you see where I might've gone wrong?


Here is a jsbin link demonstrating this problem : http://jsbin.com/rixukuh/11/edit?html,js

Notice how the classes red, green and blue never get applied because they could never catch hold of the value of vm.level.

Also, the console always prints out LEVEL => undefined, irrespective of what button is clicked.


FULL CODE

Here's the full code, if more context is needed.

options.tpl.html

<div class="full-page-cover">
 <div class="options-grid">
    <!-- some random markup -->
    <div class="buttons-grid">       
        <difficulty-button 
            level="easy"
            on-level-chosen="vm.chooseDifficulty(level)" >
            I'm just here for having fun!
        </difficulty-button>
        <!-- some more `difficulty-buttons` -->
    </div>                    
  </div>
</div>

options.js

import angular from 'angular';
import DifficultyButtonModule from './difficulty-button.js';
import template from './options.tpl.html';

class OptionsController {
   constructor() { /* ... */ }
   chooseDifficulty(level) { /* ... */ }
}

const OptionsModule = angular.module('options', [DifficultyButtonModule.name])

OptionsModule.component('options', {
  template,
  controller: OptionsController,
  controllerAs: 'vm'
});

export default OptionsModule;  

difficulty-button.tpl.html

<button 
    ng-class="[
       'uk-button uk-button-large',
       {
         easy: 'uk-button-default',
         medium: 'uk-button-primary',
         hard: 'uk-button-danger'
       } [ vm.level ]
     ]" 
     ng-click="vm.onLevelChosen({ level: vm.level })"
     ng-transclude>

</button>

difficulty-button.js

import angular from 'angular';
import template from './difficulty-button.tpl.html';

const DifficultyButtonModule = angular.module('difficultyButton', []);                                    
DifficultyButtonModule.component('difficultyButton', {
  template,
  bindings: { 
    level: '<', 
    onLevelChosen: '&' 
  }, 
  controllerAs: 'vm',
  transclude: true
});

export default DiffButtonModule; 

Solution

  • when you do this

    level="easy"
    

    you're binding against easy property in your scope (like $scope.easy). Which of course does not exist. If you want to bind against the string value directly from your html, you need to use single quotes

    level="'easy'"
    

    and the same for the other levels. That will work fine.

    If you still want to use bind against object, you will need to create them in them your scope. But since you need one way only, using string should work fine

    Disclaimer: I haven't worked with components, so it might be that the explanation is incorrect. I have just worked with angularjs