Ember.js 3.0新手求助:父页面展示分页关联资源的常规实现
嘿,我当初在Ember 3.x里处理这个需求的时候也卡了一阵,不过其实用官方推荐的标准做法就能搞定,完全不用碰那些非常规或者要废弃的特性~下面一步步给你捋清楚:
1. 先确保模型关联定义正确
假设你的父资源是Post,关联的子资源是Comment,父模型里要这样定义关联关系:
// app/models/post.js import Model, { hasMany } from '@ember-data/model'; export default class PostModel extends Model { @hasMany('comment', { async: true }) comments; }
这里的async: true是Ember Data 3.x的默认配置,明确写出来能让关联逻辑更清晰,确保关联资源是异步加载的。
2. 在父路由中加载带分页参数的关联资源
不能直接用post.get('comments')加载所有关联资源(没法传分页参数),改用store.query方法,它支持传递自定义参数给后端:
// app/routes/posts/show.js import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; export default class PostsShowRoute extends Route { @service store; async model(params) { const post = await this.store.findRecord('post', params.post_id); // 初始加载第一页,每页10条数据,参数要和后端提前约定好 const comments = await this.store.query('comment', { post_id: post.id, page: 1, per_page: 10 }); // 把父资源和分页后的子资源一起返回给模板 return { post, comments }; } }
3. 用控制器管理分页状态
Ember 3.0还没有@tracked装饰器,我们用传统的this.set来管理当前页码、总页数这些状态,同时写好切换页码的方法:
// app/controllers/posts/show.js import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class PostsShowController extends Controller { @service store; init() { super.init(...arguments); // 初始化分页状态 this.set('currentPage', 1); this.set('totalPages', 1); } async fetchComments(page) { const { post } = this.model; // 根据目标页码重新加载关联资源 const comments = await this.store.query('comment', { post_id: post.id, page, per_page: 10 }); // 更新总页数(假设后端返回的meta里包含total_pages字段) this.set('totalPages', comments.meta.total_pages); this.set('currentPage', page); // 更新模板里的关联资源数据 this.set('model.comments', comments); } // 计算属性:判断是否能切换到上一页 get canGoPrevious() { return this.get('currentPage') > 1; } // 计算属性:判断是否能切换到下一页 get canGoNext() { return this.get('currentPage') < this.get('totalPages'); } }
4. 模板展示资源和分页控件
用Ember 3.0原生的{{action}}助手绑定分页按钮的点击事件,同时展示资源列表:
{{!-- app/templates/posts/show.hbs --}} <h1>{{model.post.title}}</h1> <div class="comment-list"> {{#each model.comments as |comment|}} <div class="comment-item"> <p>{{comment.body}}</p> <small>作者:{{comment.author}}</small> </div> {{else}} <p>暂无评论</p> {{/each}} </div> <div class="pagination-controls"> <button {{action "fetchComments" (currentPage - 1)}} disabled={{not canGoPrevious}}> 上一页 </button> <span>第 {{currentPage}} 页 / 共 {{totalPages}} 页</span> <button {{action "fetchComments" (currentPage + 1)}} disabled={{not canGoNext}}> 下一页 </button> </div>
5. 配置适配器解析分页元数据
如果后端返回的结构是{ comments: [...], meta: { total_pages: 5 } },需要在子资源的适配器里配置,让Ember Data能正确提取分页元数据:
// app/adapters/comment.js import RESTAdapter from '@ember-data/adapter/rest'; export default class CommentAdapter extends RESTAdapter { normalizeQueryResponse(store, primaryModelClass, payload, id, requestType) { // 调用父类方法完成基础数据解析 const normalizedResponse = super.normalizeQueryResponse(store, primaryModelClass, payload, id, requestType); // 把后端返回的meta数据赋值给解析结果 normalizedResponse.meta = payload.meta; return normalizedResponse; } }
这些都是Ember 3.x的标准用法,完全符合官方推荐的实践,没有用到任何即将废弃的特性。唯一需要配合的是后端要支持接收page和per_page参数,并返回对应的数据及分页元信息~
内容的提问来源于stack exchange,提问作者Devonwalshe
相关产品推荐
相关产品推荐

