31、Ember.js 控制器开发:实现新目击记录的创建、编辑与删除

Ember.js实现目击记录CRUD

Ember.js 控制器开发:实现新目击记录的创建、编辑与删除

1. 控制器概述

控制器是 MVC 模式的最后一部分,它负责处理应用逻辑,获取模型实例并将其传递给视图,同时包含用于修改模型实例的处理函数。在 Ember 应用运行时,它会自动添加控制器对象,控制器充当路由对象和模板之间的代理,传递模型数据。创建控制器可以定义路由激活时要监听的事件或操作,还能定义装饰器属性来增强要显示的模型数据。

2. 创建新目击记录
2.1 设置路由模型

首先,打开 app/routes/sightings/new.js 文件,添加一个模型钩子,使用 Ember.RSVP.hash({}) 返回一个包含新目击记录和相关数据的 Promise 集合:

export default Ember.Route.extend({
  model() {
    return Ember.RSVP.hash({
      sighting: this.store.createRecord('sighting'),
      cryptids: this.store.findAll('cryptid'),
      witnesses: this.store.findAll('witness')
    });
  }
});

当此路由激活时,会返回一个新的目击记录。此时,新记录已添加到本地集合中,但还未保存到持久化数据源。

2.2 安装插件

在使用 <select> 标签展示生物和证人列表之前,需要安装 emberx-select 插件,它能方便地使用绑定属性的 <select> 标签:

ember install emberx-select

安装完成后,重启 Ember 服务器。

2.3 创建新目击记录表单

编辑 app/templates/sightings/new.hbs 文件,创建新目击记录表单:

