mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 12:21:36 +03:00
Infinite Scroll on Posts List
closes #2414 - Add PostsController with loadNextPage action - Collect and store pagination info from PostsRoute into PostsController - Use `store.filter` on PostsRoute model hook to auto-update posts list template as post models are added to the store - Add content list view and use it to check for scroll event - Make PostRoute only load a post that's already in the store, until we figure how to load multiple pages on refresh - Add util function from clientold that concats error messages from server response
This commit is contained in:
parent
a8164c7d3f
commit
12e6b09943
65
core/client/controllers/posts.js
Normal file
65
core/client/controllers/posts.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { getRequestErrorMessage } from 'ghost/utils/ajax';
|
||||
|
||||
var PostsController = Ember.ArrayController.extend({
|
||||
// set from PostsRoute
|
||||
paginationSettings: null,
|
||||
|
||||
// holds the next page to load during infinite scroll
|
||||
nextPage: null,
|
||||
|
||||
// indicates whether we're currently loading the next page
|
||||
isLoading: null,
|
||||
|
||||
init: function () {
|
||||
this._super();
|
||||
|
||||
var metadata = this.store.metadataFor('post');
|
||||
this.set('nextPage', metadata.pagination.next);
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an ajax response, concatenates any error messages, then generates an error notification.
|
||||
* @param {jqXHR} response The jQuery ajax reponse object.
|
||||
* @return
|
||||
*/
|
||||
reportLoadError: function (response) {
|
||||
var message = 'A problem was encountered while loading more posts';
|
||||
|
||||
if (response) {
|
||||
// Get message from response
|
||||
message += ': ' + getRequestErrorMessage(response);
|
||||
} else {
|
||||
message += '.';
|
||||
}
|
||||
|
||||
this.notifications.showError(message);
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* Loads the next paginated page of posts into the ember-data store. Will cause the posts list UI to update.
|
||||
* @return
|
||||
*/
|
||||
loadNextPage: function () {
|
||||
var self = this,
|
||||
store = this.get('store'),
|
||||
nextPage = this.get('nextPage'),
|
||||
paginationSettings = this.get('paginationSettings');
|
||||
|
||||
if (nextPage) {
|
||||
this.set('isLoading', true);
|
||||
this.set('paginationSettings.page', nextPage);
|
||||
store.find('post', paginationSettings).then(function () {
|
||||
var metadata = store.metadataFor('post');
|
||||
|
||||
self.set('nextPage', metadata.pagination.next);
|
||||
self.set('isLoading', false);
|
||||
}, function (response) {
|
||||
self.reportLoadError(response);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default PostsController;
|
@ -1,11 +1,27 @@
|
||||
import styleBody from 'ghost/mixins/style-body';
|
||||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||
|
||||
var paginationSettings = {
|
||||
status: 'all',
|
||||
staticPages: 'all',
|
||||
page: 1,
|
||||
limit: 15
|
||||
};
|
||||
|
||||
var PostsRoute = AuthenticatedRoute.extend(styleBody, {
|
||||
classNames: ['manage'],
|
||||
|
||||
model: function () {
|
||||
return this.store.find('post', { status: 'all', staticPages: 'all' });
|
||||
// using `.filter` allows the template to auto-update when new models are pulled in from the server.
|
||||
// we just need to 'return true' to allow all models by default.
|
||||
return this.store.filter('post', paginationSettings, function () {
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function (controller, model) {
|
||||
this._super(controller, model);
|
||||
controller.set('paginationSettings', paginationSettings);
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -1,5 +1,11 @@
|
||||
export default Ember.Route.extend({
|
||||
model: function (params) {
|
||||
return this.store.find('post', params.post_id);
|
||||
var post = this.modelFor('posts').findBy('id', params.post_id);
|
||||
|
||||
if (!post) {
|
||||
this.transitionTo('posts.index');
|
||||
}
|
||||
|
||||
return post;
|
||||
}
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
</section>
|
||||
{{#link-to "new" class="button button-add" title="New Post"}}<span class="hidden">New Post</span>{{/link-to}}
|
||||
</header>
|
||||
<section class="content-list-content">
|
||||
{{#view 'content-list-content-view' tagName="section"}}
|
||||
<ol class="posts-list">
|
||||
{{#each itemController="posts/post" itemView="post-item-view" itemTagName="li"}}
|
||||
{{!-- @TODO: Restore functionality where 'featured' and 'page' classes are added for proper posts --}}
|
||||
@ -30,7 +30,7 @@
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</ol>
|
||||
</section>
|
||||
{{/view}}
|
||||
</section>
|
||||
<section class="content-preview js-content-preview">
|
||||
{{outlet}}
|
||||
|
@ -1,4 +1,43 @@
|
||||
/* global ic */
|
||||
export default window.ajax = function () {
|
||||
|
||||
var ajax = window.ajax = function () {
|
||||
return ic.ajax.request.apply(null, arguments);
|
||||
};
|
||||
|
||||
// Used in API request fail handlers to parse a standard api error
|
||||
// response json for the message to display
|
||||
var getRequestErrorMessage = function (request) {
|
||||
var message,
|
||||
msgDetail;
|
||||
|
||||
// Can't really continue without a request
|
||||
if (!request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Seems like a sensible default
|
||||
message = request.statusText;
|
||||
|
||||
// If a non 200 response
|
||||
if (request.status !== 200) {
|
||||
try {
|
||||
// Try to parse out the error, or default to "Unknown"
|
||||
if (request.responseJSON.errors && Ember.isArray(request.responseJSON.errors)) {
|
||||
|
||||
message = request.responseJSON.errors.map(function (errorItem) {
|
||||
return errorItem.message;
|
||||
}).join('; ');
|
||||
} else {
|
||||
message = request.responseJSON.error || "Unknown Error";
|
||||
}
|
||||
} catch (e) {
|
||||
msgDetail = request.status ? request.status + " - " + request.statusText : "Server was not available";
|
||||
message = "The server returned an error (" + msgDetail + ").";
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
export { getRequestErrorMessage, ajax };
|
||||
export default ajax;
|
||||
|
29
core/client/views/content-list-content-view.js
Normal file
29
core/client/views/content-list-content-view.js
Normal file
@ -0,0 +1,29 @@
|
||||
var PostsListView = Ember.View.extend({
|
||||
classNames: ['content-list-content'],
|
||||
|
||||
checkScroll: function (event) {
|
||||
var element = event.target,
|
||||
triggerPoint = 100,
|
||||
controller = this.get('controller'),
|
||||
isLoading = controller.get('isLoading');
|
||||
|
||||
// If we haven't passed our threshold, exit
|
||||
if (isLoading || (element.scrollTop + element.clientHeight + triggerPoint <= element.scrollHeight)) {
|
||||
return;
|
||||
}
|
||||
|
||||
controller.send('loadNextPage');
|
||||
},
|
||||
|
||||
didInsertElement: function () {
|
||||
var el = this.$();
|
||||
el.bind('scroll', Ember.run.bind(this, this.checkScroll));
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
var el = this.$();
|
||||
el.unbind('scroll');
|
||||
}
|
||||
});
|
||||
|
||||
export default PostsListView;
|
Loading…
Reference in New Issue
Block a user