【问题标题】:Asp.net mvc4, knockout js, knockout ViewModel loads for all views?Asp.net mvc4,淘汰赛js,淘汰赛ViewModel加载所有视图?
【发布时间】:2012-10-02 02:45:14
【问题描述】:

我是 asp.net mvc4 和 knockoutjs 的新手,需要帮助了解视图的工作原理。

  1. 我在 Shared 文件夹中有一个 _Layout.vbhtml,它是项目中所有页面的“主”页面。

  2. 我有 AccountController、HomeController 和 GrowerController

  3. 我在 Views 文件夹中有 GrowerController 的 Grower 文件夹。像往常一样,索引是默认视图。

  4. 在 Views/Grower/Index 中,我有一个用于从服务器检索数据的 knoockout ViewModel。

  5. 现在,当我转到 Home/Index 等其他视图时,我在 Firebug 的控制台中看到,即使我不在我创建的视图中,它仍在进行这些调用以从服务器获取数据淘汰视图模型。

我很困惑。发生这种情况是因为我对所有页面都使用了 _layout.vbhtml 吗?我做错了什么?

编辑: *_Layout.vbhtml*

    <!DOCTYPE html>
    <html lang="en">
      <head>
       <meta charset="utf-8" />
       <title>@ViewData("Title")</title>
       <meta name="viewport" content="width=device-width" />
       <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
       <link href="http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.css" rel="stylesheet" type="text/css" />

        @* Javascrips files *@
        <script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
        <script src="@Url.Content("http://code.jquery.com/jquery-1.7.1.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/custom.js")" type="text/javascript"></script>


        <!-- Custom stylesheet overriding styles -->
        @If Request.QueryString("pr") = "dow" or ViewData("pr") = "dow" Then
         @<link rel="stylesheet" href="@Url.Content("~/Content/CustomDow.css")" />
        Else
         @<link rel="stylesheet" href="@Url.Content("~/Content/Custom.css")" />
        End If
     </head>
     <body>
       <div data-role="page" data-theme="b">
        <div data-role="header">
            @If IsSectionDefined("Header") Then
                @RenderSection("Header")
            Else
                @<h1>@ViewData("Title")</h1>
                @Html.Partial("_LoginPartial")
            End If
        </div>
        <div data-role="content">
            @RenderBody()
        </div>
    </div>

    @RenderSection("scripts", required:=False)
</body>

