最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

javascript - Firebase 3-way data binding with ControllerAs syntax - Stack Overflow

matteradmin1PV0评论

I'm tring to get 3-way data binding with firebase and angularfire. You can see what I've got in Plunker:

app.js:

angular.module('ideaBattle', ["firebase"]);

services:

angular
    .module('ideaBattle')
    .constant('FBURL', '/')
    .service('Ref', ['FBURL', Firebase])
    .factory('dataBank', function(Ref, $firebase) {
        return $firebase(Ref).$asArray();
    });

controller:

angular
    .module('ideaBattle')
    .controller('ideaListCtrl', displayIdeas);

displayIdeas.$inject = ['dataBank'];
function displayIdeas(dataBank){
    var vm = this;
    vm.ideas = dataBank;

    vm.upVote = function(idea){
        vm.ideas[idea.id].votes++;
    };
}

HTML:

<div ng-controller="ideaListCtrl as vm">
    <div ng-repeat="idea in vm.ideas | orderBy: '-votes'">
        <div>
            <h2>{{idea.name}}</h2>
            <p>{{idea.desc|limitTo: 190}}</p>
            <span class="btn" ng-click="vm.upVote(idea)">Vote! <span class="badge"> {{idea.votes}}</span></span>
        </div>
    </div>
</div>

Plunker version:

What it does, it gets the data from firebase and displays it correctly, but when I push the button to call upVote function it only updates locally. I know why it only works locally, but I don't know how to make it also update in firebase.

I've tried with $bindTo, but from what I understand it requires $scope to work, and I'm trying to use "Controller as vm" pattern without injecting $scope.

Can anybody tell me how to bite that?

I'm tring to get 3-way data binding with firebase and angularfire. You can see what I've got in Plunker: http://plnkr.co/edit/RGA4jZK3Y6n4RkPCHK37

app.js:

angular.module('ideaBattle', ["firebase"]);

services:

angular
    .module('ideaBattle')
    .constant('FBURL', 'https://ideabattle.firebaseio./')
    .service('Ref', ['FBURL', Firebase])
    .factory('dataBank', function(Ref, $firebase) {
        return $firebase(Ref).$asArray();
    });

controller:

angular
    .module('ideaBattle')
    .controller('ideaListCtrl', displayIdeas);

displayIdeas.$inject = ['dataBank'];
function displayIdeas(dataBank){
    var vm = this;
    vm.ideas = dataBank;

    vm.upVote = function(idea){
        vm.ideas[idea.id].votes++;
    };
}

HTML:

<div ng-controller="ideaListCtrl as vm">
    <div ng-repeat="idea in vm.ideas | orderBy: '-votes'">
        <div>
            <h2>{{idea.name}}</h2>
            <p>{{idea.desc|limitTo: 190}}</p>
            <span class="btn" ng-click="vm.upVote(idea)">Vote! <span class="badge"> {{idea.votes}}</span></span>
        </div>
    </div>
</div>

Plunker version: http://plnkr.co/edit/RGA4jZK3Y6n4RkPCHK37

What it does, it gets the data from firebase and displays it correctly, but when I push the button to call upVote function it only updates locally. I know why it only works locally, but I don't know how to make it also update in firebase.

I've tried with $bindTo, but from what I understand it requires $scope to work, and I'm trying to use "Controller as vm" pattern without injecting $scope.

Can anybody tell me how to bite that?

Share Improve this question edited Dec 9, 2014 at 0:09 zorza asked Dec 8, 2014 at 21:29 zorzazorza 2,8943 gold badges30 silver badges43 bronze badges 5
  • did you call $save(), see the documentation – Cory Silva Commented Dec 8, 2014 at 21:38
  • From what I understand $save is a function that I need to call everytime I want firebase to update. That's not really the approach I want. I'd like more of a "set-it-and-forget-it" 3-way data binding so that every change made to the data locally would be reflected in firebase. I know it can be done easiliy using $bindTo($scope, "data") method, but I don't want to use the $scope at all, hence my question, how to do it keeping this pattern. – zorza Commented Dec 8, 2014 at 21:48
  • vm.ideas[idea.id].votes++; vm.ideas.$save(idea.id).then(function(ref) { ref.key() === vm-ideas[idea.id].$id; // true }); – Cory Silva Commented Dec 8, 2014 at 21:59
  • This works - ofcourse, but it's still not a 3-way data binding - just function updating firebase. I would need to call it everywhere I make a change, but I want it to be automatic, just like it is locally (2-way data binding). I know it can be done. I cannot figure out how. – zorza Commented Dec 8, 2014 at 23:03
  • Well everything is just a function. Try extending Array factory, to keep things DRY – Cory Silva Commented Dec 8, 2014 at 23:14
Add a ment  | 

3 Answers 3

Reset to default 10

tl;dr; — 3-way data-binding does not work with ControllerAs syntax. The bindTo method requires $scope.

You can use AngularFire with ControllerAs syntax, but you can't use it with ControllerAs with $bindTo.

$bindTo has a hard dependency on $scope and it will break without it.

If you want an example of using AngularFire with ControllerAs syntax, check out this Plunker demo.

  angular.module('app', ['firebase'])

  // constant for the Firebase we're using
  .constant('FBURL', 'https://<your-firebase>.firebaseio./todos')

  // return the Firebase ref as a service
  .service('Ref', ['FBURL', Firebase])

  // return the Todos from Firebase by returning the
  // array from the factory 
  .factory('Todos', function(Ref, $firebase) {
    return $firebase(Ref).$asArray();
  })

  // inject the Todos and assign them to "this"
  // for the ControllerAs syntax
  .controller('MainCtrl', function(Todos) {
    this.todos = Todos;
  });

John Papa talks about one of the purposes of using the var vm = this; syntax instead of $scope in every controller is to make the use of $scope a conscious choice. In this case we need to include $scope.

I took David East's plunkr in his answer and fiddled with it a bit. It isn't perfect because it depends on the controllerAs value being 'vm'.

http://plnkr.co/edit/vLLaa7QJvfryYRD7cZvO?p=preview

  .controller('MainCtrl', function(Todos, $scope) { /* Add $scope */
    var vm = this;

    vm.todos = Todos.all();

    vm.lonelyTodo = Todos.get('-JeNOtYPv7AZmVAoZ1bu');
    vm.lonelyTodo.$bindTo($scope, 'vm.lonelyTodo'); /* Add three way binding */
  });

Adding a few clarifications ments to the response above using ES6/JS2015 systax as an example.

export class SomeController {
  constructor($firebaseObject, $scope) {
  'ngInject';    

  //using the firebase SDK 3.0 
  let obj = $firebaseObject(firebase.database().ref().child('someKey'));

  // To make the data available in the DOM, assign it to
  // 'this.data' accessible from DOM as $ctrl.data
  this.data = obj;

  // For three-way data bindings, you will still need to inject '$scope'
  // but you can alias your controller on $scope
  obj.$bindTo($scope, '$ctrl.data');

  // Why does this work? 
  // This works because angular 1x puts controllerAs
  // on top of $scope. So '$scope.$ctrl.data' is the same as 'this.data'.
  // Note: $ctrl is the default controllerAs syntax if not specified,
  // just change $ctrl to your defined controllerAs ailias if 
  // specified. 
  }
}
Post a comment

comment list (0)

  1. No comments so far