javascript - MVC SPA + knockout

标签 javascript asp.net-mvc knockout.js

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

我是 Knockout.js 的新手,所以我观看了一些视频,我明白了这个想法,但 SPA 项目对我来说似乎无法计算,因为它不是单页应用程序 因为你必须转到一个新的 URL 来登录、注册、授权、管理帐户等(你明白了)。

因此,查看索引页的代码,我可以看到 homeView 的 View 模型。它看起来像这样:

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的变量,则显示它(我假设这就是绑定(bind)成员名称)。

因此,看到我可以猜测是否添加了另一个部分页面并将其包含在内。我可以创建一个像这样的 View 模型:

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

所以,理论上,因为每当有人导航到 #draw 时,这会将 app.home 设置为 null,那么 home 部分将不会显示,类似地,我可以将 app.draw = null 添加到 sammy 路由中,以便隐藏 homeViewModel绘制部分。

我的问题是,我创建的 viewModel 越多,它就会变得越复杂。那么,我缺少什么吗?有更简单的方法吗?

我的最终目标是把所有页面都变成SPA(包括登录/注册页面)。

提前干杯, /r3plica

最佳答案

好吧,经过一番折腾后我发现了如何做到这一点。 基本上我重写了 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;
};

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

再次在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 文件中,我有以下 samy 声明:

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

关于javascript - MVC SPA + knockout ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25623332/

相关文章:

javascript - 如何从 api 查看商品并将商品添加到购物车

javascript - 在javascript中将带有#符号的文本转换为链接

javascript - 使用按钮插入时间取决于单击的字段?

javascript - json 和 Utc 日期时间

c# - IMemoryCache 保证唯一的新 key .NET-Core

javascript - 无法观察从多选列表中选择的 knockout 选项

javascript - 如何防止从outerHTML/innerHTML 中删除双引号?

javascript - 更改图像大小时,JCrop 不保存选择

javascript - KnockoutJS & jQuery(noConflict) & prototype.js = 冲突?

javascript - knockout 自动增长绑定(bind)删除更改事件绑定(bind)