Grower/Index.vbhtml

    @Code
      ViewData("Title") = "Select a Grower/Branch"
    End Code

    @section scripts
      <script type="text/javascript">

      function SuperViewModel() {

        //====== GrowerInfo =======
        var self = this;
        self.GrowerName = ko.observable();
        self.GrowerCompany = ko.observable();
        self.GrowerAddress = ko.observable();
        self.ShowGrowerCompany = ko.observable();
        self.GrowerID = ko.observable();

        self.updateGrowerInfo = function () {
          $.getJSON("GetGrower", function (allData) {
          self.GrowerName(allData.Name);
          self.GrowerCompany(allData.CompanyName);
          self.GrowerAddress(allData.Address);
          self.ShowGrowerCompany(allData.ShowCompany);
          self.GrowerID(allData.ID);
         });
        };

        //Load initial state from server and populate viewmodel
        self.updateGrowerInfo();
        //========= End GrowerInfo ==========

         if ($("#hfFlag").val() == "1") {
          //========= BranchInfo ==========
          self.BranchName = ko.observable();
          self.Company = ko.observable();
          self.Address = ko.observable();
          self.ID = ko.observable();

          //Load initial state from server and populate viewmodel
          self.updateBranchInfo = function () {
           $.getJSON("GetBranch", function (allData) {
            self.BranchName(allData.Name);
            self.Company(allData.CompanyName);
            self.Address(allData.Address);
            self.ID(allData.ID);
          });
        };

        self.updateBranchInfo();
        //=========== End BranchInfo ==============
      }


      //=============== GrowerList ===============
      var MyGrower = function (data) {
       this.growerId = ko.observable(data.GrowerId);
       this.growerName = ko.observable(data.GrowerName);
     };

      self.growers = ko.observableArray([]);

      self.updateGrowers = function () {
        //refresh listview
        $("#ulGrowerList").listview();
        $("#ulGrowerList").listview("refresh");

      $.getJSON("GetGrowers", function (allData) {
        var mappedGrowers = $.map(allData, function (item) { return new MyGrower(item) });
        self.growers(mappedGrowers);


        });
      };

      self.setSelectedClassToGrowerList = function (item, event) {

        $(ulGrowerList).closest('ul').find('a').removeClass('highlight');
        $(ulGrowerList).closest('ul').find('.selected').remove();

        $(event.target).toggleClass("highlight");
        if ($(event.target).hasClass("highlight")) {
          $(event.target).append("<span class='selected'>Selected</span>");

          replaceByValue('GrowerID', event.target.id);
          postjsonToServerNow("grower");

          //update GrowerInfo 
          $.getJSON("GetGrower", function (allData) {
            self.GrowerName(allData.Name);
            self.GrowerCompany(allData.CompanyName);
            self.GrowerAddress(allData.Address);
            self.ShowGrowerCompany(allData.ShowCompany);
            self.GrowerID(allData.ID);
          });

        } else {
          $(event.target).find(".selected").remove();
        }
      };



      self.setSelectedClassToBranchList = function (item, event) {

        $(ulBranchList).closest('ul').find('a').removeClass('highlight');
        $(ulBranchList).closest('ul').find('.selected').remove();

        $(event.target).toggleClass("highlight");

        if ($(event.target).hasClass("highlight")) {
          $(event.target).append("<span class='selected'>Selected</span>");

          replaceByValue('BranchID', event.target.id);
          postjsonToServerNow("branch");
        } else {
          $(event.target).find(".selected").remove();
        }

      };



      //Load initial state from server and populate viewmodel
      self.updateGrowers();

      //============ End GrowerList =============
    }

    //============= End ViewModel Section ====================//


      $(document).bind('pageinit', function () {
        //enable ko
        ko.applyBindings(new SuperViewModel());


        $("#divBranchList").hide();

        //show hide lists
        $("#btnGrower").click(function () {
        $("#divGrowerList").show();
        $("#divBranchList").hide();
       });

       $("#btnBranch").click(function () {
         $("#divBranchList").show();
         $("#divGrowerList").hide();
       });

      });
    </script>
    End Section




    <table class="maintable" id="maintable">
      <tr>
        <td class="left">
          <div id="GrowerInfo">
            <strong>Grower</strong><br />
            <a data-role="button" data-theme="e" id="btnGrower" data-bind="click: updateGrowers">
              <h3>
               <span data-bind="text: GrowerName"></span>
              </h3>
              <span data-bind="text:GrowerCompany, visible: ShowGrowerCompany" class="block"></span><span data-bind="text: GrowerAddress">
              </span>
              <br />
              <span data-bind="text: GrowerID"></span>
            </a>
          </div>
          @If ViewData("IsDealer") Then
            @<div id="BranchInfo">
              <strong>Branch</strong> <a data-role="button" data-theme="e" id="btnBranch">
                <h3>
                  <span data-bind="text: BranchName"></span>
                </h3>
                <span data-bind="text: Company"></span>
                <br />
                <span data-bind="text: Address"></span>
                <br />
                <span data-bind="text: ID"></span></a>
            </div>
          End If
        </td>
        <td class="splitline">
        </td>
        <td class="right">
          <div class="content-right">
            <div id="divGrowerList" style="overflow: auto; height: 450px; padding: 10px;">

            <p>Total growers: <span data-bind="text: growers().length">&nbsp;</span></p>

               <ul data-inset="true" data-filter="true" data-bind="foreach: growers" data-role="listview" id="ulGrowerList">
                  <li><a data-bind="click: $parent.setSelectedClassToGrowerList, attr: {id: growerId}"><span data-bind="text: growerName, attr: {id: growerId}, click: $parent.setSelectedClassToGrowerList" /></a></li>
              </ul>

              <textarea name="growers" rows="10" data-bind="value: ko.toJSON(growers)"></textarea>

            </div>
            <div id="divBranchList">
              @If ViewData("IsDealer") Then
                @Html.Action("MyBranchList2")
              End If
            </div>
            @If ViewData("IsDealer") Then
              @<input type="hidden" id="hfFlag" value="1" />
            Else
              @<input type="hidden" id="hfFlag" value="0" />
            End If
          </div>
        </td>
      </tr>
    </table>

