0

我目前正在尝试使用backbone.js,我认为最好的方法是进入在线教程和文档。在线教程和示例应用程序非常好,但为了通过知识构建,我正在尝试构建我自己的示例网站 CRUD 应用程序。对于示例,基本上我想要做的是合并两个当前的在线示例/教程。试图更好地理解使用多个模型、集合和视图。

不幸的是我被卡住了......我为冗长的解释道歉,但作为一个新手,我试图尽可能地解释这个问题......

我的网站应用程序示例基于以下教程:

https://github.com/ccoenraets/backbone-cellar/tree/master/bootstrap

查看在线示例:

http://coenraets.org/backbone-cellar/bootstrap/

我能够遵循本教程并拥有该站点的工作版本。现在我希望扩展应用程序以包含更多适合应用程序(backbone.js)结构的页面。如果您查看教程,您会注意到有一个“关于”页面,它只是将静态 html 模板加载到应用程序中。我想做的是添加一个显示联系人管理器的新页面。联系人管理器被剥夺了以下教程:

http://net.tutsplus.com/tutorials/javascript-ajax/build-a-contacts-manager-using-backbone-js-part-1/

请注意:为简单起见,此时我仅使用本教程的第 1 部分。

好的,现在解释一下我遇到的问题......首先,我将概述我所做的事情。在应用程序上,我在 headerView 中添加了一个名为 Directory 的新链接。在 main.js 页面(原始示例:https ://github.com/ccoenraets/backbone-cellar/blob/master/bootstrap/js/main.js )我添加了如下代码:

var AppRouter = Backbone.Router.extend({

routes: {
    ""                  : "list",
    "wines/page/:page"  : "list",
    "wines/add"         : "addWine",
    "wines/:id"         : "wineDetails",
    "about"             : "about",
    "directory"         : "directory"
},

initialize: function () {
    this.headerView = new HeaderView();
    $('.header').html(this.headerView.el);
},

list: function(page) {
    var p = page ? parseInt(page, 10) : 1;
    var wineList = new WineCollection();
    wineList.fetch({success: function(){
        $("#content").html(new WineListView({model: wineList, page: p}).el);
    }});
    this.headerView.selectMenuItem('home-menu');
},

wineDetails: function (id) {
    var wine = new Wine({id: id});
    wine.fetch({success: function(){
        $("#content").html(new WineView({model: wine}).el);
    }});
    this.headerView.selectMenuItem();
},

addWine: function() {
    var wine = new Wine();
    $('#content').html(new WineView({model: wine}).el);
    this.headerView.selectMenuItem('add-menu');
},

about: function () {
    if (!this.aboutView) {
        this.aboutView = new AboutView();
    }
    $('#content').html(this.aboutView.el);
    this.headerView.selectMenuItem('about-menu');
},

directory: function () {
    if (!this.directoryView) {
        this.directorytView = new DirectoryView();
    }
    $('#content').html(this.directoryView.el);
    this.headerView.selectMenuItem('directory-menu');
}

});

utils.loadTemplate(['HeaderView', 'WineView', 'WineListItemView', 'AboutView', 'DirectoryView'], function() { app = new AppRouter(); Backbone.history.start(); });

现在对于目录(联系人管理器)页面,为了解释起见,我按照教程将模型视图和集合留在了单个 .js 文件中 - 我当然希望将文件分开(分成模型和视图) 一旦我让它工作。根据教程,联系人管理器(目录)的代码如下:

//demo data
window.contacts = [
    { name: "Contact 1", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "family" },
    { name: "Contact 2", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "family" },
    { name: "Contact 3", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "friend" },
    { name: "Contact 4", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "colleague" },
    { name: "Contact 5", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "family" },
    { name: "Contact 6", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "colleague" },
    { name: "Contact 7", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "friend" },
    { name: "Contact 8", address: "1, a street, a town, a city, AB12 3CD", tel: "0123456789", email: "anemail@me.com", type: "family" }
];

//define product model
window.Contact = Backbone.Model.extend({
    defaults: {
        photo: "/img/placeholder.png"
    }
});

//define directory collection
window.Directory = Backbone.Collection.extend({
    model: Contact
});

//define individual contact view
window.ContactView = Backbone.View.extend({
    tagName: "article",
    className: "contact-container",
    template: $("#contactTemplate").html(),

    render: function () {
        var tmpl = _.template(this.template);

        $(this.el).html(tmpl(this.model.toJSON()));
        //alert('this model: ' + this.model.toJSON().name);

        return this;
    }
});

//define master view
window.DirectoryView = Backbone.View.extend({
    el: $("#contacts"),

    initialize: function () {
        this.collection = new Directory(contacts);
        this.render();
    },

    render: function () {
        var that = this;
        _.each(this.collection.models, function (item) {
            that.renderContact(item);
        }, this);
    },

    renderContact: function (item) {
        var contactView = new ContactView({
            model: item
        });         

        this.$el.append(contactView.render().el);
    }
});

我所做的更改只是删除“var”并替换为“window”。以适应应用程序的现有结构。例如:

var DirectoryView = Backbone.View.extend({

变成:

window.DirectoryView = Backbone.View.extend({

现在到我遇到的问题。我能够获取代码以输出(呈现)html 代码以显示模板。

我相信问题在于

//define individual contact view
window.ContactView = Backbone.View.extend({
    tagName: "article",
    className: "contact-container",
    template: $("#contactTemplate").html(),

    render: function () {
        var tmpl = _.template(this.template);

    $(this.el).html(tmpl(this.model.toJSON()));
    alert('this model: ' + this.model.toJSON().name);

    return this;
    }
});

现在我知道数据正在被正确解析,因为“警报”正在正确输出名称。我遇到的问题是以下代码行:

var tmpl = _.template(this.template);

正在引发以下错误:“未捕获的 TypeError:无法调用 null 的方法 'replace'”。

我对如何解决这个问题一无所知:(

DirectoryView.html 模板代码是:

<div class="row">
<div class="span12">
    <div id="contact"></div>
    <script id="contactTemplate" type="text/template">
        <img src="<%= photo %>" alt="<%= name %>" />
        <h1><%= name %><span><%= type %></span></h1>
        <div><%= address %></div>
        <dl>
            <dt>Tel:</dt><dd><%= tel %></dd>
            <dt>Email:</dt><dd><a href="mailto:<%= email %>"><%= email %></a></dd>
        </dl>
    </script>
</div>

我希望我提供了足够的信息。如果需要更多信息,请告诉我。

谢谢你看:)

杰克

4

1 回答 1

0

无法调用 null 的方法“替换”

这意味着在_.template方法内部你试图调用replace一个空值,大概是一个字符串。undecore 方法看起来像这样(来自带注释的源

_.template = function(text, data, settings) {
  settings = _.defaults({}, settings, _.templateSettings);

  var matcher = new RegExp([
    (settings.escape || noMatch).source,
    (settings.interpolate || noMatch).source,
    (settings.evaluate || noMatch).source
  ].join('|') + '|$', 'g');

  // This is the only place where replace is used
  var index = 0;
  var source = "__p+='";
  // Replace used on variable text
  text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
    // replace used on source that can't be null
    source += text.slice(index, offset)
      .replace(escaper, function(match) { return '\\' + escapes[match]; });
    source +=
      escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" :
      interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" :
      evaluate ? "';\n" + evaluate + "\n__p+='" : '';
    index = offset + match.length;
  });
  source += "';\n";

所以变量text必须为空。在您的代码text中是this.template,因此在初始化时它必须为 null 。

您确定当您扩展View为 createContactView时,该#contactTemplate元素已加载到 DOM 中吗?问题一定在那里。尝试控制台记录 this.template 以查看它是否真的为空。如果您想确保在运行任何 javascript 之前加载 DOM,请将它们包装在 jQuery就绪函数中。

于 2012-11-15T20:20:24.573 回答