私は初期の「学習」Ember プロジェクトに取り組んでおり、最終的には REST JSON サービスに支えられた完全なアプリケーションに発展します。今のところ、会社のリストを管理するための単純な CRUD スタイルのページ セットを作成しようとしています。今のところ、動作するまで LocalStorage を使用しています。その後、REST アダプターを接続する予定です。
これに3日間頭をぶつけた後、SOに助けを求めに来ます。
以下に含まれるコード (共有を容易にするための単一の HTML ページ スタイル) は、LocalStorage アダプターと Fixture アダプターの両方を使用して、企業レコードの一覧表示、追加、および削除に機能するようです。私は2つの問題を抱えていますが、そのうちの1つは私を殺しています。
会社の詳細ページ (パス /companies/:company_id、コントローラー App.CompaniesEditController、ルート App.CompaniesEditRoute) で [会社の更新] ボタンをクリックすると、「Uncaught TypeError: Cannot call method 'lookup' of undefined」というエラーが表示されますどこから来ているのか、なぜ保存されていないのかわかりません。また、非同期で入ってくるようです。最後の「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>