3

Solved: The below code now works for anyone who needs it.

  1. Create an Angular factory that queries a database and returns query results.
  2. Pass those results of the query into the $scope of my controller
  3. Render the results of the $scope variable in my view (html)

Challenge: Because this is for a node-webkit (nwjs) app, I am trying to do this without using express, setting up api endpoints, and using the $http service to return the query results. I feel like there should be a more eloquent way to do this by directly passing the data from nodejs to an angular controller. Below is what I've attempted but it hasn't worked.

Database: Below code is for a nedb database.

My Updated Controllers

app.controller('homeCtrl', function($scope,homeFactory){

    homeFactory.getTitles().then(function(data){
        $scope.movieTitles = data;
    });
});

My Updated Factory:

app.factory('homeFactory', function($http,$q) {

return {

    getTitles: function () {
        var deferred = $q.defer();
        var allTitles = [];

        db.find({}, function (err, data) {
            for (var i = 0, len = data.length; i < len; i++) {
                allTitles.push(data[i].title)
            }
            deferred.resolve(allTitles);
        });

        return deferred.promise;
    }
}

});

HTML

   <script>
        var Datastore = require('nedb');
        var db = new Datastore({ filename: './model/movies.db', autoload: true });
    </script>

<--shows up as an empty array-->
<p ng-repeat="movie in movieTitles">{{movie}}</p>
user2263572
  • 5,435
  • 5
  • 35
  • 57

2 Answers2

1

Partial Answer: I was able to get it working by moving all the code into the controller. Anyone now how to refactor the below to use a factory to clean up the code?

Updated Controller ($scope.movietitles is updating in views with the correct data):

    $scope.movieTitles = [];

    $scope.getMovieTitles =  db.find({},function(err,data){

        var arr = [];

        for (var i = 0, len = data.length; i < len; i++){
            arr.push(data[i].title)
        }

        $scope.movieTitles = arr;
        $scope.$apply();

    });
user2263572
  • 5,435
  • 5
  • 35
  • 57
1

So the problem with your original homeFactory is that db.find happens asynchronously. Although you can use it just fine with callbacks, I think it is better practice to wrap these with $q.

app.service('db', function ($q) {
  // ...
  this.find = function (query) {
    var deferred = $q.defer();

    db.find(query, function (err, docs) {
      if (err) {
        deferred.reject(err);
      } else {
        deferred.resolve(docs);
      }
    });

    return deferred.promise;
  };
  // ...
});

Once you begin wrapping non-Angular code like this, then it makes the interface consistent.

app.service('homeFactory', function (db) {
  this.getAllTitles = function () {
    return db.find({}).then(function (docs) {
      return docs.map(function (doc) {
        return doc.title;
      });
    });
  };
});

And the controller would look like:

app.controller('homeCtrl', function ($scope, homeFactory) {
  homeFactory.getAllTitles().then(function (titles) {
    $scope.movieTitles = titles;
  });
});
Zachary Kuhn
  • 1,152
  • 6
  • 13
  • Thanks for the response. I was headed in that direction, see updated code above in my question. For some reason I am returning data from the promise which is an array of string, yet when I set my $scope.movieTitles = data it's not carrying over to views. – user2263572 Jul 27 '15 at 01:03
  • Try to move the `deferred.resolve(allTitles);` line into the callback to `db.find`. – Zachary Kuhn Jul 27 '15 at 01:13
  • Moving deferred.resolve(allTitles) solved the problem and now everything is working. – user2263572 Jul 27 '15 at 02:50