【问题讨论】:

    标签: knockout.js asp.net-mvc-4


    【解决方案1】:

    您已在 _layout 页面中定义了您的 Knockout ViewModel。

    如果您打算在所有页面上使用此视图模型,这很好,但听起来您不想这样做。将其移动到特定的操作视图中以将其隔离。

    在上面对 Sethi 的评论中,您提到 javascript 在您这样做时不起作用……这可能是因为您在布局和视图中都放置了脚本标签。我敢打赌,您正在尝试在加载 knockout.js 之前构建视图模型。

    良好做法:

    在您的布局中,将您的脚本标签放在页面底部,就在&lt;/body&gt; 关闭标签之前...关闭之前的最后一个标签应该是您设置的RenderSection() 调用。

    现在,您可以在视图中的任何位置定义此脚本部分,并确保它出现在您的框架脚本之后 - 例如。 jQuery、Knockout 等

    另外,请记住,对于 jQuery,使用$.ready() 几乎总是更可取的,以确保您的脚本仅在加载 DOM 后运行。

    【讨论】:

    • 我的 knockout.js 包含在 _layout 的 标记中,并且应该可用于使用 _layout.vbhtml 的每个页面。我正在使用 $(document).bind('pageinit', function () { per jQuery Mobile 推荐 (jquerymobile.com/test/docs/api/events.html) 并在其中执行 ko.applyBindings。不知道这是否是问题所在,它认为 pageinit 是页面的全局初始化,所以 ko 模型正在加载所有视图?创建“脚本”部分并将 jscript 移动到该视图中没有帮助。请告知。
    • 另外,澄清一下,淘汰视图模型在特定视图中,而不是在_layout中。我已将其移至“脚本”部分,并且 javascript 似乎中断了(通过在 $(document).bind('pageinit', function () {,但 div 显示,将 js 从“脚本”部分移出有效)。在淘汰视图模型中定义的所有函数调用仍在每个 Firebug 中执行每个视图导航(叹气!)。
    • 请注意,如果我将 ko 视图模型移动到(特定视图的)“脚本”部分,则在刷新页面之前它根本不会加载!不知道发生了什么。
    • 在我的原始消息中添加了 _Layout.vbhtml 和 Grower/Index.vbhtml 标记。
    【解决方案2】:

    没有看到您的 _Layout.vbhtml 我不能肯定地说,但是由于您在每个页面上都运行了 javascript,因此您的 _Layout.vbhtml 上可能有您的 script 标签

    因此,将其移动到您的 Index.vbhtml 中,将其放置在 &lt;head&gt; 标记中,在 Layout.vbhtml 中使用 @RenderSection("Scripts") 并在您的视图中使用:

    @Section "Scripts"
    
    @<script>
        // write JS here or reference a file using the src attribute
    </script>
    
    End Section
    

    这会将 @Section "SectionName"End Section 之间的所有内容放在 Layout.vbhtml 中,而不是 @RenderSection("Scripts")

    编辑:

    从您的 cmets 到 one.beat.consumer 我想我看到了您的问题。但请理解我仍在猜测,因为我还没有看到您的代码 - 一个大问题,因为您没有提及您使用的是 jQuery Mobile

    常规 MVC4 Web 应用程序和 jQuery Mobile 应用程序之间存在重大差异。

    一个是:使用 jQM,您总是在同一个页面上——您通过 ajax 从服务器加载页面,然后将其剥离为 &lt;div data-role="page"&gt;...&lt;/div&gt;。这解释了为什么如果您将特定页面的脚本放在其&lt;head&gt; 部分中,jQM 将忽略它。唯一未被忽略的&lt;head&gt; 部分是加载的原始页面并不总是您的主页

    因此,要为特定页面加载脚本,您不能使用 MVC 部分将其放入 &lt;head&gt; 元素中。您需要在&lt;div data-role="page"&gt; 中引用该脚本。 虽然如果你正在缓存你的页面,它只会触发一次,如果你希望它再次触发,你可能需要绑定到pageshow(也许是为了刷新视图模型)。

    最后,

    您说您在 pageinit 处理程序中应用您的视图模型。每当从服务器获取页面并注入 DOM 时都会调用此方法。

    如果您想让您的 js 不在视图中,您可以在 _Layout.vbhtml &lt;head&gt; 中引用站点范围的 js 文件 (custom.js) 并使用:

    $(document).on('pageinit', '#PageId', function(event) {
        /* do viewmodel stuff here */
    });
    

    只要获取带有id="PageId"&lt;div data-role="page"&gt; 并将其注入到DOM 中,就会运行此程序。您可以在此处使用任何选择器,因此.require-vm 可用于在注入带有class="require-vm" 的页面时触发。

    【讨论】:

    • 我的 _layout.vbhtml 在关闭
      后有一个 @RenderSection("scripts", required:=False) 但在正文中。我不将 index.vbhtml 中的脚本部分用于 javascript,因为如果我这样做,javascripts 将不起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    • 2013-08-14
    相关资源
    最近更新 更多