在移动node-webkit窗口之前,ng-repeat不显示数据

时间:2014-12-19 22:07:32

标签: angularjs angularjs-ng-repeat node-webkit

我正在node-webkit中编写一个Angular应用程序,以便我可以访问文件系统和其他API,如桌面通知等等。

我有一个Angular服务(让我们称之为FileService)使用fs.readdir()来读取文件目录并将每个文件的名称作为对象推送到名为allFiles的数组上。因此,当此方法完成其操作时,它将返回具有此结构的对象的数组allFiles{name: "some_file_name.txt"}通过回调。

以下是FileService代码的相关位的显示方式:

// ...
methods.getFileList = function (cb) {
    var allFiles = [];

    fs.readdir(directory, readFiles);

    // @todo: this is ugly as hell. Refactor later.
    function readFiles(err, files) {
        if (err) throw err;
        files.forEach(function (filename) {
            if (!isSupportedFileFormat(filename)) return;

            fs.open(directory + filename, 'r', function (err, fileDescriptor) {
                var buffer = new Buffer(85);

                fs.read(fileDescriptor, buffer, 0, buffer.length, null, function (err, bytesRead, buffer) {
                    allFiles.push({
                        name: filename,
                        contents: buffer.toString('utf8', 0, bytesRead)
                    });
                    fs.close(fileDescriptor);
                });
            });
        });
        cb(allFiles);
    }
};
// ...

在我的主控制器中,我们称之为MyCtrl,我有一些类似的代码:

// FileService is the Angular factory I wrote that uses Node's fs to get files
FileService.getFileList(function (list) {
    $scope.listOfFiles = list;
    console.log($scope.listOfFiles);
});

在我看来(让我们说它在index.html中),我有一些像这样的代码:

<ul class="file-list">
    <li ng-repeat="file in listOfFiles">{{ file.name }}</li>
</ul>

现在,我已经设置了所有内容,以便在应用程序加载时弹出调试控制台,以便我可以看到正在发生的事情。当我构建并运行应用程序时,我可以在控制台中看到所有文件的列表,这些文件在范围变量listOfFiles中很好地显示。但是,在我的index.html视图中,我只是在屏幕上显示一个空白区域,列表应该是。 但是,一旦我移动应用程序窗口或点击textarea甚至是列表所在的空白区域,文件列表就会像我期望的那样神奇地弹出。

我开始抓住稻草,并认为我可能需要触发重绘以显示我的文件列表。所以我尝试了this site上的建议以及Paul Irish的一些建议,但这些解决方案都没有解决问题。

更新:我想要注意我尝试了

$scope.$apply(function() {
    $scope.listOfFiles = list;
}

但遗憾的是,这也没有解决问题。

2 个答案:

答案 0 :(得分:2)

就像对注释的扩展一样,你在那里进行了太多的非角度异步活动,并且你正在使用数组引用调用回调(此时将为空),但数组中没有项目。这就是你不能立即看到数据的原因。还要避免在角度服务中进行回调,而是使用angular $q并返回一个promise。在非角度活动的情况下,您通常可以使用延迟对象($q.defer)返回自定义承诺。创建$ q promise并将其链接起来将确保在执行promise链回调后自动调用摘要周期。这样就无需执行scope.$apply。在您的情况下,更新后没有发生摘要周期,因此即使您的模型更新,相应的DOM绑定(在视图中)也不会更新(这是摘要周期的一部分)。当你点击其他一些可能有自己的ng-click或者某个消化周期的控件时,你会突然看到数据。

在你的服务中注入$q并执行类似的操作(需要进行大量的重新分解,这是一个未经测试的快速和脏代码):

methods.getFileList = function () {

    var defer = $q.defer(); //Create a deferred object

    var allFiles = [];

    fs.readdir(directory, readFile);

    // @todo: this is ugly as hell. Refactor later.
    function readFile(err, files) {
        if (err) throw err;

        var cnt = 0; //Set up a counter

       //Resolve/reject promise here too if there is no file list

        files.forEach(function (filename) {
            if (!isSupportedFileFormat(filename)) { 

               //Resolve promise here too after the check
                return ++cnt;
            }

            fs.open(directory + filename, 'r', function (err, fileDescriptor) {
                var buffer = new Buffer(85);

                fs.read(fileDescriptor, buffer, 0, buffer.length, null, function (err, bytesRead, buffer) {
                    allFiles.push({
                        name: filename,
                        contents: buffer.toString('utf8', 0, bytesRead)
                    });
                    fs.close(fileDescriptor);

                    //Check for the counter (Reverify this logic)
                    if(++cntr === files.length){
                       defer.resolve(allFiles);
                    }

                });
            });
        });

    }

  //Return a promise
  return defer.promise;
};

在您的控制器链中,通过服务方法返回的承诺: -

FileService.getFileList().then(function (list) {
    $scope.listOfFiles = list;
    console.log($scope.listOfFiles);
});

答案 1 :(得分:1)

作为@ PSL答案的后续内容,我重构了我的代码以避免回调地狱,并将其作为一个机会来了解有关$q的更多信息。我仍然有我的保留意见,但我想我会把它发布在这里,以防它对任何人有帮助。这就是我想出的:

methods.getNotes = function () {
    var allFiles = [];

    return getFiles(directory).then(processFiles);

    function getFiles(directory) {
        return $q(function (resolve, reject) {
            fs.readdir(directory, function (err, files) {
                if (err) {
                    resolve(err);
                } else {
                    resolve(files);
                }
            });
        });
    }

    function processFiles(files) {
        return $q(function (resolve, reject) {
            var promises = [];
            files.forEach(function (filename) {
                promises.push(processFile(filename));
            });

            $q.all(promises).then(function () {
                resolve(allFiles);
            });
        });
    }

    function processFile(filename) {
        return $q(function (resolve, reject) {
            if (!isSupportedFileFormat(filename)) {
                resolve();
                return 'Error processing ' + filename;
            }

            openFile(directory, filename, 'r').then(function () {
                resolve();
            });
        });
    }

    function openFile(directory, filename, flags) {
        return $q(function (resolve, reject) {
            fs.open(directory + filename, flags, function (err, fileDescriptor) {
                if (err) {
                    resolve(err);
                    // implement later
                } else {
                    readFile(fileDescriptor, filename).then(function (err, bytesRead, buffer) {
                        if (err) return err;
                        resolve(bytesRead, buffer);
                    });
                }
            });
        });
    }

    function readFile(fileDescriptor, filename) {
        return $q(function (resolve, reject) {
            var buffer = new Buffer(85);
            fs.read(fileDescriptor, buffer, 0, buffer.length, null, function (err, bytesRead, buffer) {
                allFiles.push({
                    filename: filename,
                    title: stripFileExtension(filename),
                    contents: buffer.toString('utf8', 0, bytesRead)
                });
                fs.close(fileDescriptor);
                resolve(err, bytesRead, buffer);
            });
        });
    }
};

我认为这有点冗长,但它更平坦,避免使用文件的计数器,并且更容易阅读和理解......至少对我来说,无论如何。您现在已经注意到我已经跳过了实现错误处理的问题,因为我发现如果阵列中的任何承诺被拒绝,Angular的$q.all()将会保释。显然原始的Q实现确实有allSettled方法来处理这类事情,但不幸的是,它看起来并不像Angular那样移植过(参见this question)。

欢迎任何建议和意见:)