如何在母版页和单个页面上应用KnockoutJS?

时间:2013-12-16 14:21:49

标签: javascript asp.net knockout.js webforms master-pages

我遇到了一个较旧的WebForms项目,我想知道我的方案是否有推荐的方法。

目标

  • 我在使用KnockoutJS绑定的模态对话框中有一个反馈表。
  • 我希望通过网站页脚中的链接在所有页面上提供反馈表。
  • 我想有几个其他页面使用淘汰赛以及他们自己的个人脚本&绑定,不论模态中的反馈形式绑定。
  • 我有些页面根本没有使用淘汰赛。我希望他们不必插入代码来实现这一目标。
  • 如果可能的话,我想避免使用全局变量来支持命名空间的JavaScript。
  • 从本质上讲,我希望页面上的视图模型和反馈视图模型不要注意彼此的存在。

当前设置

  • 我们的页脚链接位于Site.master文件中,因此我放置了Feedback.js脚本和具有绑定的模式的div。所以在母版页上,我打电话给ko.applyBindings(vm, referenceToFeedbackDiv),它可以很好地连接反馈表。
  • 我们的个人页面偶尔会有一个淘汰视图模型,所以他们可能会调用ko.applyBindings(vm),因为据他们所知,他们想将vm应用到整个页面。

问题

  • 这导致淘汰赛中的冲突,因为一个vm通过Site.master调用应用于反馈表单,并且一个vm正在由页面后面的整个正文中应用。

问题

如何启用这两项内容 - 跨所有使用淘汰的页面和个别淘汰页面的模式对话框 - 协调工作?

(当前)代码中的问题演示

请记住,问题是我希望能够有一个仅适用于客户端站点上的反馈div的反馈VM,并且我希望能够应用其他不需要知道任何内容的VM关于反馈vm。

母版页文件(Site.Master) - 摘录

这是在每一页上:

<div class="page">
    <div class="main">
        <asp:ContentPlaceHolder ID="MainContent" runat="server" />
    </div>
</div>
<div class="footer">
    &copy; <%=DateTime.Now.Year.ToString() %> Company, Inc. |  <a href="/">Home</a> | <a
        href="/about.aspx">About</a> | 

    <!-- begin feedback area -->

    <span id="FeedbackArea">
        <a data-bind="click: showModal">Feedback</a>

        <div id="feedback-modal" title="What's on your mind?">
            <div class="btn-group" id="feedbackButtonGroup">
                <button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
                    <i class="fa fa-warning fa-2x fa-align-center"></i>

                    <br />
                    <span>Problem</span>

                </button>
                <button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
                    <i class="fa fa-question-circle fa-2x fa-align-center"></i>

                    <br />
                    <span>Question</span>

                </button>
                <button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
                    <i class="fa fa-lightbulb-o fa-2x fa-align-center"></i>

                    <br />
                    <span>Suggestion</span>

                </button>
                <button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
                    <i class="fa fa-thumbs-o-up fa-2x fa-align-center"></i>

                    <br />
                    <span>Praise</span>

                </button>
                <button class="btn" data-bind="click: UpdateFeedbackType" style="padding-top: 6px;">
                    <i class="fa fa-info-circle fa-2x fa-align-center"></i>

                    <br />
                    <span>General</span>

                </button>
            </div>
            <br />
            <br />
            <textarea rows="5" placeholder="Enter feedback here" data-bind="value: feedbackText, valueUpdate: 'afterkeydown'"></textarea>
            <br />
            <br />
            <button>Send Feedback</button>&nbsp;&nbsp;
            <button data-bind="click: CancelFeedback">Cancel</button>
            <h3>Other Information: </h3>

            <ul>
                <li><strong>Feedback Type:</strong> <span data-bind="text: feedbackType"></span></li>
                <li><strong>Current URL:</strong> <span data-bind="text: pageUserIsOn"></span></li>
                <li><strong>Current User: </strong><%=hdnLoggedInUsername.Value %></li>
                <li><strong>Current Client: </strong>[Not yet captured]</li>
                <li><strong>Current Tab: </strong>[Not yet captured]</li>
            </ul>
        </div>
    </span>
    <!-- End feedback area --> 
</div>

Feedback.JS - 这也包含在每一页

......一个有点命名空间的FeedbackVM定义:

var FeedbackNamespace = FeedbackNamespace || {};

..命名空间本身的定义:

FeedbackNamespace = {
    ViewModel: function () {
       // etc. etc. 
    }
};

...并声明VM变量并在document.ready()上连接它:

var FeedbackVM;
$(document).ready(function () {
    FeedbackVM = new FeedbackNamespace.ViewModel();
    ko.applyBindings(FeedbackVM, $('#FeedbackArea')[0]); 
    FeedbackVM.Start();
    log('FeedbackVM started');
});

没有Knockout / JS的其他页面

其他网页可能有也可能没有任何javascript,更不用说淘汰了。在这些页面上,FeedbackVM目前工作正常。

具有自己的Knockout ViewModel

的页面

这些页面将有自己的命名空间JS文件,其中包含自己的document.ready()事件,这会创建一个说invoiceUploaderVM = new InvoiceUploader.ViewModel()的vm,然后调用ko.applyBindings(invoiceUploaderVM)

这是我们遇到麻烦的地方。

