Note that there are some explanatory texts on larger screens.

plurals
  1. POError saving new record with Ember.js: "Uncaught TypeError: Cannot call method 'lookup' of undefined"
    primarykey
    data
    text
    <p>I'm working on an initial "learning" Ember project which will ultimately evolve into a full application backed by a REST JSON service. For now, I'm just attempting to create a simple CRUD style set of pages for managing a list of companies. For now it's using LocalStorage until it works, then I intend to attach a REST adapter.</p> <p>After banging my head on this for three days I am coming to SO for help, please.</p> <p>The code included below (single HTML page style for ease of sharing) seems to work for listing, adding, and deleting company records, both with the LocalStorage adapter and the Fixture adapter. I am having two problems though, one of which is killing me.</p> <ol> <li><p>When I click the "Update Company" button in the company detail page (path /companies/:company_id, controller App.CompaniesEditController, route App.CompaniesEditRoute), it gives me an error "Uncaught TypeError: Cannot call method 'lookup' of undefined" that I can't figure out where it's coming from or why it's not saving. It also appears to be coming in asynchronously. The final "transitionToRoute" is also not being executed. I figure I'm setting up something incorrectly on the router, controller, or model (or maybe even the template), but I can't figure it out. This is the more urgent problem, please.</p></li> <li><p>(Lower priority) When data (the company name) is edited on the company detail page, but not saved, it evidently is still updating the data stored in the application (i.e., the Javascript state, not the persistent state). So, if I edit a company name from "abc" to "def" on that edit page, then just return to the company list, the name is updated in the company list. However, if I reload the page (forcing a reload of data from the local storage), the data is restored. How can I make a view/edit page that doesn't change anything if the "save" button is not pressed, please?</p></li> </ol> <p>Thanks for reading. Full code follows.</p> <pre class="lang-html prettyprint-override"><code>&lt;html lang="en"&gt; &lt;head&gt; &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"&gt; &lt;!-- See also: http://stackoverflow.com/questions/13588523/setting-page-title-with-ember-js --&gt; &lt;title&gt;Company Test&lt;/title&gt; &lt;script src="handlebars-1.0.0.js"&gt;&lt;/script&gt; &lt;script src="jquery-1.10.2.js"&gt;&lt;/script&gt; &lt;!-- &lt;script src="jquery-2.0.3.js"&gt;&lt;/script&gt; --&gt; &lt;script src="ember-1.0.0.js"&gt;&lt;/script&gt; &lt;script src="ember-data-1.0.0-beta.2.js"&gt;&lt;/script&gt; &lt;!-- Local storage adapter from: https://github.com/rpflorence/ember-localstorage-adapter --&gt; &lt;script src="localstorage_adapter-20130906.js"&gt;&lt;/script&gt; &lt;script&gt; // 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('&lt;type&gt;')` 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." } ]; &lt;/script&gt; &lt;!-- The main screen layout. It should be called "application". --&gt; &lt;script type="text/x-handlebars" data-template-name='application'&gt; &lt;header&gt;&lt;font color="red"&gt;Shared page header&lt;/font&gt;&lt;/header&gt; &lt;div&gt; {{outlet}} &lt;/div&gt; &lt;footer&gt;&lt;font color="red"&gt;Copyright &amp;copy; Somewhen Somewho (shared page footer)&lt;/font&gt;&lt;/footer&gt; &lt;/script&gt; &lt;!-- 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 --&gt; &lt;!-- This page shows the log in or create account --&gt; &lt;script type="text/x-handlebars" data-template-name='index'&gt; &lt;p&gt;{{#linkTo 'companies'}}View companies{{/linkTo}}&lt;/p&gt; &lt;/script&gt; &lt;!-- 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. --&gt; &lt;script type="text/x-handlebars" data-template-name='companies'&gt; {{outlet}} &lt;/script&gt; &lt;!-- 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. --&gt; &lt;script type="text/x-handlebars" data-template-name='companies/index'&gt; &lt;h1&gt;Company List&lt;/h1&gt; &lt;p&gt;{{#linkTo 'companies.new'}}Create a company{{/linkTo}}&lt;/p&gt; &lt;ul&gt; {{#each}} &lt;li&gt; &lt;label&gt;{{#linkTo 'companies.edit' this}}{{name}}{{/linkTo}}&lt;/label&gt; &lt;button {{action "deleteCompany" this}}&gt;delete&lt;/button&gt; &lt;/li&gt; {{/each}} &lt;/ul&gt; &lt;/script&gt; &lt;!-- Shared content for both new/edit company --&gt; &lt;script type="text/x-handlebars" data-template-name='_company-form'&gt; &lt;p&gt;{{#linkTo 'companies'}}Return to Company List{{/linkTo}} &lt;hr/&gt; &lt;form&gt; &lt;p&gt; {{#if id}}Company ID: {{id}}&lt;br/&gt;{{/if}} Company Name: {{input type="text" id="new-name" placeholder="New Co." value=name}} &lt;/p&gt; &lt;/form&gt; &lt;/script&gt; &lt;!-- 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). --&gt; &lt;script type="text/x-handlebars" data-template-name='companies/new'&gt; &lt;h1&gt;Add Company&lt;/h1&gt; {{partial "company-form"}} &lt;p&gt;&lt;button {{action createCompany this}}&gt;Add Company&lt;/button&gt;&lt;/p&gt; &lt;/script&gt; &lt;script type="text/x-handlebars" data-template-name='companies/edit'&gt; &lt;h1&gt;Edit Company&lt;/h1&gt; {{partial "company-form"}} &lt;!-- 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? --&gt; &lt;p&gt;&lt;button {{action updateCompany this}}&gt;Update Company&lt;/button&gt;&lt;/p&gt; &lt;/script&gt; &lt;/head&gt; &lt;body bgcolor="#ffffff"&gt;&lt;/body&gt; &lt;/html&gt; &lt;body bgcolor="#ffffff"&gt;&lt;/body&gt; &lt;/html&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload