65.9K
CodeProject 正在变化。 阅读更多。
Home

Backbone.js 在 asp.net mvc razor 第 2 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.25/5 (4投票s)

2012年4月27日

CPOL

3分钟阅读

viewsIcon

93868

downloadIcon

1192

使用 backbone 添加、更新、删除对象

引言

这是 我第一个 backbone 教程 的升级版。 在第一个教程中,我们对要更新/插入的数据进行了硬编码,现在我们将进行实时更新。 

背景 

流派更新屏幕 -   

  

  

  

流派插入屏幕 -  

  

  

  

流派删除屏幕 -   

  

使用代码

Genre.cshtml-  

现在我们将添加两个与上一个教程类似的模板。 '#Genre-Edit-Template' 用于插入和更新,以及 '#Genre-Delete-Template' 用于删除。 

@{
    ViewBag.Title = "Genres";
}
<script src="@Url.Content("~/Scripts/StoreManager/Genre.js")" type="text/javascript"></script>
<div class="styler">
    <fieldset class="ui-widget">
        <legend class="ui-state-legend-default ui-corner-top ui-corner-bottom">Genre List -
            Using Backbone</legend>
        <div id="Genre_Container">
            <input type="button" value="Create New" class="Add" id="btnCreateNew" />
            <table id="Genre_List">
                <tr>
                    <th>
                        Name
                    </th>
                    <th>
                        Description
                    </th>
                    <th>
                    </th>
                </tr>
            </table>
            <div id="GenreEditDialog" style="height: 350">
                <div class="styler">
                    <fieldset class="ui-widget" id="Genre_Edit" style="height: 350">
                        <legend class="ui-state-legend-default ui-corner-top ui-corner-bottom">                          </legend>
                        <br />
                        <div id="valSum" >
                        </div>
                    </fieldset>
                </div>
            </div>
        </div>
        <script id='Genre-Template' type='text/template'>
                <td><%=Name%></td> <td><%=Description%></td>
                <td><input type="button" value= "Edit" class="Edit" /> | <input type="button" value= "Delete" class="Delete" /> </td>
        </script>
        <script id='Genre-Edit-Template' type='text/template'>
               <tr>
                <td>
                    <div class="editor-label">
                        Name</div>
                    <div class="editor-field">
                        <input type="text" class="gName" value='<%=Name%>' />
                    </div>
                </td>
                <td>
                    <div class="editor-label">
                        Description</div>
                    <div class="editor-field">
                        <input type="text" class="gDesc" value='<%=Description%>' />
                    </div>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="button" value="Save" class="Update" /> | <input type="button" id="btnCancel" value="Cancel" class="Cancel" />
                </td>
            </tr> 
        </script>
        <script id='Genre-Delete-Template' type='text/template'>
            <div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;
            width: 500px">
                <p>
                <span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
                <strong>Confirm</strong><br />
                Are you sure you want to delete this Album - <%=Name%> ?</p>
            </div><br/>
            <table>
                <tr>
                    <td>
                        <div class="editor-label">
                            Name</div>
                        <div class="editor-field">
                            <%=Name%>
                        </div>
                    </td>
                    <td>
                        <div class="editor-label">
                            Description</div>
                        <div class="editor-field">
                            <%=Description%>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td colspan="2">
                        <input type="button" value="Delete" class="Update" /> | <input type="button" id="btnCancel" value="Cancel" class="Cancel" />
                    </td>
                </tr>
            </table>
        </script>
    </fieldset>
</div> 

 Genre.js - 

好吧,这并不是什么高深的东西,你现在应该已经猜到了,我们需要有两个视图 EditView 和 DeleteView,并在单击更新和删除按钮时分别实例化它们(传递流派模型)。 在创建的情况下,我们将使用新的流派模型实例化 EditView。 然后渲染视图,以便我们可以查看它们。

流派模型 - 

我们添加了一个新函数 validateModel。 此函数将验证模型并显示验证摘要。

同样,在我上一个教程中,我曾指出,在创建新流派后,单击相应的“编辑”会触发 Http Post 而不是 put。 原因是,即使添加了新流派,该流派也被标识为新对象(genre.isNew() 为 true),backbone 总是对新对象执行 post。 原因? 好吧,我们数据库中的流派实体没有名为 id 的属性。 Backbone 需要此属性来验证对象是新的还是旧的。 我们通过在 GenreId 属性更改时将 GenreId 设置为 id 来处理这个问题。   

EditView - 

在 Editview 中请注意,我们直接绑定了名称和描述文本框('.gName', '.gDesc'),并将值设置为模型。 通过这样做,我们充分利用了 backbone.js,我们正在使视图和模型相互交互。 难道不酷吗。

在渲染视图(render 函数)时,我们打开一个包含编辑视图的对话框,设置标题“创建”或“编辑”。 否则,它与在“el”上应用模板并在视图容器“#Genre_Edit”上附加相同。 

在更新模型时,我们调用模型的 validateModel() 函数,在出错时显示验证摘要,否则保存模型。 请注意,在“创建新”时,我们将新流派添加到集合中并保存模型。

