Ember模型定义 #
一、模型基础 #
1.1 创建模型 #
bash
ember generate model post
ember generate model user
ember generate model comment
1.2 基本模型 #
javascript
// app/models/post.js
import Model, { attr } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@attr('string') body;
@attr('date') createdAt;
}
二、属性定义 #
2.1 基本属性类型 #
javascript
import Model, { attr } from '@ember-data/model';
export default class UserModel extends Model {
@attr('string') name;
@attr('number') age;
@attr('boolean') isActive;
@attr('date') birthDate;
@attr('array') tags;
@attr('object') metadata;
}
2.2 默认值 #
javascript
export default class PostModel extends Model {
@attr('string', { defaultValue: '' }) title;
@attr('number', { defaultValue: 0 }) viewCount;
@attr('boolean', { defaultValue: false }) isPublished;
@attr('array', { defaultValue: () => [] }) tags;
@attr('object', { defaultValue: () => ({}) }) metadata;
}
2.3 自定义转换 #
javascript
// app/transforms/point.js
import Transform from '@ember-data/serializer/transform';
export default class PointTransform extends Transform {
deserialize(serialized) {
return { x: serialized[0], y: serialized[1] };
}
serialize(deserialized) {
return [deserialized.x, deserialized.y];
}
}
javascript
// app/models/location.js
import Model, { attr } from '@ember-data/model';
export default class LocationModel extends Model {
@attr('point') coordinates;
}
三、关联关系 #
3.1 belongsTo(一对一/多对一) #
javascript
// app/models/post.js
import Model, { attr, belongsTo } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@belongsTo('user') author;
}
javascript
// app/models/user.js
import Model, { attr, hasMany } from '@ember-data/model';
export default class UserModel extends Model {
@attr('string') name;
@hasMany('post') posts;
}
3.2 hasMany(一对多) #
javascript
// app/models/post.js
import Model, { attr, hasMany } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@hasMany('comment') comments;
}
javascript
// app/models/comment.js
import Model, { attr, belongsTo } from '@ember-data/model';
export default class CommentModel extends Model {
@attr('string') body;
@belongsTo('post') post;
}
3.3 多对多 #
javascript
// app/models/post.js
import Model, { attr, hasMany } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@hasMany('tag') tags;
}
javascript
// app/models/tag.js
import Model, { attr, hasMany } from '@ember-data/model';
export default class TagModel extends Model {
@attr('string') name;
@hasMany('post') posts;
}
3.4 关联选项 #
javascript
export default class PostModel extends Model {
// 异步加载
@belongsTo('user', { async: true }) author;
// 反向关联
@hasMany('comment', { inverse: 'post' }) comments;
// 多态关联
@belongsTo('commentable', { polymorphic: true }) commentable;
}
3.5 自引用关联 #
javascript
// app/models/category.js
import Model, { attr, belongsTo, hasMany } from '@ember-data/model';
export default class CategoryModel extends Model {
@attr('string') name;
@belongsTo('category', { inverse: 'children' }) parent;
@hasMany('category', { inverse: 'parent' }) children;
}
四、计算属性 #
4.1 基本计算属性 #
javascript
import Model, { attr, belongsTo } from '@ember-data/model';
export default class UserModel extends Model {
@attr('string') firstName;
@attr('string') lastName;
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
get initials() {
return `${this.firstName[0]}${this.lastName[0]}`.toUpperCase();
}
}
4.2 基于关联的计算属性 #
javascript
import Model, { attr, hasMany } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@hasMany('comment') comments;
get commentCount() {
return this.comments.length;
}
get hasComments() {
return this.commentCount > 0;
}
}
4.3 异步计算属性 #
javascript
import Model, { attr, hasMany } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@hasMany('comment') comments;
get asyncComments() {
return this.comments;
}
}
handlebars
{{! 使用async/await}}
{{#each (await @post.asyncComments) as |comment|}}
<p>{{comment.body}}</p>
{{/each}}
五、模型方法 #
5.1 自定义方法 #
javascript
import Model, { attr } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@attr('string') body;
@attr('date') createdAt;
get excerpt() {
return this.body?.substring(0, 100) + '...';
}
isOlderThan(days) {
const diff = Date.now() - this.createdAt.getTime();
return diff > days * 24 * 60 * 60 * 1000;
}
}
5.2 静态方法 #
javascript
import Model, { attr } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
@attr('string') status;
static findPublished(store) {
return store.query('post', { status: 'published' });
}
}
六、模型验证 #
6.1 内置验证 #
javascript
import Model, { attr } from '@ember-data/model';
import { validates } from 'ember-cp-validations';
export default class UserModel extends Model {
@attr('string') name;
@attr('string') email;
get validations() {
return {
name: {
presence: true,
length: { minimum: 2, maximum: 50 },
},
email: {
presence: true,
format: { type: 'email' },
},
};
}
}
6.2 自定义验证 #
javascript
import Model, { attr } from '@ember-data/model';
export default class UserModel extends Model {
@attr('string') password;
@attr('string') passwordConfirmation;
get isValid() {
return this.password === this.passwordConfirmation;
}
get errors() {
const errors = [];
if (this.password !== this.passwordConfirmation) {
errors.push({ field: 'passwordConfirmation', message: '密码不匹配' });
}
return errors;
}
}
七、模型钩子 #
7.1 ready钩子 #
javascript
import Model, { attr } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
ready() {
console.log('模型已加载');
}
}
7.2 didCreate钩子 #
javascript
import Model, { attr } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
didCreate() {
console.log('记录已创建');
}
}
7.3 didUpdate钩子 #
javascript
import Model, { attr } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
didUpdate() {
console.log('记录已更新');
}
}
7.4 didDelete钩子 #
javascript
import Model, { attr } from '@ember-data/model';
export default class PostModel extends Model {
@attr('string') title;
didDelete() {
console.log('记录已删除');
}
}
八、模型继承 #
8.1 基础模型 #
javascript
// app/models/base/content.js
import Model, { attr } from '@ember-data/model';
export default class ContentModel extends Model {
@attr('string') title;
@attr('date') createdAt;
@attr('date') updatedAt;
}
8.2 继承模型 #
javascript
// app/models/post.js
import ContentModel from './base/content';
import { attr, hasMany } from '@ember-data/model';
export default class PostModel extends ContentModel {
@attr('string') body;
@hasMany('comment') comments;
}
九、最佳实践 #
9.1 命名约定 #
javascript
// 好的命名
@attr('string') firstName;
@attr('string') lastName;
@belongsTo('user') author;
// 避免
@attr('string') fn;
@attr('string') ln;
@belongsTo('u') a;
9.2 合理的关联 #
javascript
// 好的做法 - 明确的关联
@belongsTo('user', { async: true }) author;
@hasMany('comment', { inverse: 'post' }) comments;
// 避免 - 过度关联
@belongsTo('user') author;
@belongsTo('user') editor;
@belongsTo('user') reviewer;
// ...
9.3 文档化 #
javascript
/**
* Post模型
*
* @property {string} title - 文章标题
* @property {string} body - 文章内容
* @property {Date} createdAt - 创建时间
* @property {User} author - 作者
* @property {Comment[]} comments - 评论列表
*/
export default class PostModel extends Model {
@attr('string') title;
@attr('string') body;
@attr('date') createdAt;
@belongsTo('user') author;
@hasMany('comment') comments;
}
十、总结 #
模型定义要点:
| 概念 | 说明 |
|---|---|
| @attr | 定义属性 |
| @belongsTo | 定义一对一关联 |
| @hasMany | 定义一对多关联 |
| getter | 计算属性 |
| 钩子 | 生命周期回调 |
良好的模型设计是应用数据层的基础。
最后更新:2026-03-28