Ember数据操作 #

一、创建记录 #

1.1 createRecord #

javascript
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

export default class PostsNewController extends Controller {
  @service store;

  @action
  createPost() {
    const post = this.store.createRecord('post', {
      title: 'New Post',
      body: 'Content...',
    });
  }
}

1.2 保存记录 #

javascript
@action
async savePost() {
  const post = this.store.createRecord('post', {
    title: 'New Post',
    body: 'Content...',
  });

  try {
    await post.save();
    console.log('保存成功');
  } catch (error) {
    console.error('保存失败:', error);
  }
}

1.3 创建并关联 #

javascript
@action
async createComment(post) {
  const comment = this.store.createRecord('comment', {
    body: 'Great post!',
    post: post,
  });

  await comment.save();
}

二、读取记录 #

2.1 findAll #

获取所有记录:

javascript
// app/routes/posts.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class PostsRoute extends Route {
  @service store;

  model() {
    return this.store.findAll('post');
  }
}

2.2 findRecord #

获取单个记录:

javascript
// 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;

  model(params) {
    return this.store.findRecord('post', params.post_id);
  }
}

2.3 query #

条件查询:

javascript
model() {
  return this.store.query('post', {
    category: 'tech',
    status: 'published',
    page: 1,
    limit: 10,
  });
}

2.4 queryRecord #

查询单个记录:

javascript
model() {
  return this.store.queryRecord('post', {
    slug: 'hello-world',
  });
}

2.5 peekAll / peekRecord #

从缓存读取(不发请求):

javascript
// 获取所有已加载记录
const posts = this.store.peekAll('post');

// 获取单个已加载记录
const post = this.store.peekRecord('post', 1);

三、更新记录 #

3.1 基本更新 #

javascript
@action
async updatePost(post) {
  post.title = 'Updated Title';
  await post.save();
}

3.2 更新多个属性 #

javascript
@action
async updatePost(post, attrs) {
  post.setProperties(attrs);
  await post.save();
}

3.3 更新关联 #

javascript
@action
async changeAuthor(post, newAuthor) {
  post.author = newAuthor;
  await post.save();
}

3.4 部分更新 #

javascript
@action
async updateTitle(post, newTitle) {
  post.title = newTitle;
  await post.save({ adapterOptions: { partial: true } });
}

四、删除记录 #

4.1 destroyRecord #

删除并持久化:

javascript
@action
async deletePost(post) {
  await post.destroyRecord();
}

4.2 deleteRecord #

仅标记删除(不持久化):

javascript
@action
markForDeletion(post) {
  post.deleteRecord();
  // 需要调用 save() 才会持久化
}

4.3 unloadRecord #

从Store移除(不持久化):

javascript
@action
removeFromCache(post) {
  post.unloadRecord();
}

五、记录状态 #

5.1 检查状态 #

javascript
// 新记录
post.isNew; // true/false

// 已保存
post.isSaved; // true/false

// 有未保存更改
post.hasDirtyAttributes; // true/false

// 正在保存
post.isSaving; // true/false

// 已删除
post.isDeleted; // true/false

// 正在删除
post.isDeleting; // true/false

// 有效
post.isValid; // true/false

// 有错误
post.hasErrors; // true/false

5.2 changedAttributes #

javascript
const changes = post.changedAttributes();
// { title: ['Old Title', 'New Title'] }

5.3 rollbackAttributes #

javascript
@action
cancelChanges(post) {
  post.rollbackAttributes();
}

六、批量操作 #

6.1 批量创建 #

javascript
@action
async createMultiple() {
  const posts = [
    { title: 'Post 1', body: '...' },
    { title: 'Post 2', body: '...' },
    { title: 'Post 3', body: '...' },
  ];

  const records = posts.map((data) => this.store.createRecord('post', data));

  await Promise.all(records.map((post) => post.save()));
}

6.2 批量更新 #

javascript
@action
async publishAll(posts) {
  posts.forEach((post) => {
    post.isPublished = true;
  });

  await Promise.all(posts.map((post) => post.save()));
}

6.3 批量删除 #

javascript
@action
async deleteAll(posts) {
  await Promise.all(posts.map((post) => post.destroyRecord()));
}

七、错误处理 #

7.1 基本错误处理 #

javascript
@action
async savePost(post) {
  try {
    await post.save();
  } catch (error) {
    console.error('保存失败:', error);
    // 检查错误
    if (post.errors.length > 0) {
      post.errors.forEach((error) => {
        console.log(`${error.attribute}: ${error.message}`);
      });
    }
  }
}

7.2 验证错误 #

javascript
@action
async saveWithValidation(post) {
  try {
    await post.save();
  } catch (error) {
    if (error.errors) {
      error.errors.forEach((err) => {
        console.log(`字段 ${err.source.pointer} ${err.detail}`);
      });
    }
  }
}

7.3 网络错误 #

javascript
@action
async saveWithRetry(post, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      await post.save();
      return;
    } catch (error) {
      if (i === retries - 1) throw error;
      await new Promise((resolve) => setTimeout(resolve, 1000));
    }
  }
}

八、关联操作 #

8.1 添加关联 #

javascript
@action
async addComment(post) {
  const comment = this.store.createRecord('comment', {
    body: 'New comment',
    post: post,
  });

  await comment.save();
}

8.2 移除关联 #

javascript
@action
async removeComment(comment) {
  comment.post = null;
  await comment.save();
}

8.3 更新多对多 #

javascript
@action
async addTag(post, tag) {
  post.tags.pushObject(tag);
  await post.save();
}

@action
async removeTag(post, tag) {
  post.tags.removeObject(tag);
  await post.save();
}

九、缓存控制 #

9.1 强制重载 #

javascript
model(params) {
  return this.store.findRecord('post', params.post_id, {
    reload: true,
  });
}

9.2 后台重载 #

javascript
model(params) {
  return this.store.findRecord('post', params.post_id, {
    backgroundReload: true,
  });
}

9.3 预加载关联 #

javascript
model(params) {
  return this.store.findRecord('post', params.post_id, {
    include: 'author,comments',
  });
}

十、最佳实践 #

10.1 在路由中加载数据 #

javascript
// 好的做法
export default class PostsRoute extends Route {
  model() {
    return this.store.findAll('post');
  }
}

// 避免 - 在组件中加载数据

10.2 使用async/await #

javascript
// 好的做法
async savePost(post) {
  await post.save();
  this.router.transitionTo('posts.show', post.id);
}

// 避免 - Promise链
savePost(post) {
  post.save().then(() => {
    this.router.transitionTo('posts.show', post.id);
  });
}

10.3 处理加载状态 #

handlebars
{{#if @post.isSaving}}
  <span>保存中...</span>
{{else}}
  <button {{on "click" (fn this.save @post)}}>保存</button>
{{/if}}

十一、总结 #

数据操作要点:

操作 方法
创建 createRecord() + save()
读取 findAll(), findRecord(), query()
更新 修改属性 + save()
删除 destroyRecord()
缓存 peekAll(), peekRecord()

掌握数据操作是构建应用的核心技能。

最后更新:2026-03-28