【问题标题】:Defer rendering of template in AngularJS在 AngularJS 中延迟渲染模板
【发布时间】:2015-08-10 19:29:40
【问题描述】:

背景信息

我是 AngularJS 的新手,正在开发一个显示照片提要的简单应用程序。我有 2 个视图:

  • 列表 - 显示所有照片列表的主视图
  • 详细信息 - 当您点击其中一张照片时,您会被重定向到此处以查看该照片的详细信息

我正在使用 JSONP 请求从远程 URL 获取照片提要。为了避免在更改视图时多次获取它,我创建了一个提供程序“feedFactory”,连接到两个视图的控制器使用它来将提要对象提供到范围内。

我遇到的问题是我看到 JavaScript 错误(错误显示在帖子底部),因为在最初呈现视图时将未定义的值传递给过滤器。这是因为视图是立即呈现的——在承诺完成和获取提要之前——并且值仍然是未定义的。毕竟一切都正常显示,但我当然需要在 JavaScript 控制台中消除这些错误。

问题

如何推迟模板中视图的呈现,直到完成承诺并将提要插入范围。

代码

providers.js

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

/* Factory providing a function that returns a promise object. Promise provides
   a feed after it is fetched. */
module.factory("feedFactory", ["$http", "$q",
    function($http, $q) {
        /* Adds unique "id" key to every object in the array */
        function indexList(photos) {
            for (var i = 0; i < photos.length; i++) {
                photos[i].id = i;
            }
        };

        /* URL from which the feed is fetched */
        var FEED_URL = "https://api.flickr.com/services/feeds/photos_public.gne?tags=potato&tagmode=all&format=json&jsoncallback=JSON_CALLBACK";

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

        $http.jsonp(FEED_URL)
            .success(function(response) {
                indexList(response.items);
                /* Pass data on success */
                deferred.resolve(response)
            })
            .error(function(response) {
                /* Send friendly error message on failure */
                deferred.reject("Error occured while fetching feed");
            });

        /* Return promise object */
        return deferred.promise;
    }]);

controllers.js

var module = angular.module("flickrFeedControllers", [
    "flickrFeedProviders"
]);


/* Loads the whole feed - list of photos */
module.controller("photoListController", ["feedFactory", "$scope",
    function(feedFactory, $scope) {
        feedFactory.then(function(feed) {
            $scope.feed = feed;
        });
    }]);


/* Load only 1 photo */
module.controller("photoDetailController",
                  ["feedFactory", "$scope", "$routeParams",
    function(feedFactory, $scope, $routeParams) {
        var photoID = parseInt($routeParams.photoID);

        feedFactory.then(function(feed) {
            $scope.photo = feed.items[photoID];
        });
    }]);

filters.js

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


/* Given author_id from Flickr feed, return the URL to his page */
module.filter("flickrAuthorURL", function() {
    var FLICKR_URL = "https://www.flickr.com/";

    return function(author_id) {
        return FLICKR_URL + "photos/" + author_id;
    };
})


/* Given author field from Flickr feed, return hid nickname only */
module.filter("flickrAuthorName", function() {
    /* Regular expression for author field from feed, that groups the name
       part of the string, so that it can be later extracted */
    var nameExtractionRegExp = /.* \((.*)\)/;

    return function(author) {
        return author.match(nameExtractionRegExp)[1];
    }
})


/* Given date ISO string return day number with added suffix st/nd/rd/th */
module.filter("dayNumber", function () {
    return function(dateISO) {
        var suffix;
        var date       = new Date(dateISO);
        var dayOfMonth = date.getDate();

        switch(dayOfMonth % 10) {
        case 1:
            suffix = "st";
            break;
        case 2:
            suffix = "nd";
            break;
        case 3:
            suffix = "rd";
            break;
        default:
            suffix = "th";
            break;
        }

        return dayOfMonth + suffix;
    };
});


/* Splits string using delimiter and returns array of results strings.*/
module.filter("split", function() {
    return function(string, delimiter) {
        return string.split(delimiter);
    };
});

photo-detail.html 模板

<!-- Title -->
<a href="{{ photo.link }}" title="Go to photo's details"
   class="title-container">
  <h2 class="title">{{ photo.title }}</h2>
</a>

<!-- Photo author -->
<a href="{{ photo.author_id | flickrAuthorURL }}"
   title="Go to author's page"
   class="author-link">{{ photo.author | flickrAuthorName }}</a>

<!-- Publication date information -->
<div class="publication-date">
  Published:
  {{ photo.published | dayNumber }}
  {{ photo.published | date : "MMM yyyy 'at' h:mm" }}
</div>

<!-- Photo image -->
<img alt="{{ photo.title }}" ng-src="{{ photo.media['m'] }}" class="photo" />

<!-- Description -->
<p class="description">{{ description }}</p>

<!-- Tag list -->
<ul class="tag-list">
  <li ng-repeat="tag in photo.tags | split : ' '" class="tag">
    <a href="#/tag/{{ tag }}" title="Filter photos by this tag">{{ tag }}</a>
  </li>
</ul>

<!-- Back button -->
<a href="#/photos" title="Go back" class="back" />

控制台错误之一

Error: author is undefined
@http://localhost:8000/app/js/filters.js:24:9
anonymous/fn@http://localhost:8000/app/bower_components/angular/angular.js line 13145 > Function:2:211
regularInterceptedExpression@http://localhost:8000/app/bower_components/angular/angular.js:14227:21
expressionInputWatch@http://localhost:8000/app/bower_components/angular/angular.js:14129:26
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost:8000/app/bower_components/angular/angular.js:15675:34
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost:8000/app/bower_components/angular/angular.js:15951:13
done@http://localhost:8000/app/bower_components/angular/angular.js:10364:36
completeRequest@http://localhost:8000/app/bower_components/angular/angular.js:10536:7
requestLoaded@http://localhost:8000/app/bower_components/angular/angular.js:10477:1

http://localhost:8000/app/bower_components/angular/angular.js
Line 12330

其他错误

Error: string is undefined
@http://localhost:8000/app/js/filters.js:59:9
anonymous/fn@http://localhost:8000/app/bower_components/angular/angular.js line 13145 > Function:2:208
regularInterceptedExpression@http://localhost:8000/app/bower_components/angular/angular.js:14227:21
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost:8000/app/bower_components/angular/angular.js:15675:34
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost:8000/app/bower_components/angular/angular.js:15951:13
done@http://localhost:8000/app/bower_components/angular/angular.js:10364:36
completeRequest@http://localhost:8000/app/bower_components/angular/angular.js:10536:7
requestLoaded@http://localhost:8000/app/bower_components/angular/angular.js:10477:1

http://localhost:8000/app/bower_components/angular/angular.js
Line 12330

【问题讨论】:

  • app/js/filters.js:24:9 中有什么内容? flickrAuthorName 过滤器是否包含未定义的变量?
  • @plamut 我在帖子中添加了 filter.js 文件。第 24 行是“返回 author.match(nameExtractionRegExp)[1];”
  • 啊哈,所以过滤器的输入是未定义的 (photo.author)。确保在过滤器函数中覆盖这种边缘情况(如果收到undefined,只需返回undefined)以避免错误。
  • @plamut 真的能解决问题吗?当并非所有数据仍在范围内时渲染视图是否正常?然后页面看起来很丑 - 在页面上显示“{{ photo.author | flickrAuthorName }}”之类的内容。没有办法摆脱它吗?那么过滤器也不会得到未定义的值。
  • 确实是这样,但确实有时可能并不希望这样做。解决这个问题的一个好方法是推迟视图渲染,直到某些承诺得到解决(例如,已获取所需的数据)。请参阅 this answer 以获取有关如何执行此操作的提示。

标签: angularjs angular-promise angularjs-templates


【解决方案1】:

好的,总结一下cmets和后续的聊天,答案如下:

“作者未定义”错误的原因

原来是自定义过滤器 (flickrAuthorName) 提高了它。 来自模板:

<a ...>{{ photo.author | flickrAuthorName }}</a>

加载模板时,尚未从服务器获取数据,photo.authorundefined,传递给过滤器。过滤器应该更加健壮以检查这种边缘情况,也只需返回 undefined 本身。

“如何推迟模板中视图的呈现,直到完成承诺并将提要插入范围?”

此问题已回复here

这个想法是配置 Angular 的 $route 服务(通过 $routeProvider)等待模板的呈现,直到某些承诺得到解决,例如直到从服务器获取的一些数据到达。

accepted answer复制/粘贴:

$routeProvider.when("path", {
    controller: ["$scope", "mydata", MyPathCtrl], // NOTE THE NAME: mydata
    templateUrl: "...",
    resolve: {
        mydata: ["$http", function($http) { // NOTE THE NAME: mydata
            // $http.get() returns a promise, so it is OK for this usage
            return $http.get(...your code...);
        }]
        // You can also use a service name instead of a function, see docs
    },
    ...
});

Angular 的$routeProvider 文档中也描述了这种机制(在when() 的函数route 参数的描述下)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-02
    • 1970-01-01
    • 2014-01-31
    • 2019-11-19
    • 2018-06-07
    相关资源
    最近更新 更多