Reputation: 52
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.
Upvotes: 2
Views: 748
Reputation: 48211
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:
bookmarks_folders
is declared (and initialized to undefined).allBookmarksFolders()
is executed and returns (the still undefined) bookmarks_folders
.folders
is assigned a reference to the object currently referenced by bookmarks_folders
(i.e. undefined).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()
};
});
Upvotes: 4