通过这个,我们能够使用单个视图完成插入和编辑流派。

DeleteView - 

 我感觉这个视图是不言自明的。 我们唯一做的不同的是,我们正在销毁模型。 

$(function () {
    $("#GenreEditDialog").dialog({
        autoOpen: false,
        show: "blind",
        hide: "explode",
        modal: true,
        height: 250,
        width: 600,
        open: function (event, ui) {
            $('.ui-dialog-titlebar', ui.dialog || ui).hide();
        }
    });

    // Genre Model
    var Genre = Backbone.Model.extend({
        url: function () {
            var base = '/StoreManager/GenreList/';
            if (this.isNew())
                return base;
            return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
        },
        initialize: function () {
            console.log('Genre Constructor Triggered');
            this.bind("change:GenreId", function () {
                var genreId = this.get('GenreId');
                this.set({ id: genreId });
            });
        },
        defaults: {
            GenreId: 0,
            Name: '',
            Description: ''
        },
        validateModel: function () {
            var validate = true;
            var divHtml = "<div class='ui-state-error ui-corner-all' style='display: block; padding-top: 0px; padding-bottom: 0px; width: 500px; padding-left: 0.7em; padding-right: 0.7em;'>";
            divHtml += "<p><span class='ui-icon ui-icon-alert' style='float: left; margin-right: .3em;'></span><strong>Please correct these Errors</strong> </p><div class='validation-summary-errors' data-valmsg-summary='true'>";
            divHtml += "</div><p></p></div>";
            var ulHtml = "<ul></ul>";
            var div = $(divHtml);
            var ul = $(ulHtml);
            if (this.get('Name') == "") {
                ul.append("<li>Name is Mandatory</li>");
                validate = false;
            }
            if (this.get('Description') == "") {
                ul.append("<li>Description is Mandatory</li>");
                validate = false;
            }
            div.find(".validation-summary-errors").append(ul);

            if (!validate) {
                $('#valSum').show("slow");
                $('#valSum').html(div);
            }
            else {
                $('#valSum').hide("slow");
                $('#valSum').html("");
            }

            return validate;
        }
    });

    // Genre Collection
    var GenreCollection = Backbone.Collection.extend({
        model: Genre,
        url: '/StoreManager/GenreList/'
    });

    //Genre-Edit view
    var EditView = Backbone.View.extend({
        template: _.template($('#Genre-Edit-Template').html()),
        tagName: "table",
        container: "#Genre_Edit",
        initialize: function () {
            console.log('Genre Edit View Constructor Triggered');
        },
        events: {
            "change .gName": 'NameChange',
            "change .gDesc": 'DescChange',
            "click .Update": 'UpdateGenre',
            "click .Cancel": 'CancelGenre'
        },
        NameChange: function () {
            // Directly bind the name change to the model, best use of back bone.
            // Otherwise set the data during save which ever you prefer
            this.model.set({ Name: $(".gName").val() });
        },
        DescChange: function () {
            this.model.set({ Description: $(".gDesc").val() });
        },
        render: function () {
            $(this.container).append($(this.el).html(this.template(this.model.toJSON())));
            $("input:button", $(this.el)).button();
            $("#GenreEditDialog").dialog("open");
            var title = 'Create Genre';
            if (this.collection == null)
                title = 'Edit Genre - ' + this.model.get('Name');
            $(this.container).find('legend').html(title);
            $('.ui-dialog-titlebar').hide();
            $('#valSum').hide("slow");
            return this;
        },
        unrender: function () {
            $(this.el).remove();
            return this;
        },
        UpdateGenre: function () {
            if (this.model.validateModel()) {
                var self = this;
                if (this.collection != null) {
                    this.collection.add(this.model);
                }
                this.model.save(this.model, { success: function () {
                    self.unrender();
                    $("#GenreEditDialog").dialog("close");
                    var genreId = self.model.get('GenreId');
                    $("input:button", '#Genre_List').button();
                }
                });
            }
            $("input:button", '#Genre_List').button();
        },
        CancelGenre: function () {
            this.model.fetch({ success: function () {
                $("input:button", '#Genre_List').button();
            } 
            });
            this.unrender();
            $("#GenreEditDialog").dialog("close");
        }
    });

    // Genre Delete View
    var DeleteView = Backbone.View.extend({
        template: _.template($('#Genre-Delete-Template').html()),
        container: "#Genre_Edit",
        initialize: function () {
            console.log('Genre Edit View Constructor Triggered');
        },
        events: {
            "click .Update": 'DeleteGenre',
            "click .Cancel": 'CancelGenre'
        },
        render: function () {
            $(this.container).append($(this.el).html(this.template(this.model.toJSON())));
            $("input:button", $(this.el)).button();
            $("#GenreEditDialog").dialog("open");
            $('.ui-dialog-titlebar').hide();
            var title = 'Delete Genre - ' + this.model.get('Name');
            $(this.container).find('legend').html(title);
            return this;
        },
        unrender: function () {
            $(this.el).remove();
            return this;
        },
        CancelGenre: function () {
            this.unrender();
            $("#GenreEditDialog").dialog("close");
        },
        DeleteGenre: function () {
            var self = this;
            this.model.destroy({ success: function () {
                self.unrender();
                $("#GenreEditDialog").dialog("close");
            }
            });
        }
    });

    // Genre View - el returns the template enclosed within a tr
    var GenreView = Backbone.View.extend({
        template: _.template($('#Genre-Template').html()),
        tagName: "tr",
        initialize: function () {
            console.log('GenreView Constructor Triggered');
            this.model.bind('change', this.render, this);
            this.model.bind('remove', this.unrender, this);
        },
        render: function () {
            console.log('Rendering...');
            $(this.el).html(this.template(this.model.toJSON()));
            return this;
        },
        unrender: function () {
            console.log('Un-Rendering...');
            $(this.el).remove();
            return this;
        },
        events: {
            "click .Edit": 'EditGenre',
            "click .Delete": 'DeleteGenre'
        },
        EditGenre: function () {
            var editView = new EditView({ model: this.model });
            editView.render();
        },
        DeleteGenre: function () {
            var deleteView = new DeleteView({ model: this.model });
            deleteView.render();
        }
    });

    // Actual App view
    var AppView = Backbone.View.extend({
        initialize: function () {
            this.collection.bind('add', this.AppendGenre, this);
        },
        el: '#Genre_Container',
        events: {
            "click #btnCreateNew": "AddNewGenre"
        },
        AddNewGenre: function () {
            console.log('Add Genre....');
            var newGenre = new Genre();
            var editView = new EditView({ model: newGenre, collection: this.collection });
            editView.render();
        },
        AppendGenre: function (genre) {
            var genreView = new GenreView({ model: genre });
            $(this.el).find('#Genre_List').append(genreView.render().el);
        },
        render: function () {
            if (this.collection.length > 0) {
                this.collection.each(this.AppendGenre, this);
            }
            $("input:button", "#Genre_List").button();
        }
    });

    var genres = new GenreCollection();
    var view = new AppView({ collection: genres });

    genres.fetch({ success: function () {
        view.render();
    }
    });
}); 

