簡體   English   中英

了解VS2013 MVC 5 SPA模板

[英]Understanding the VS2013 MVC 5 SPA template

我已經開始使用Visual Studio 2013附帶的MVC 5單頁面應用程序模板。我對Knockout.js非常熟悉,雖然我沒有和Sammy.js在一起, Sammy.js我一直在閱讀它它看起來並不那么復雜。

我似乎無法理解的是MVC 5 SPA模板如何結合這些技術,或者Visual Studio團隊為模板所考慮的內容作為示例; 該模板提供了一個home.viewModel.js文件,該文件應該作為起點,但我似乎無法理解如何使用Sammy.js路由添加更多視圖。 如果只有他們提供了第二個局部視圖和視圖模型。

我的問題

所以,短篇小說,我真正的問題是,

  1. 如何以模仿提供的home.viewmodel.js的方式顯示鏈接到路徑#users的局部視圖,以便我可以從#home向后導航到#users Sammy.js路由定義在users.viewModel.jsusers.viewModel.js
  2. 我是否需要做一些特殊的事情來啟用瀏覽器后退按鈕,或者只要我正確定義了我的路由它就能正常工作?
  3. 它是我還是這個模板感覺是一個半生不熟的例子?

以下代碼僅用於額外的參考/上下文,但為了回答問題,可能沒有必要。


一些背景

假設我創建了一個部分視圖_Users.cshtml ,由UserController ,它是一個MVC控制器,而不是一個WebAPI控制器,我希望通過Sammy.js路由顯示該部分視圖,為此我創建了一個users.viewModel.js 現在...

提供的Index.cshtml視圖如下所示:

@section SPAViews {
   @Html.Partial("_Home")
}
@section Scripts{
   @Scripts.Render("~/bundles/knockout")
   @Scripts.Render("~/bundles/app")
}

我認為這意味着應用程序“shell”頁面,其中部分視圖的其余部分將被加載以替換_Home部分的內容。 問題是在home.viewmodel.js上初始化Sammy路由而不傳入將保存內容的元素的選擇器,就像這樣

