将数据注入 Durandal 小部件

Injecting data into a Durandal widget

我制作了一个名为 statusAlert 的 "simple" 警报小部件,用于在数据未能按预期到达时显示消息。到目前为止,我无法将数据输入其中。我认为我可以将可观察对象注入小部件,并在可观察对象设置为对象时显示和填充小部件。但我还没有想出如何让这个概念发挥作用。

statusAlert/view.html

<div class="alert alert-danger" role="alert" data-bind="visible: content">
    <button type="button" class="close" aria-label="Close" data-bind="click: hide"><span aria-hidden="true"><i class="fa fa-times"></i></span></button>
    <button type="button" class="close" aria-label="Show Details" data-bind="click: showDetails"><span aria-hidden="true"><i class="fa fa-info-circle"></i></span></button>
    <p class="lead" data-bind="text: header"></p>
    <p data-bind="text: message"></p>
    <p data-bind="visible: detailsIsVisible"><pre data-bind="text: details"></pre></p>
</div>

statusAlert/viewmodel.js

define(['knockout'], function (ko) {

    var ctor = function () {
        this.content = ko.observable();
        this.detailsIsVisible = ko.observable(false);
    };

    // Methods.

    // Activate event handler.
    ctor.prototype.activate = function () {
       //TODO What goes here?
    };

    // Destroying the content will result in the alert becoming invisible.
    ctor.prototype.hide = function () {
        this.content(null);
    };

    ctor.prototype.showDetails = function () {
        this.detailsIsVisible(true);
    };

    // Data fields: Computed observables that extract data from 'content.'

    ctor.prototype.header = ko.computed(function () {
        if (this.content) {
            return this.content() ? this.content().header : null;
        }
        return null;
    }, this);

    ctor.prototype.message = ko.computed(function () {
        if (this.content) {
            return this.content() ? this.content().message : null;
        }
        return null;
    }, this);

    ctor.prototype.details = ko.computed(function () {
        if (this.content) {
            return this.content() ? this.content().details : null;
        }
        return null;
    }, this); 

    return ctor;
});

来自父视图模型的相关代码

var statusAlertObservable = ko.observable();

$.ajax({
    success: function (data, status, request) {
        if (data.ResponseStatus.ErrorCode) {
            statusAlertObservable({
                header: "Service reports error.",
                message: data.ResponseStatus.Message,
                details: data.ResponseStatus
            });
        } else {
        // (do something with data)
        }
    },
    error: function (data, status, error) {
        statusAlertObservable({
            header: "Error getting application data.",
            message: error,
            details: data
        });
    }
});

return {
    statusAlertObservable: statusAlertObservable,
    //...
}

来自父视图的相关代码

<div data-bind="statusAlert: 0"></div>

首先,你的parentdata-binding需要调整一下。应该是这样的:

<div data-bind="widget: {kind: 'statusAlert', ...}"></div>

省略号表示您可能传递给小部件的属性:

<div data-bind="widget: {kind: 'statusAlert', config: {alertObservable: statusAlertObservable}}"></div>

注意 config 属性 是任意的。我可以给它起任何名字。我还可以包括我希望的任何其他自定义属性。

这里有一个微妙之处:在上面的代码中,我传入了 observable 本身,而不是 observable 的值。这意味着在我的小部件的 activate 处理程序中,我需要 de-reference observable 来获取它的值。或者,您可以通过这种方式传递值本身:

<div data-bind="widget: {kind: 'statusAlert', config: {alertObservable: statusAlertObservable()}}"></div>

请注意 statusAlertObservable 上的 parentheses。我不需要 de-reference 在小部件的 viewModel 中观察这个对象,因为它已经 de-referenced 了。

其他一些事情

确保将小部件的视图和 viewModel 放在 widgets 文件夹中包含小部件名称的文件夹下。这是我们的屏幕截图:

在您的情况下,widgets 文件夹下的文件夹名称将是 statusAlert

其次,确保将小部件的视图模型命名为 viewModel.js,并将小部件的视图命名为 view.html。如果不编写自定义 viewLocator,则不支持其他名称。因此,控制命名约定的是包含文件夹的名称——在本例中为 statusAlert

第三,将一个名为 activate 的激活处理程序(它必须被称为这个)添加到您的小部件的 viewModel 中,您将 widgetSettings:

ctor.prototype.activate = function (widgetSettings) {
    this.statusAlertObservable = widgetSettings.config.alertObservable;
    //alternatively: this.statusAlertObservable = widgetSettings.config.alertObservable();
};

Durandal 在 activate 处理程序上自动将小部件绑定的属性作为 "settings" 传递。

第四,将小部件的构造函数命名为 ctor 并不是一个好主意。这使得调试变得非常困难,尤其是当它们都以这种方式命名时。在您的情况下,将其命名为 StatusAlert(在 Pascal 的情况下,因为它是一个构造函数)。

最后,看看 Durandal 关于小部件的文档,here

更新

正如 OP 在他的评论中指出的那样,您可以注册您的小部件绑定,然后简单地将它们与香草绑定 data-bind

我不推荐这种方法。 当您有很多小部件绑定时,尤其是在大型应用程序中,很难通过搜索访问它们。此外,其他人很难区分自定义 Knockout 绑定和小部件,因为没有 apparent 差异。在大型代码库中,程序员自己甚至可能开始怀疑哪个是哪个,然后不得不访问他的 main.js 文件或他的自定义 Knockout 绑定来确定哪个是哪个。

至少,如果您坚持要注册您的小部件,至少给它们起以 Widget 结尾的名称。因此,在 OP 的情况下,我们将调用小部件 StatusAlertWidget.