【发布时间】:2013-09-08 16:31:58
【问题描述】:
我正在从事最初的“学习”Ember 项目,该项目最终将演变成一个由 REST JSON 服务支持的完整应用程序。现在,我只是尝试创建一个简单的 CRUD 样式的页面集来管理公司列表。现在它使用 LocalStorage 直到它工作,然后我打算附加一个 REST 适配器。
在这个问题上纠结了三天后,我要来 SO 寻求帮助。
下面包含的代码(便于共享的单一 HTML 页面样式)似乎适用于使用 LocalStorage 适配器和 Fixture 适配器列出、添加和删除公司记录。不过,我有两个问题,其中一个让我很生气。
当我在公司详细信息页面(路径 /companies/:company_id、控制器 App.CompaniesEditController、路由 App.CompaniesEditRoute)中单击“更新公司”按钮时,它给我一个错误“未捕获的类型错误:无法调用方法'查找'未定义“我无法弄清楚它来自哪里或为什么它不保存。它似乎也是异步进入的。最终的“transitionToRoute”也没有被执行。我想我在路由器、控制器或模型(甚至可能是模板)上设置了不正确的东西,但我无法弄清楚。这是更紧迫的问题,拜托。
(低优先级)当数据(公司名称)在公司详情页面上被编辑,但没有保存时,它显然仍在更新存储在应用程序中的数据(即Javascript状态,而不是持久的)状态)。因此,如果我在该编辑页面上将公司名称从“abc”编辑为“def”,然后只需返回公司列表,该名称就会在公司列表中更新。但是,如果我重新加载页面(强制从本地存储重新加载数据),数据就会恢复。如果没有按下“保存”按钮,如何制作一个不会改变任何内容的查看/编辑页面?
感谢阅读。完整代码如下。
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<!-- See also: http://stackoverflow.com/questions/13588523/setting-page-title-with-ember-js -->
<title>Company Test</title>
<script src="handlebars-1.0.0.js"></script>
<script src="jquery-1.10.2.js"></script>
<!-- <script src="jquery-2.0.3.js"></script> -->
<script src="ember-1.0.0.js"></script>
<script src="ember-data-1.0.0-beta.2.js"></script>
<!-- Local storage adapter from: https://github.com/rpflorence/ember-localstorage-adapter -->
<script src="localstorage_adapter-20130906.js"></script>
<script>
// App is the name of our ember application in JavaScript
App = Ember.Application.create( { LOG_TRANSITIONS: true } );
// Our application's route map.
App.Router.map(function() {
this.resource('companies', { path: "/companies" }, function() {
// This page has three versions, a list, a new company and edit an existing company.
// The rendering goes through "companies" which should have an outlet for these
// two sub-pages and the main page (companies/index).
this.route('new', { path: "/new" });
this.route('edit', { path: "/:company_id" });
});
});
///////////////////////////////////////////////////////////////////////////////////////
// ROUTES
App.CompaniesRoute = Ember.Route.extend({
// Tell what data is available to this route
model: function() {
return this.store.find('company');
}
});
App.CompaniesIndexRoute = Ember.Route.extend({
model: function() {
// Reuse the parent route's model: http://stackoverflow.com/questions/14609757/emberjs-1-0-0pre4-how-do-you-pass-a-context-object-to-a-resource-index-rout/14610816#14610816
return this.modelFor('companies');
}
});
App.CompaniesEditRoute = Ember.Route.extend({
// This route's model has just the one specified company
model: function(co) {
console.log("Companies.Edit Route invoked with argument:");
console.log(co);
// This is company_id because that is what is specified in the route
var found = this.store.find('company', co.company_id);
console.log("Companies.Edit model is:");
console.log(found);
return found;
}
});
//////////////////////////////////////////////////////////////////////////////
// CONTROLLERS
// From the index (main) page of the companies list, you can
// add, edit and delete. Only delete has an action. The others
// just go to different routes.
App.CompaniesIndexController = Ember.ArrayController.extend({
actions: {
// Deletes the specified company
deleteCompany: function (co) {
co.deleteRecord();
co.save();
}
}
});
// The new page shows some empty fields and when add is selected,
// takes those and creates a new company from them. In the future,
// if there is an error saving, stay on the page and show a message.
// When added, clear the page for future use and go back to the
// company list. We might want to pass a message to say "added company
// so-and-so" at that point too.
App.CompaniesNewController = Ember.ArrayController.extend({
actions: {
createCompany: function () {
// Get value of name input box
var newName = this.get('name');
// Ignore blank companies
if (!newName.trim()) { return; }
// Create the new model entry
var newCo = this.store.createRecord('company', {
name: newName
});
// Clear name for next company to add
this.set('name', '');
// Save the new company to the model's backing store
newCo.save();
// And move to the list page
// TODO: Move this to the Router Events Hash (whatever that means)
this.transitionToRoute('companies');
}
}
});
// This page edits a company and saves it to the persistent store.
// TODO: How to make it not change the model object directly when
// the form is changed, but not saved?
App.CompaniesEditController = Ember.ObjectController.extend({
actions: {
updateCompany: function () {
// No idea why I can't save the company. Get this error:
// Uncaught TypeError: Cannot call method 'lookup' of undefined
/*
var content = this.get('content');
console.log("Content is:");
console.log(content);
console.log(content.data);
*/
var co = this.get('model');
console.log("Model is:");
console.log(co);
console.log(co.get('data'));
console.log("About to save...");
co.save();
console.log("Done with save...");
// Go back to the company list
this.transitionToRoute('companies');
}
}
});
//////////////////////////////////////////////////////////////////////
// EMBER DATA
// Set up Ember Data
// And Local Storage Adapter - https://github.com/rpflorence/ember-localstorage-adapter
App.Store = DS.Store.extend({
revision: 11, // Not sure what this means or is here for
adapter: DS.LSAdapter
});
// http://emberjs.com/guides/getting-started/using-other-adapters/
App.ApplicationAdapter = DS.LSAdapter.extend({
namespace: 'companies-demo'
});
// We're using the LocalStorage Adapter now, not Fixture Adapter
// App.ApplicationAdapter = DS.FixtureAdapter.extend();
//////////////////////////////////////////////////////////////////////////////////
// MODEL OBJECT TEMPLATES
App.Company = DS.Model.extend({
// I get an error if I use the below:
// Assertion failed: You may not set `id` as an attribute on your model. Please remove any lines that look like:
// `id: DS.attr('<type>')` from App.Company
//id: DS.attr('number'),
name: DS.attr('string')
});
// TEST DATA (When not using Local Storage Adapter)
App.Company.FIXTURES = [
{ id: 11, name: "Apple, Inc." },
{ id: 12, name: "Netflix, Inc." },
{ id: 13, name: "Facebook, Inc." },
{ id: 14, name: "Google, Inc." },
{ id: 15, name: "Microsoft, Inc." }
];
</script>
<!-- The main screen layout. It should be called "application". -->
<script type="text/x-handlebars" data-template-name='application'>
<header><font color="red">Shared page header</font></header>
<div>
{{outlet}}
</div>
<footer><font color="red">Copyright © Somewhen Somewho (shared page footer)</font></footer>
</script>
<!-- See this for how to set the page title (which I would like to do):
http://stackoverflow.com/questions/13588523/setting-page-title-with-ember-js -->
<!-- This page shows the log in or create account -->
<script type="text/x-handlebars" data-template-name='index'>
<p>{{#linkTo 'companies'}}View companies{{/linkTo}}</p>
</script>
<!-- This is an empty placeholder that will have the sub-pages
for index (the main company list), new and a single company
rendered into it. -->
<script type="text/x-handlebars" data-template-name='companies'>
{{outlet}}
</script>
<!-- This lists all companies and lets one be clicked to get the view/edit.
Future: Search box to show a list of companies with that in the name. -->
<script type="text/x-handlebars" data-template-name='companies/index'>
<h1>Company List</h1>
<p>{{#linkTo 'companies.new'}}Create a company{{/linkTo}}</p>
<ul>
{{#each}}
<li>
<label>{{#linkTo 'companies.edit' this}}{{name}}{{/linkTo}}</label>
<button {{action "deleteCompany" this}}>delete</button>
</li>
{{/each}}
</ul>
</script>
<!-- Shared content for both new/edit company -->
<script type="text/x-handlebars" data-template-name='_company-form'>
<p>{{#linkTo 'companies'}}Return to Company List{{/linkTo}}
<hr/>
<form>
<p>
{{#if id}}Company ID: {{id}}<br/>{{/if}}
Company Name: {{input type="text" id="new-name" placeholder="New Co." value=name}}
</p>
</form>
</script>
<!-- This views, adds or edits a company, depending on if the company has an ID or not
and whether the edit flag is set. VAE = VIEW ADD EDIT
Future: A DELETE button to delete a company and all its associated data (except
we don't actually delete it, we just mark it as deleted with a timestamp). -->
<script type="text/x-handlebars" data-template-name='companies/new'>
<h1>Add Company</h1>
{{partial "company-form"}}
<p><button {{action createCompany this}}>Add Company</button></p>
</script>
<script type="text/x-handlebars" data-template-name='companies/edit'>
<h1>Edit Company</h1>
{{partial "company-form"}}
<!-- FIXME: TODO: If you edit a company here and change its name, but
do NOT hit the "Update" button, it still shows the changed data
in the list and the edit page, but does NOT save it if you quit
the browser (or even reload the page). I wonder how we get it to restore the original data
if it is not saved? -->
<p><button {{action updateCompany this}}>Update Company</button></p>
</script>
</head>
<body bgcolor="#ffffff"></body>
</html>
<body bgcolor="#ffffff"></body>
</html>
【问题讨论】:
-
我也遇到了您在 #1 中提到的问题。到目前为止,我已经追踪到 JSONSerializer 的 transformFor 中未定义“this.container”。就我而言,我正在尝试查找 DS.attr("string") 的转换。保存后我也在做一个transitionToRoute,但这可能是一个红鲱鱼(我实际上是在“.then”中转换它)。反正以后继续挖,就是想发帖看看大家有没有什么想法。
-
我有一个拉取请求来解决这个问题。我们会看看会发生什么。 github.com/emberjs/data/pull/1261
标签: ember.js