控制器方法 -  

请注意,我们现在只处理 httppost 操作中的插入。 我们之前的代码同时处理插入和更新。 由于我们在模型上添加了 id 属性,因此不再需要处理更新。  

        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Genres(int? Id)
        {
            return View();
        }
        // Get Genres
        [AcceptVerbs(HttpVerbs.Get)]
        public JsonResult GenreList (int? Id)
        {
            if (Id.HasValue)
            {
                var genre = db.Genres
                                .Where(x => x.GenreId == Id.Value)
                                .Select(x => new { id = x.GenreId, GenreId = x.GenreId, Name = x.Name, Description = x.Description })
                                .FirstOrDefault();
                return Json(genre, JsonRequestBehavior.AllowGet);
            }
            var genres = db.Genres
                            .Select(x => new { id = x.GenreId, GenreId = x.GenreId, Name = x.Name, Description = x.Description })
                            .ToList();
            var result =  Json(genres, JsonRequestBehavior.AllowGet);
            return result;
        }
        // Update Genre
        [AcceptVerbs(HttpVerbs.Put)]
        public JsonResult GenreList(int Id, Genre UpdatedGenre)
        {
            var genre = db.Genres.Where(x => x.GenreId == Id).FirstOrDefault();
            genre.Name = UpdatedGenre.Name;
            genre.Description = UpdatedGenre.Description;

            db.SaveChanges();
            return Json(genre, JsonRequestBehavior.DenyGet);
        }
        //Add Genre
        [AcceptVerbs(HttpVerbs.Post)]
        public JsonResult GenreList(Genre CreateGenre)
        {
            db.Genres.Add(CreateGenre);
            db.SaveChanges();
            return Json(CreateGenre, JsonRequestBehavior.DenyGet);
        }
        //Delete Genre
        [AcceptVerbs(HttpVerbs.Delete)]
        public JsonResult GenreList(int Id)
        {
            var genre = db.Genres.Where(x => x.GenreId == Id).FirstOrDefault();
            db.Genres.Remove(genre);
            db.SaveChanges();
            return Json(genre, JsonRequestBehavior.DenyGet);
        } 

值得关注的点  

我们没有在流派模型中使用 Backbone.Validate 函数,仅仅是因为我们想在验证摘要上显示错误。 模型上的 Validate 函数在保存和设置对象时被触发,但它不会验证整个模型。 它只会验证模型中已更改的属性。 因此,使用 Validate 函数实现验证摘要具有很大的挑战性。

还有一件事,在我们的“validateModel”函数中,我们正在进行一些 DOM 操作。 好吧,这是一个不好的做法。 最好将我们的模型与任何 DOM 操作代码分开。 所有 DOM 操作都应该在视图内部进行。 我在这个教程中只是稍微放纵了一下。 这样做更好的方法是在 EditView.UpdateGenre 函数内部处理验证摘要。 好吧,我把它留给你来弄清楚。 我猜这应该很容易。 

编码愉快.... 

-- ANON  

 

© . All rights reserved.