使用Ember.js保存新记录时出错:“Uncaught TypeError:无法调用未定义的方法'查找'”

时间:2013-09-08 16:31:58

标签: ember.js

我正在开发一个最初的“学习”Ember项目,该项目最终将演变为由REST JSON服务支持的完整应用程序。现在,我只是想创建一个简单的CRUD样式页面来管理公司列表。现在它使用LocalStorage直到它工作,然后我打算附加一个REST适配器。

在我的头上敲了三天之后,我来找SO求助。

下面的代码(单个HTML页面样式易于共享)似乎适用于列出,添加和删除公司记录,包括LocalStorage适配器和Fixture适配器。我有两个问题,其中一个是杀了我。

  1. 当我点击公司详细信息页面中的“更新公司”按钮(路径/公司/:company_id,控制器App.CompaniesEditController,路由App.CompaniesEditRoute)时,它给出了一个错误“未捕获的TypeError:无法调用方法'查找'未定义“我无法弄清楚它来自何处或为什么它不能保存。它似乎也异步进入。最终的“transitionToRoute”也没有被执行。我想我在路由器,控制器或模型(或者甚至模板)上设置错误,但我无法弄明白。这是更迫切的问题。

  2. (优先级较低)当在公司详细信息页面上编辑数据(公司名称)但未保存时,显然仍在更新应用程序中存储的数据(即Javascript状态,而不是持久性州)。因此,如果我在该编辑页面上将公司名称从“abc”编辑为“def”,则只需返回公司列表,该名称将在公司列表中更新。但是,如果我重新加载页面(强制从本地存储重新加载数据),则会恢复数据。如果没有按下“保存”按钮,如何制作一个不会改变任何内容的视图/编辑页面?

  3. 感谢阅读。完整代码如下。

    <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 &copy; 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>
    

2 个答案:

答案 0 :(得分:0)

对于第二个问题(没有自动更新视图),我找到了这个引用:http://www.solitr.com/blog/2012/06/ember-input-field-with-save-button/。我还没有尝试过,但很快就会尝试。

答案 1 :(得分:0)

我遇到了同样的问题,Joe的拉动请求我认为会修复它,它与序列化器的引入有关。

我的猜测是,如果你调用record.toJSON(),你会得到你提到的错误。一个简单的 解决方法只是覆盖模型上的toJSON函数。

相关问题