更新:一种可能的方法和一点点麻烦

在Site.master页面中,我将整个页脚包装在“stopBindings:true”div中:

<div data-bind="stopBindings: true">
    <div class="footer" id="footerDiv">
        <!-- Feedback Viewmodel stuff in here -->
    </div>
</div>

我已将stopBindings定义为:

ko.bindingHandlers.stopBindings = {
    init: function () {
        return { controlsDescendantBindings: true };
    }
};

My Feedback.js文件作为全局JS文件的一部分加载到每个页面上,具有:

var FeedbackNamespace = FeedbackNamespace || {};
FeedbackNamespace = {
    // defines viewmodel, etc. etc.
};
var FeedbackVM;
$(document).ready(function () {
    FeedbackVM = new FeedbackNamespace.ViewModel();
    ko.applyBindings(FeedbackVM, $('#footerDiv')[0]); 
    FeedbackVM.Start();
    log('FeedbackVM started');
});

这种方法非常有效 - 只要没有其他视图模型被绑定。在从我的母版页继承的页面上,我可能会有:

$(document).ready(function () {
    'use strict';
    vm = new invoiceUploader.ViewModel();
    ko.applyBindings(vm);
});

我希望如此:

  • 设置应用于div的反馈视图模型,停止其他视图模型
  • 设置invoiceUploader视图模型并将其应用于正文(然后由stopBindings div停止)

但是,我在加载子页面时遇到错误:

enter image description here

注释该行以应用反馈绑定使得这项工作再次正常。

我做错了什么?

3 个答案:

答案 0 :(得分:0)

我想我会将你的模态的视图模型放在一个全局对象中,除了在共享脚本中应用绑定之外,还要做任何你需要做的事情

window.feedbackModal = {
    foo: ko.observable("Whatever you need to do here"),
    bar: ko.observable("assuming it can be done the same on every page")
};

然后在Site.master

<div class="feedback-modal" data-bind="with: feedbackModal">
  <p data-bind="text: foo"></p>
  <p data-bind="text: bar"></p>
</div>

在每个页面的脚本中:

function ViewModel() {
  this.individualProperty = ko.observable(true);
  this.specificAction = function() { /* do something specific to this page */ };

  this.feedbackModal = window.feedbackModal;
}

ko.applyBindings(new ViewModel());

所以window.feedbackModal可能是未定义的,它不会给你带来问题,但如果你ko.applyBindings,你必须在视图模型中公开feedbackModal属性,否则你会收到错误应用这些绑定。

当然,有更聪明的方法可以实现这个基本想法,以便最好地适应您的模式,但重要的是,如你所知,你不能两次应用绑定,所以你需要推迟对您最具体的代码执行任务,并将可重复使用的代码公开给它。

答案 1 :(得分:0)

以下是将常用模块与页面相关模块分离的另一种策略:

// An example of a module that runs on everypage
var modalDialog = function(){
    this.name = "dialog1";
    this.title = ko.observable("My Modal Title");
    this.content = ko.observable("My Modal content is also something");
}
// An example of a module that runs on everypage
var modalDialog2 = function(){
    this.name = "dialog2";
    this.title = ko.observable("My Modal Title 2");
    this.content = ko.observable("My Modal content is also something 2");
}
// Either generate it automatically or by hand
// to represent which modules are common
var commonModules = [modalDialog, modalDialog2];

// An example of a module only for this page
var pageModule = function(){
    this.pageFunction = function(){
        alert("Called page function");
    }
}

// Composition is the final object you will actually bind to the page
var composition = {
    pageMod: new pageModule()
}

// Let's add the common modules to the composition 
ko.utils.arrayForEach(commonModules, function(item){
    var module = new item();
    composition[module.name] = module;
});

// Bind the composition
ko.applyBindings(composition);

示例HTML将是:

<div class="modalDialog">
    <h2 data-bind="text: dialog1.title"><h2>
    <h2 data-bind="text: dialog1.content"><h2>
</div>
<div class="modalDialog">
    <h2 data-bind="text: dialog2.title"><h2>
    <h2 data-bind="text: dialog2.content"><h2>
</div>

<div id="content">
    <h2>Welcome to page</h2>
    <div id="somePageStuff">
        <a href="#" data-bind="click: pageMod.pageFunction">Click me</a>
    </div>
</div>

Link to the jsfille for this

答案 2 :(得分:0)

您可以使用一种技术来设置此功能,使您的绑定范围不受页面中特定区域的限制。

退房:How to stop knockout.js bindings evaluating on child elements

示例: http://jsfiddle.net/anAgent/RfM2R/

HTML

<div id="Main">
    <label data-bind="text: ViewModel.Name">default</label>
    <div data-bind="stopBindings: true">
        <div id="ChildBinding">
            <label data-bind="text: AnotherViewModel.Name">default</label>
        </div>
    </div>
</div>

<强>的JavaScript

$(function () {
    ko.bindingHandlers.stopBindings = {
        init: function () {
            return {
                controlsDescendantBindings: true
            };
        }
    };
    var data = {
        ViewModel: {
            Name: "Testing"
        }
    };

    var data2 = {
        AnotherViewModel: {
            Name: "More Testing"
        }
    };

    ko.applyBindings(data, $("#Main")[0]);
    ko.applyBindings(data2, $("#MyModalHtml")[0]);

});