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

Posted

技术标签:

【中文标题】Knockout js:有条件地绑定title属性【英文标题】:Knockout js: Conditionally bind title attribute 【发布时间】:2021-09-28 17:12:22 【问题描述】:

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

我在#main-header 中添加了一个“通知”属性,它是整个顶部 div 的 id。

只有 css 的项目正在相应地改变,到目前为止一切都很好。

但是,只有在有用户通知时,我才需要在标题 attr 中添加文本。 如果有任何通知,它会每 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。 这也给了我同样的结果。

编辑: 不确定它是否重要,但在视图模型的底部,它会返回 NavigationViewModel 之类的

return NavigationViewModel;

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

return NavigationViewModel = 
    notificationTitle: this.notificationTitle
;

在系统内容中导致浏览器出现错误,因为 notificationTitle 未定义,它不是...

【问题讨论】:

如果hash是可观察的,你应该使用hash()。如果setNotificationTitle 是可观察的(或返回字符串的函数),则必须调用它:setNotificationTitle() 我可以读取哈希值,if 语句适用于硬编码文本。在 setNotificationTitle 中添加括号会给我同样的结果。 啊,我看到您实际上在您的问题中包含了setNotificationTitle。对于那个很抱歉。我认为问题在于您正在从计算中的 DOM 中读取数据。 Knockout 永远不会知道何时必须重新检查 main-header 属性。它不会自动观察您的文档是否有更改。解决方案是在您的定期更新运行时翻转一个可观察的属性,而不仅仅是在您的标题上设置一个属性。 我添加了一个编辑,以便您可以看到我所做的更改。现在在检查完成时调用 setNotificationTitle 是否有任何通知。不幸的是,我得到了相同的结果 css 无法读取 observable 属性。 Css 读取此标头并相应地更改 css 可以自行更改的内容。 (背景颜色、字体颜色、警告图像是否可见)如果我通过代码执行此操作,那么我需要设置超时,因为代码到达此处时未加载选项卡。 【参考方案1】:

问题在于您正在使用和更新 DOM 来存储状态。淘汰赛无法知道您何时修改该状态。

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

<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 属性;淘汰赛没有提供订阅 DOM 更改的机制。

所以每当你这样做时:

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

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

可能的解决方案

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

仅坚持使用 knockout.js。 不要通过 jQuery 或任何其他 javascript 代码直接访问或修改 DOM。即:通过淘汰视图模型运行您的定期更新,并使用在淘汰赛中运行良好的模式来共享您想要显示的数据。 确保您的视图模型实例可供外界访问,并确保更新代码通知它任何新信息。 在视图模型中实现额外的定时更新,每 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>

【讨论】:

【参考方案2】:

最终通过添加这个 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 ">

【讨论】:

以上是关于Knockout js:有条件地绑定title属性的主要内容,如果未能解决你的问题,请参考以下文章

如何有条件地绑定到 knockout.js 中的“valueUpdate”?

Knockout.js 条件绑定

knockout.js--基本用法

Knockout js绑定可空对象的属性

CSS 绑定在 Knockout JS 中不起作用

Visual Studio 数据绑定属性突出显示 [knockout.js]