【问题标题】:MVC SPA + KnockoutMVC SPA + 淘汰赛
【发布时间】:2014-09-03 02:33:50
【问题描述】:

我最近刚刚接到了创建 SPA 的任务。所以,我创建了一个新项目并选择了 SPA,发现它加载了我需要的所有文件,包括这个 knockout.js。

我是 knockout.js 的新手,所以我看了一些视频就明白了,但是 SPA 项目对我来说似乎并没有计算出来,因为它不是一个单页应用程序 因为你必须去一个新的 URL 来登录、注册、授权、管理帐户等(你明白了)。

所以,查看索引页面的代码,我可以看到 homeView 的视图模型。它看起来像这样:

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

    self.myHometown = ko.observable("");

    Sammy(function () {
        this.get('#home', function () {
            // Make a call to the protected Web API by passing in a Bearer Authorization Header
            $.ajax({
                method: 'get',
                url: app.dataModel.userInfoUrl,
                contentType: "application/json; charset=utf-8",
                headers: {
                    'Authorization': 'Bearer ' + app.dataModel.getAccessToken()
                },
                success: function (data) {
                    self.myHometown('Your Hometown is : ' + data.hometown);
                }
            });
        });
        this.get('/', function () { this.app.runRoute('get', '#home') });
    });

    return self;
}

app.addViewModel({
    name: "Home",
    bindingMemberName: "home",
    factory: HomeViewModel
});

HTML 看起来像这样:

<!-- ko with: home -->

<!-- removed HTML to make it concise -->

<!-- /ko -->

现在,从这个外观(如果我错了,请纠正我)with 句柄指出如果有一个名为 home 的变量,则显示它(我假设这就是 bindingMembername)。

所以,看到我可以猜到,如果我添加了另一个部分页面并包含它。我可以像这样创建一个视图模型:

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

    Sammy(function () {
        this.get('#draw', function () {
            app.home = null;
        });
    });

    return self;
}

app.addViewModel({
    name: "Draw",
    bindingMemberName: "draw",
    factory: DrawViewModel
});

所以,理论上,因为这会将 app.home 设置为 null 每当有人导航到 #draw 时,将不会显示 home 部分,同样我可以将 app.draw = null 添加到 homeViewModel 隐藏的 sammy 路线抽奖部分。

我的问题是,我创建的视图模型越多,它就会变得非常复杂。那么,有什么我想念的吗?有没有更简单的方法?

我的最终目标是将所有页面移动为 SPA(包括登录/注册页面)。

提前干杯, /r3plica

【问题讨论】:

  • 我不知道你为什么发送那个链接。我知道每个 ViewModel 都不是一个新页面,该页面是加载的第一个页面(在我的情况下为 Index.cshtml),我正在尝试使用哈希标签提取内容
  • 提醒一下,我使用基于 KO 的框架已经有几年了,但是在迁移到 Angular 之后发现它要容易得多。甚至像 Durandal 这样的大型框架也在逐渐远离 KO(参见 - blog.angularjs.org/2014/04/angular-and-durandal-converge.html)。这是一个很好的教程,可以帮助您入门。它让我们的团队在很短的时间内了解了很多 - youtube.com/watch?v=i9MHigUZKEM
  • 这很宽泛,很快就可以回答,但主要想法是将您的Sammy 导航到app(其中包含一组页面和ko.observable 中的当前选定页面)。然后,在layout.html 上,您将拥有一个 ko 模板,它将使用当前选定页面的视图模型。所以,当一个 url 改变时,在你的 Sammy 回调中你只是改变当前页面,然后 ko 会自动改变模板内容。所以页面和导航的所有工作都包含在一个地方 - app
  • 在这里,我创建了一个jsfiddle,其中包含基本原则。

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


【解决方案1】:

好吧,经过一番折腾,我发现了如何做到这一点。 基本上我重写了 AddView 方法,让它看起来像这样:

// Other operations
self.addViewModel = function (options) {
    var viewItem = new options.factory(self, dataModel),
        navigator;

    // Add view to AppViewModel.Views enum (for example, app.Views.Home).
    self.Views[options.name] = viewItem;

    // 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);
    });

    if (typeof (options.navigatorFactory) !== "undefined") {
        navigator = options.navigatorFactory(self, dataModel);
    } else {
        navigator = function () {
            self.view(viewItem);
        };
    }

    // Add navigation member to AppViewModel (for example, app.NavigateToHome());
    self["navigateTo" + options.name] = navigator;
};

如果检查当前持有的视图是否与我添加的不同,你可以看到。如果是,那么我返回 null (这是我如何让它隐藏我不使用的任何视图)。 为了进一步回答我的问题,如果用户未登录,我需要一种方法来确定如何定向到登录页面。

再次在 app.viewmodel.js 中添加了一些可观察的属性:

// UI state
self.user = ko.observable(null);

self.loggedIn = ko.computed(function () {
    return self.user() !== null;
});

在我的新 login.viewmodel.js 中我添加了这个函数:

// Operations
self.login = function () {
    self.loggingIn(true);

    dataModel.login({
        grant_type: "password",
        username: self.userName(),
        password: self.password()
    }).done(function (data) {
        if (data.userName && data.access_token) {
            app.navigateToLoggedIn(data.userName, data.access_token, self.rememberMe());
        } else {
            //self.errors.push("An unknown error occurred.");
        }
    }).fail(function (jqXHR, textStatus, error) {
        dataModel.displayError(jqXHR);
    }).always(function () {
        self.loggingIn(false);
    });
};

这里重要的是 app.naviateToLoggedIn 方法。它位于 app.viewmodel.js 中,如下所示:

// UI operations
self.navigateToLoggedIn = function (userName, accessToken, persistent) {
    if (accessToken) {
        dataModel.setAccessToken(accessToken, persistent)
    }

    self.user(new UserViewModel(self, userName, dataModel));
    self.navigateToHome();
};

userViewModel 非常简单:

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

    // Data
    self.name = ko.observable(name);

    // Operations
    self.logOff = function () {
        dataModel.logout().done(function () {
            app.navigateToLoggedOff();
        }).fail(function (jqHXR) {
            dataModel.displayError(jqHXR);
        });
    };

    return self;
}

最后,为了让我们的初始加载正确,在 home.viewmodel.js js 文件中,我有这个 sammy 声明:

Sammy(function () {
    this.get('#home', function () {
        if (app.loggedIn()) {
            app.navigateToHome();
        } else {
            window.location.hash = "login";
        }
    });
    this.get('/', function () { this.app.runRoute('get', '#home') });
});

【讨论】:

    猜你喜欢
    • 2012-05-17
    • 1970-01-01
    • 2017-11-29
    • 2013-06-15
    • 1970-01-01
    • 2016-11-03
    • 2018-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多