2

I have a simpe google-chrome extension app. I'm using bookmarks and it present in manifest file. Firstly, i use chrome bookmarks api in controller, and it works well. But i decided use factory for clear code and best practices.

My index.html file

<!DOCTYPE html>
<html lang="en" ng-app="BookmarksSharer" ng-csp>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>bokmarks sharer</title>
    <link rel="stylesheet" href="/css/main.css"/>
    <script src="/javascript/jquery-2.1.1.min.js"></script>
    <script src="/javascript/angular.min.js"></script>
    <script src="/javascript/bookmark_sharer.js"></script>
    <script src="/javascript/MainController.js"></script>
    <script src="/javascript/BookmarkService.js"></script>
</head>
<body>
    <div id="main_popup" ng-controller="MainController">
        <p>Bookmarks</p>
        <ul>
            <li ng-repeat="bookmark_folder in bookmarks_folders" id="{{bookmark_folder.title}}">
                {{bookmark_folder.title}}
                <ul ng-repeat="bookmarks in bookmark_folder">
                    <li ng-repeat="bookmark in bookmarks | filter">{{bookmark.title}}</li>
                </ul>
            </li>
        </ul>
    </div>
</body>
</html>

bookmark_sharer.js is simple

var app = angular.module("BookmarksSharer", []);

MainController.js very simple too.

app.controller('MainController',['$scope', 'bookmarkFactory', function($scope, bookmarkFactory){
    $scope.boormarks_folders = bookmarkFactory.folders;
}]);

And my factory

app.factory('bookmarkFactory', function () {
    var bookmarks_folders;
    function allBookmarksFolders(){
        chrome.bookmarks.getTree(function(bookmarks_tree){
            bookmarks_folders = bookmarks_tree[0].children;
        });

        return bookmarks_folders;
    }

    return {
        folders: allBookmarksFolders()
    };
});

Why $scope.bookmarks_folders is undefined?

ExpertSystem your code not working too. Simple solution is

app.controller('MainController',['$scope', function($scope){
    chrome.bookmarks.getTree(function(nodes){
        $scope.bookmarks_folders = nodes[0].children;
        $scope.$apply();
    })
}]);

But i want organize my code using factories or services.

3
  • Did you debug and see what this 'bookmarks_tree[0].children;' returns in the bookmark factory? Commented May 31, 2014 at 13:47
  • of course, bookmarks_tree[0].children return right value. Commented May 31, 2014 at 13:50
  • @YegorGorodov: Did you take a look at my answer ? Did it help you sort out the problem ? If so, please mark it as accepted (and optionally upvote it as well) :) Commented Jun 5, 2014 at 5:47

1 Answer 1

4

chrome.bookmarks.getTree() is asynchronous, so by the time the getAllBookmarks() function returns, bookmarks_folders is undefined.

Your service exposes a property (folders) which is bound to the result of allBookmarksFolders(), alas undefined.

Since this operation is asynchronous, your service should return a promise instead, so the controller can use that promise and get the actual data when it is returnd:

// The service
app.factory('bookmarkFactory', function ($q) {
    function retrieveAllBookmarksFolders() {
        var deferred = $q.defer();
        chrome.bookmarks.getTree(function (bookmarks_tree) {
            deferred.resolve(bookmarks_tree[0].children);
        });
        return deferred.promise;
    }

    return {
        foldersPromise: retrieveAllBookmarksFolders()
    };
});

// The controller
app.controller('MainController', function($scope, bookmarkFactory){
    bookmarkFactory.foldersPromise.then(function (bookmarks_folders) {
        $scope.boormarks_folders = bookmarks_folders;
    });
});

The main problem arises from the fact that (in your implementation) you return the value bound to bookmarks_folders at that point and later on re-assign bookmarks_folders to hold a reference to a different object (bookmarks_tree[0].children).

The flow of events inside your service is somewhat like this:

  1. bookmarks_folders is declared (and initialized to undefined).
  2. allBookmarksFolders() is executed and returns (the still undefined) bookmarks_folders.
  3. folders is assigned a reference to the object currently referenced by bookmarks_folders (i.e. undefined).
  4. The getTree() callback executes and assigns to bookmarks_folders a reference to a different object (bookmarks_tree[0].children). At that point folders knows nothing about it and continues to reference to the previous value (undefined).

An alternative approach (one that is used by the $resource btw), is to not assign a new reference to bookmarks_folders, but to modify the already referenced object.

// The service
app.factory('bookmarkFactory', function () {
    var bookmarks_folders = [];   // initialize to an empty array
    function allBookmarksFolders(){
        chrome.bookmarks.getTree(function(bookmarks_tree){
            // Here we need to modify the object (array)
            // already referenced by `bookmarks_folders`

            // Let's empty it first (just in case)
            bookmarks_folders.splice(0, bookmarks_folders.length);

            // Let's push the new data
            bookmarks_tree[0].children.forEach(function (child) {
                bookmarks_folders.push(child);
            });
        });

        return bookmarks_folders;
    }

    return {
        folders: allBookmarksFolders()
    };
});
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.