Sammy(function () {
    this.get('#home', function () {
    // more code here
}

而不是,例如

Sammy("#content", function () {
    this.get('#home', function () {
    // more code here
}

我應該放置_Users一起局部_Home從一開始就使得Index視圖看起來像這樣?

@section SPAViews {
   @Html.Partial("_Home")
   @Html.Partial("_Users")
}
@section Scripts{
   @Scripts.Render("~/bundles/knockout")
   @Scripts.Render("~/bundles/app")
}

當然,這將同時顯示兩個視圖,這不是我們想要的。

我的users.viewmodel.js看起來像這樣:

function UsersViewModel(app, dataModel) {
    var self = this;

    Sammy(function () {
        this.get('#users', function () {
            // the following line only makes sense if _Users is not 
            // called from Index.cshtml
            //this.load(app.dataModel.shoppingCart).swap();
        });
    });

    return self;
}

app.addViewModel({
    name: "Users",
    bindingMemberName: "users",
    factory: UsersViewModel
});

我已經嘗試過使用Sammy.js swap方法,但由於我的_Users視圖是部分的,並且Sammy沒有設置為對特定元素進行操作,因此整個頁面被替換...並且瀏覽器的后退按鈕似乎沒有工作。

對於大量的文本感到抱歉,如果這是一個非常微不足道的問題。 令我困擾的是,即使在完成文檔之后,我似乎無法自己解決這個問題。

我自己磕磕絆絆地設法應用自己的小“黑客”來解決這個問題。

在比較“舊”模板和新模板時,我注意到Sammy.js更多地嵌入模板中。 雖然這是一件好事,但原始開箱即with裝訂結合顯示你的觀點已被打破。

要應用此修復程序,首先需要了解with綁定的敲除。 在默認的home視圖中有

<!-- ko with: home-->

只有在成員home存在時才應確保home view可見性的聲明。 在這種情況下,全名是app.home

如果我們檢查這個成員名稱,我們會看到它是一個定義的計算成員,例如(app.viewmodel.js):

// Add binding member to AppViewModel (for example, app.home);
self[options.bindingMemberName] = ko.computed(function () {
    if (!dataModel.getAccessToken()) {
        //omitted for clearity
        if (fragment.access_token) {
            //omitted for clearity
        } else {
            //omitted for clearity      
        }
    }

    return self.Views[options.name];
});

如您所見,它始終從Views集合返回完整的初始化視圖。

如果我們將其與舊模板進行比較,我們可以在此處看到更改:

// Add binding member to AppViewModel (for example, app.home);
self[options.bindingMemberName] = ko.computed(function () {
    if (self.view() !== viewItem) {
        return null;
    }

    return new options.factory(self, dataModel);
});

如果當前視圖不是目標viewItem則返回null。 這對於with約束力的淘汰賽至關重要。

對這兩個模板的進一步檢查顯示與sammy.js的更好集成。 它的一個關鍵部分在於viewmodels(home.viewmodel.js):

Sammy(function () {
   this.get('#home', function () {
    });
    this.get('/', function () { this.app.runRoute('get', '#home') });
});

由於sammy.js正在處理導航,因此未設置封裝在app.view()的前面提到的viewItem 對於淘汰賽綁定來說,這也是至關重要的。

所以,我提出的修復方法如下:

app.viewmodel.js

// Add binding member to AppViewModel (for example, app.home);
self[options.bindingMemberName] = ko.computed(function () {
    if (!dataModel.getAccessToken()) {
        //omitted for clearity
        if (fragment.access_token) {
            //omitted for clearity
        } else {
            //omitted for clearity      
        }
    }

    ///change start here
    if (self.view() !== viewItem) {
            return null;
        }

    return self.Views[options.name];
});

並在每個自定義視圖模型中:

home.viewmodel.js

Sammy(function () {
    this.get('#home', function () {
         app.view(self);  //this line is added
    });
    this.get('/', function () { this.app.runRoute('get', '#home') });
});

免責聲明:由於我剛剛啟動並運行,我沒有時間分析任何不必要的副作用。 此外,改變默認模板的核心並不是很令人滿意,因此歡迎更好的解決方案。

當然,這將同時顯示兩個視圖,這不是我們想要的。

實際上,在許多情況下,這正是您想要的(或者,您希望他們的存在並控制其可見性。)除了viewmodel上的visibility屬性和一些JS幫助器方法(或類)來顯示/隱藏您的視圖(通過視圖模型引用,通常也與特定URL相關聯。)

_Home.cshtml

<!-- ko with: $root.home -->
<div data-bind="visible: isVisible">
    <!-- view markup/etc here -->
</div>
<!-- /ko -->

偽: app.viewmanager.js

MyViewManager = function () {
    this.registerView = function(route, selector, viewmodel) {/**/};
    this.showView = function(selector, callback) {};
    this.cancelView = function(callback) {/**/};
    this.showModal = function(selector, callback) {/**/};
    this.closeModal = function(selector, callback) {/**/};
}

這些將處理與History API集成以進行路由/深度鏈接,以及用於顯示/隱藏DOM元素的淘汰(通過IsVisible綁定)。 當然,上面的'registerView'將取代默認支架中的addViewModel 所有這些,IMO,都是垃圾。

多年來,我一直在MVC框架之上開發SPA。 MVC5 SPA模板很受歡迎,但它有問題。 正確的深層鏈接,視圖模型初始化和視圖管理是更明顯的問題,但有一點肘部油脂,您可以輕松編寫您需要的代碼。

我還發現SPAViews部分沒用,並且更喜歡使用RenderBody進行部分傳遞,這需要對_Layout.cshtml一些修改。 畢竟,對於一個足夠大的SPA,你最終會在一個頁面/視圖中提供幾乎所有的主要視圖(很少見到SPA中的Ajax部分,甚至是大的。)並且SPAViews部分提供的唯一值是_Layout中的位置,有效地復制了RenderBody()的功能(因為SPA的主體總是一組不可見的視圖。)

是的,它確實令人困惑,而且似乎沒有太多的文檔。 我懷疑做事方式的數量是如此之大,以至於他們不得不將其烘焙一半。 FWIW我通過簡單的頁面導航完成,將以下內容添加到app.viewmodel

   navigator = function () {

            self.view(viewItem);   //THIS IS ADDED



            window.location.hash = options.bindingMemberName;
        };

在index.cshtml我有這個:

@section SPAViews {
<!-- ko if: app.view() === app.Views.Login -->
    @Html.Partial("_login")
<!-- /ko -->
<!-- ko if: app.view() === app.Views.MyDashboard -->
    @Html.Partial("_myDashboard")

}

我認為他們可能期望你設置的方式是讓整體視圖狀態以某種方式綁定到'app'viewmodel(更改視圖,視圖observable以某種方式重新排列頁面)。 除了上面的簡單方法,還不確定應該如何做到最好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM