Knockout js:有条件地绑定title属性

Knockout js: Conditionally bind title attribute

我在屏幕顶部有几个选项卡,可以将用户导航到程序的几个部分。 如果程序的那部分有用户的紧急通知,现在应该突出显示其中一个选项卡。

我在#main-header 中添加了一个 'notifications' 属性,这是整个顶部的 ID div。

只有 css 项正在相应更改,到目前为止还不错。

但是,只有在有任何用户通知时,我才需要在标题属性中添加文本。 如果有任何通知,每 10 分钟检查一次。

这个顶部菜单是在循环中创建的,在 html 中看起来像这样:

<ul id="navigationMenuTop" class="nav navbar-nav" data-bind="foreach: getRoutes">
        <li data-bind="css: { active: isActive() }, attr: { title: hash === '#url/location' ? setNotificationTitle : '' }">
            <a data-bind="attr: { href: hash }, html: displayName"></a>

在视图模型中:

        var self = this;
        self.setNotificationTitle = ko.computed(function () {
            var attr = $("#main-header").attr("notifications");
            if (typeof attr !== 'undefined' && attr !== false) {
                return "NotificationAlert";
            } else {
                return "";
            }
        });

这是在以下帮助下创建的:Knockout.js: conditionally bind title attribute of div

问题是使用 setNotificationTitle 导致我在顶部根本看不到标签。 当我在 html 中简单地对文本进行硬编码时,就会显示顶部菜单并且工具提示有效。

我错过了什么?

编辑: 现在的视图如下所示:

<ul id="navigationMenuTop" class="nav navbar-nav" data-bind="foreach: getRoutes">
        <li data-bind="css: { active: isActive() }, attr: { title: hash === '#url/location' ? notificationTitle : '' }">
            <a data-bind="attr: { href: hash }, html: displayName"></a>

和视图模型:

    var self = this;
    self.notificationTitle = ko.observable("");

    self.setNotificationTitle = ko.computed(function () {
        var attr = $("#main-header").attr("notifications");
        if (typeof attr !== 'undefined' && attr !== false) {
            self.notificationTitle("NotificationAlert");
        } else {
            self.notificationTitle("");
        }
    });

每当检查是否有任何通知时,都会调用 setNotificationTitle。 这也给了我同样的结果。

编辑: 不确定它是否重要,但在视图模型的底部它 returns NavigationViewModel 喜欢

return NavigationViewModel;

将其更改为我在 knockout js 文档中看到的内容

return NavigationViewModel = {
    notificationTitle: this.notificationTitle
};

由于 notificationTitle 未定义,导致我在浏览器中出现系统内容错误...

问题是您正在使用和更新 DOM 来存储状态。当您修改该状态时,Knockout 无法知道。

假设您的页面上有一个元素:

<div id="stateEl" data-state="1">...</div>

您可以编写一个可以正常工作的敲除测试绑定:

<div data-bind="text: document.getElementById('stateEl').dataset.state"></div>

当您调用 ko.applyBindings.

时,此元素将在页面上呈现“1”

但是,如果您在 调用 ko.applyBindings 之后更改第一个元素的属性 ,knockout 的文本绑定将不会更新。数据绑定依赖于 ko.observable 属性; Knockout 没有订阅 DOM.

更改的机制

所以每当你这样做时:

document.getElementById("stateEl").dataset.state = 2;

剔除元素仍将呈现“1”。

可能的解决方案

有许多可能的解决方案,有些比其他的更激进。我将按照个人喜好的顺序列出一些:

  • 仅坚持 knockout.js。 不要通过 jQuery 或任何其他 javascript 代码 直接访问或修改 DOM。即:运行 您通过 knockout 视图模型定期更新并使用在 knockout 中运行良好的模式来共享您要显示的数据。
  • 确保外部世界可以访问您的视图模型实例,并确保更新代码通知它任何新信息。
  • 在您的视图模型中实施额外的定时更新,每隔 x 秒从 DOM 读取一次

这是第二种解决方案的示例:

// Viewmodel
const vm = {
  notificationCount: ko.observable(0)
}

// Apply bindings
ko.applyBindings(vm, document.getElementById("app"));

// Periodic updates
let i = 0;
setInterval(() => {
  const newCount = i++;
  
  // Update the attribute
  const header = document.getElementById("header");
  header.dataset.count = newCount;
  
  // Update the knockout vm explicitly
  vm.notificationCount(header.dataset.count);
}, 500);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<!-- HTML not bound to knockout -->
<div id="header" data-count="0"></div>

<!-- HTML bound to a viewmodel in knockout -->
<div id="app" data-bind="text: notificationCount"></div>

最终通过添加这个 observable 和这个在检查是否有任何通知时调用的函数解决了这个问题。

        self.notificationTitle = ko.observable("");
    
        self.setNotificationTitle = function () {
            var attr = $("#main-header").attr("notifications");
            if (typeof attr !== 'undefined' && attr !== false) {
                self.notificationTitle("NotificationAlert");
            } else {
                self.notificationTitle("");
            }
        };

并像这样添加:

<li data-bind="css: { active: (isActive() }, attr: { title: $parent.notificationTitle }">