<h1>New Sighting</h1>
<form>
  <div class="form-group">
    <label for="name">Cryptid</label>
    {{#x-select value=model.sighting.cryptid class="form-control"}}
      {{#x-option}}Select Cryptid{{/x-option}}
      {{#each model.cryptids as |cryptid|}}
        {{#x-option value=cryptid}}{{cryptid.name}}{{/x-option}}
      {{/each}}
    {{/x-select}}
  </div>
  <div class="form-group">
    <label>Witnesses</label>
    {{#x-select value=model.sighting.witnesses multiple=true class="form-control"}}
      {{#x-option}}Select Witnesses{{/x-option}}
      {{#each model.witnesses as |witness|}}
        {{#x-option value=witness}}{{witness.fullName}}{{/x-option}}
      {{/each}}
    {{/x-select}}
  </div>
  <div class="form-group">
    <label for="location">Location</label> 
    {{input value=model.sighting.location type="text" class="form-control" name="location" required=true}}
  </div>
</form>

{{x-select}} 组件用于处理 <select> 元素的选择事件,对于证人列表,设置 multiple=true 允许用户选择多个证人。

2.4 添加导航链接

app/templates/sightings.hbs 文件中添加一个链接,以便用户可以导航到新目击记录页面:

<h1>Sightings</h1>
<div class="row">
  <div class="col-xs-6">
    <h1>Sightings</h1>
  </div>
  <div class="col-xs-6 h1">
    {{#link-to "sightings.new" class="pull-right btn btn-primary"}}
      New Sighting
    {{/link-to}}
  </div>
</div>
{{outlet}}

访问 http://localhost:4200/sightings 即可看到新的“New Sighting”按钮。

2.5 创建控制器和操作

使用以下命令创建 sightings.new 路由的控制器:

ember g controller sightings/new

打开 app/controllers/sightings/new.js 文件,添加 create cancel 操作:

import Ember from 'ember';
export default Ember.Controller.extend({
  actions: {
    create() {
    },
    cancel() {
    }
  }
});

app/templates/sightings/new.hbs 文件中,为表单元素设置提交操作,并添加“Create”和“Cancel”按钮:

<h1>New Sighting</h1>
<form {{action "create" on="submit"}}>
  ...
  <div class="form-group">
    <label for="location">Location</label> 
    {{input value=model.location type="text" class="form-control" name="location" required=true}}
  </div>
  <button type="submit" class="btn btn-primary btn-block">Create</button>
  <button {{action 'cancel'}} class="btn btn-link btn-block">Cancel</button>
</form>

更新 app/controllers/sightings/new.js 文件中的 create 操作,完成目击记录的保存并返回目击记录列表:

actions: {
  create() {
    var self = this;
    this.get('model.sighting').save().then(function() {
      self.transitionToRoute('sightings');
    });
  },
  cancel() {
  }
}

同时,更新 app/routes/sightings.js 文件,删除虚拟数据,使用 store.findAll 方法从数据源获取目击记录:

import Ember from 'ember';
export default Ember.Route.extend({
  model() {
    return this.store.findAll('sighting', {reload: true});
  }
});

cancel 操作添加删除未保存记录的功能:

actions: {
  create() {
    var self = this;
    this.get('model.sighting').save().then(function() {
      self.transitionToRoute('sightings');
    });
  },
  cancel() {
    this.get('model.sighting').deleteRecord();
    this.transitionToRoute('sightings');
  }
}

最后,打开 app/routes/sightings/new.js 文件,重写 willTransition 操作,确保在路由切换时删除未保存的脏记录:

model(){
  return Ember.RSVP.hash({
    sighting: this.store.createRecord('sighting'),
    cryptids: this.store.findAll('cryptid'),
    witnesses: this.store.findAll('witness')
  });
},
actions: {
  willTransition() {
    var sighting = this.get('controller.model.sighting');
    if(sighting.get('hasDirtyAttributes')){
      sighting.deleteRecord();
    }
  }
}
3. 编辑目击记录
3.1 添加编辑按钮

app/templates/sightings/index.hbs 文件中添加“Edit”按钮:

<div class="media well">
  <img class="media-object thumbnail" src="{{if sighting.cryptid.profileImg sighting.cryptid.profileImg 'assets/images/cryptids/blank_th.png'}}" alt="{{sighting.cryptid.name}}" width="100%" height="100%">
  <div class="caption">
    <h3>{{sighting.cryptid.name}}</h3>
    {{#if sighting.location}}
      <h3>{{sighting.location}}</h3>
      <p>{{moment-from sighting.sightedAt}}</p>
    {{else}}
      <h3 class="text-danger">Bogus Sighting</h3>
    {{/if}}
  </div>
  {{#link-to 'sighting.edit' sighting.id tagName="button" class="btn btn-success btn-block"}}
    Edit
  {{/link-to}}
</div>

访问 http://localhost:4200/sightings 即可看到新的“Edit”按钮。

3.2 设置编辑路由参数

router.js 文件中添加编辑路由的动态参数:

this.route('sighting', function() {
  this.route('edit', {path: "sightings/:sighting_id/edit"});
});
3.3 设置编辑路由模型

打开 app/routes/sighting/edit.js 文件,添加获取目击记录、生物和证人数据的方法:

export default Ember.Route.extend({
  model(params) {
    return Ember.RSVP.hash({
      sighting: this.store.findRecord('sighting', params.sighting_id),
      cryptids: this.store.findAll('cryptid'),
      witnesses: this.store.findAll('witness')
    });
  }
});
3.4 创建编辑表单

编辑 app/templates/sighting/edit.hbs 文件,添加编辑表单:

{{outlet}}
<h1>Edit Sighting:
  <small>
    {{model.sighting.location}} -
    {{moment-from model.sightin.sightedAt}}
  </small>
</h1>
<form {{action "update" model on="submit"}}>
  <div class="form-group">
    <label for="name">Cryptid</label>
    {{input value=model.sighting.cryptid.name type="text" class="form-control" name="location" disabled=true}}
  </div>
  <div class="form-group">
    <label>Witnesses</label>
    {{#each model.sighting.witnesses as |witness|}}
      {{input value=witness.fullName type="text" class="form-control" name="location" disabled=true}}
    {{/each}}
  </div>
  <div class="form-group">
    <label for="location">Location</label>
    {{input value=model.sighting.location type="text" class="form-control" name="location" required=true}}
  </div>
  <button type="submit" class="btn btn-info btn-block">Update</button>
  <button {{action 'cancel'}} class="btn btn-block">Cancel</button>
</form>
3.5 创建编辑控制器和操作

使用以下命令创建编辑路由的控制器:

ember g controller sighting/edit

打开 app/controllers/sighting/edit.js 文件,添加 update cancel 操作:

import Ember from 'ember';
export default Ember.Controller.extend({
  sighting: Ember.computed.alias('model.sighting'),
  actions: {
    update() {
      if(this.get('sighting').get('hasDirtyAttributes')){
        this.get('sighting').save().then(() => {
          this.transitionToRoute('sightings');
        });
      }
    },
    cancel() {
      if(this.get('sighting').get('hasDirtyAttributes')){
        this.get('sighting').rollbackAttributes();
      }
      this.transitionToRoute('sightings');
    }
  }
});
4. 删除目击记录
4.1 添加删除按钮

app/templates/sighting/edit.hbs 文件中添加“Delete”按钮:

<button type="submit" class="btn btn-info btn-block">Update</button>
<button {{action 'cancel'}} class="btn btn-block">Cancel</button>
</form>
<hr>
<button {{action 'delete'}} class="btn btn-block btn-danger">
  Delete
</button>
4.2 添加删除操作

打开 app/controllers/sighting/edit.js 文件,添加 delete 操作:

cancel() {
  if(this.get('sighting').get('hasDirtyAttributes')){
    this.get('sighting').rollbackAttributes();
  }
  this.transitionToRoute('sightings');
},
delete() {
  var self = this;
  if (window.confirm("Are you sure you want to delete this sighting?")) {
    this.get('sighting').destroyRecord().then(() => {
      self.transitionToRoute('sightings');
    });
  }
}

访问 http://localhost:4200/sightings ,点击“Edit”按钮,即可看到新的“Delete”按钮。至此,新目击记录的创建、编辑和删除功能全部完成。

流程图

graph LR
    A[开始] --> B[创建新目击记录]
    B --> C[设置路由模型]
    C --> D[安装插件]
    D --> E[创建新目击记录表单]
    E --> F[添加导航链接]
    F --> G[创建控制器和操作]
    G --> H[编辑目击记录]
    H --> I[添加编辑按钮]
    I --> J[设置编辑路由参数]
    J --> K[设置编辑路由模型]
    K --> L[创建编辑表单]
    L --> M[创建编辑控制器和操作]
    M --> N[删除目击记录]
    N --> O[添加删除按钮]
    O --> P[添加删除操作]
    P --> Q[结束]

总结

通过以上步骤,我们完成了 Ember.js 应用中创建、编辑和删除目击记录的功能。整个过程涉及路由模型的设置、插件的安装、表单的创建、控制器和操作的定义等多个方面。在实际开发中,需要注意脏记录的处理,确保数据的一致性和完整性。同时,合理使用 Ember.js 提供的工具和方法,可以提高开发效率和代码质量。

详细步骤总结表格

操作类型 具体步骤 操作文件 代码示例
创建新目击记录 设置路由模型 app/routes/sightings/new.js javascript<br>export default Ember.Route.extend({<br> model() {<br> return Ember.RSVP.hash({<br> sighting: this.store.createRecord('sighting'),<br> cryptids: this.store.findAll('cryptid'),<br> witnesses: this.store.findAll('witness')<br> });<br> }<br>});<br>
创建新目击记录 安装插件 命令行 bash<br>ember install emberx-select<br>
创建新目击记录 创建新目击记录表单 app/templates/sightings/new.hbs handlebars<br><h1>New Sighting</h1><br><form><br> <div class="form-group"><br> <label for="name">Cryptid</label><br> {{#x-select value=model.sighting.cryptid class="form-control"}}<br> {{#x-option}}Select Cryptid{{/x-option}}<br> {{#each model.cryptids as |cryptid|}}<br> {{#x-option value=cryptid}}{{cryptid.name}}{{/x-option}}<br> {{/each}}<br> {{/x-select}}<br> </div><br> <div class="form-group"><br> <label>Witnesses</label><br> {{#x-select value=model.sighting.witnesses multiple=true class="form-control"}}<br> {{#x-option}}Select Witnesses{{/x-option}}<br> {{#each model.witnesses as |witness|}}<br> {{#x-option value=witness}}{{witness.fullName}}{{/x-option}}<br> {{/each}}<br> {{/x-select}}<br> </div><br> <div class="form-group"><br> <label for="location">Location</label> <br> {{input value=model.sighting.location type="text" class="form-control" name="location" required=true}}<br> </div><br></form><br>
创建新目击记录 添加导航链接 app/templates/sightings.hbs handlebars<br><h1>Sightings</h1><br><div class="row"><br> <div class="col-xs-6"><br> <h1>Sightings</h1><br> </div><br> <div class="col-xs-6 h1"><br> {{#link-to "sightings.new" class="pull-right btn btn-primary"}}<br> New Sighting<br> {{/link-to}}<br> </div><br></div><br>{{outlet}}<br>
创建新目击记录 创建控制器和操作 app/controllers/sightings/new.js javascript<br>import Ember from 'ember';<br>export default Ember.Controller.extend({<br> actions: {<br> create() {<br> var self = this;<br> this.get('model.sighting').save().then(function() {<br> self.transitionToRoute('sightings');<br> });<br> },<br> cancel() {<br> this.get('model.sighting').deleteRecord();<br> this.transitionToRoute('sightings');<br> }<br> }<br>});<br>
编辑目击记录 添加编辑按钮 app/templates/sightings/index.hbs handlebars<br><div class="media well"><br> <img class="media-object thumbnail" src="{{if sighting.cryptid.profileImg sighting.cryptid.profileImg 'assets/images/cryptids/blank_th.png'}}" alt="{{sighting.cryptid.name}}" width="100%" height="100%"> <br> <div class="caption"><br> <h3>{{sighting.cryptid.name}}</h3><br> {{#if sighting.location}}<br> <h3>{{sighting.location}}</h3><br> <p>{{moment-from sighting.sightedAt}}</p><br> {{else}}<br> <h3 class="text-danger">Bogus Sighting</h3><br> {{/if}}<br> </div><br> {{#link-to 'sighting.edit' sighting.id tagName="button" class="btn btn-success btn-block"}}<br> Edit<br> {{/link-to}}<br></div><br>
编辑目击记录 设置编辑路由参数 router.js javascript<br>this.route('sighting', function() {<br> this.route('edit', {path: "sightings/:sighting_id/edit"});<br>});<br>
编辑目击记录 设置编辑路由模型 app/routes/sighting/edit.js javascript<br>export default Ember.Route.extend({<br> model(params) {<br> return Ember.RSVP.hash({<br> sighting: this.store.findRecord('sighting', params.sighting_id),<br> cryptids: this.store.findAll('cryptid'),<br> witnesses: this.store.findAll('witness')<br> });<br> }<br>});<br>
编辑目击记录 创建编辑表单 app/templates/sighting/edit.hbs handlebars<br>{{outlet}}<br><h1>Edit Sighting:<br> <small><br> {{model.sighting.location}} -<br> {{moment-from model.sightin.sightedAt}}<br> </small><br></h1><br><form {{action "update" model on="submit"}}><br> <div class="form-group"><br> <label for="name">Cryptid</label><br> {{input value=model.sighting.cryptid.name type="text" class="form-control" name="location" disabled=true}}<br> </div><br> <div class="form-group"><br> <label>Witnesses</label><br> {{#each model.sighting.witnesses as |witness|}}<br> {{input value=witness.fullName type="text" class="form-control" name="location" disabled=true}}<br> {{/each}}<br> </div><br> <div class="form-group"><br> <label for="location">Location</label><br> {{input value=model.sighting.location type="text" class="form-control" name="location" required=true}}<br> </div><br> <button type="submit" class="btn btn-info btn-block">Update</button><br> <button {{action 'cancel'}} class="btn btn-block">Cancel</button><br></form><br>
编辑目击记录 创建编辑控制器和操作 app/controllers/sighting/edit.js javascript<br>import Ember from 'ember';<br>export default Ember.Controller.extend({<br> sighting: Ember.computed.alias('model.sighting'),<br> actions: {<br> update() {<br> if(this.get('sighting').get('hasDirtyAttributes')){<br> this.get('sighting').save().then(() => {<br> this.transitionToRoute('sightings');<br> });<br> }<br> },<br> cancel() {<br> if(this.get('sighting').get('hasDirtyAttributes')){<br> this.get('sighting').rollbackAttributes();<br> }<br> this.transitionToRoute('sightings');<br> }<br> }<br>});<br>
删除目击记录 添加删除按钮 app/templates/sighting/edit.hbs handlebars<br><button type="submit" class="btn btn-info btn-block">Update</button><br><button {{action 'cancel'}} class="btn btn-block">Cancel</button><br></form><br><hr><br><button {{action 'delete'}} class="btn btn-block btn-danger"><br> Delete<br></button><br>
删除目击记录 添加删除操作 app/controllers/sighting/edit.js javascript<br>cancel() {<br> if(this.get('sighting').get('hasDirtyAttributes')){<br> this.get('sighting').rollbackAttributes();<br> }<br> this.transitionToRoute('sightings');<br>},<br>delete() {<br> var self = this;<br> if (window.confirm("Are you sure you want to delete this sighting?")) {<br> this.get('sighting').destroyRecord().then(() => {<br> self.transitionToRoute('sightings');<br> });<br> }<br>}<br>

关键技术点分析

4.1 Ember.RSVP.hash 的使用

在路由的 model 钩子中, Ember.RSVP.hash 用于将多个 Promise 组合成一个,这样可以同时处理多个异步操作。例如,在创建新目击记录和编辑目击记录的路由中,我们使用它来同时获取目击记录、生物和证人的数据:

// 创建新目击记录路由
export default Ember.Route.extend({
  model() {
    return Ember.RSVP.hash({
      sighting: this.store.createRecord('sighting'),
      cryptids: this.store.findAll('cryptid'),
      witnesses: this.store.findAll('witness')
    });
  }
});

// 编辑目击记录路由
export default Ember.Route.extend({
  model(params) {
    return Ember.RSVP.hash({
      sighting: this.store.findRecord('sighting', params.sighting_id),
      cryptids: this.store.findAll('cryptid'),
      witnesses: this.store.findAll('witness')
    });
  }
});

这样做的好处是可以确保所有数据都准备好后再渲染模板,避免出现数据不完整的情况。

4.2 控制器操作的处理

控制器中的操作(如 create update cancel delete )是处理表单事件的关键。这些操作通过 {{action}} 助手绑定到模板中的按钮或表单元素上。例如:

<form {{action "create" on="submit"}}>
  ...
  <button type="submit" class="btn btn-primary btn-block">Create</button>
  <button {{action 'cancel'}} class="btn btn-link btn-block">Cancel</button>
</form>

在控制器中,这些操作包含了具体的业务逻辑,如保存记录、删除记录、回滚属性等:

actions: {
  create() {
    var self = this;
    this.get('model.sighting').save().then(function() {
      self.transitionToRoute('sightings');
    });
  },
  cancel() {
    this.get('model.sighting').deleteRecord();
    this.transitionToRoute('sightings');
  },
  update() {
    if(this.get('sighting').get('hasDirtyAttributes')){
      this.get('sighting').save().then(() => {
        this.transitionToRoute('sightings');
      });
    }
  },
  delete() {
    var self = this;
    if (window.confirm("Are you sure you want to delete this sighting?")) {
      this.get('sighting').destroyRecord().then(() => {
        self.transitionToRoute('sightings');
      });
    }
  }
}
4.3 脏记录的处理

脏记录是指已经被修改但还未保存到持久化数据源的记录。在创建和编辑目击记录时,需要处理脏记录,避免数据不一致。例如,在路由的 willTransition 操作中,检查记录是否有脏属性,如果有则删除记录:

actions: {
  willTransition() {
    var sighting = this.get('controller.model.sighting');
    if(sighting.get('hasDirtyAttributes')){
      sighting.deleteRecord();
    }
  }
}

在取消编辑操作时,使用 rollbackAttributes 方法回滚记录的属性:

cancel() {
  if(this.get('sighting').get('hasDirtyAttributes')){
    this.get('sighting').rollbackAttributes();
  }
  this.transitionToRoute('sightings');
}

总结与建议

5.1 总结

通过以上的步骤和技术点分析,我们成功实现了 Ember.js 应用中目击记录的创建、编辑和删除功能。整个过程涵盖了路由模型的设置、插件的使用、表单的创建、控制器操作的定义以及脏记录的处理等多个方面。合理使用 Ember.js 提供的工具和方法,可以提高开发效率和代码质量。

5.2 建议
  • 代码复用 :在实际开发中,可以将一些通用的代码提取成组件或服务,提高代码的复用性。例如,将 x-select 组件的使用封装成一个通用的选择组件,减少代码的重复编写。
  • 错误处理 :在异步操作中,如保存记录和删除记录,应该添加错误处理逻辑,以提高应用的健壮性。例如,在 save destroyRecord then 方法中添加 catch 方法,处理可能出现的错误。
  • 用户体验 :可以进一步优化用户界面和交互,例如添加加载提示、错误提示等,提高用户体验。例如,在保存记录时显示加载动画,保存成功或失败后给出相应的提示信息。

流程图(操作流程)

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B(创建新目击记录):::process
    B --> C{是否需要安装插件?}:::decision
    C -- 是 --> D(安装 emberx - select 插件):::process
    C -- 否 --> E(设置路由模型):::process
    D --> E
    E --> F(创建新目击记录表单):::process
    F --> G(添加导航链接):::process
    G --> H(创建控制器和操作):::process
    H --> I(编辑目击记录):::process
    I --> J(添加编辑按钮):::process
    J --> K(设置编辑路由参数):::process
    K --> L(设置编辑路由模型):::process
    L --> M(创建编辑表单):::process
    M --> N(创建编辑控制器和操作):::process
    N --> O(删除目击记录):::process
    O --> P(添加删除按钮):::process
    P --> Q(添加删除操作):::process
    Q --> R([结束]):::startend

通过以上的总结和分析,我们可以更好地理解和掌握 Ember.js 中控制器的开发,以及如何实现 CRUD 功能。在实际开发中,我们可以根据具体需求对代码进行优化和扩展,提高应用的性能和用户体验。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值