【问题标题】:Why does the alert box show twice in this code?为什么此代码中的警告框显示两次?
【发布时间】:2015-09-24 15:32:15
【问题描述】:

这里有一些简单的代码来演示敲除模板的“后渲染”功能。当我点击按钮时,警告框会显示两次,而它应该只显示一次。

您可以在下面找到完整的代码。知道为什么警报框会出现两次吗?

<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.min.js"></script>

</head>

<body>

<div data-bind="component: {name : 'foo-component', params: {onLoad: fooLoadedCallback}}"></div>

<!--foo-component params="onLoad: fooLoadedCallback"></foo-component-->


<script type="text/javascript">
ko.components.register('foo-component', {
    //inner view model, only for this component in the template below.
    viewModel: function(params) {
        return {
            status: ko.observable('Constructed'),

            //this is called only after the template is rendered. This is called due to a statement in template.
            componentLoaded: function () {

                if (params.onLoad) {
                    params.onLoad(this);
                }
                $('button').click(function(){
                    alert("Rendered.");
                });

            }
        };
    },
    template: '<h1>Hello</h1><br><button>Click me</button><br><span data-bind="text: status">Template</span>'
       +'<span data-bind="template: { afterRender: componentLoaded() }"></span>',
});

//outer view model, for the div element.
var outerViewModel = {
    fooLoadedCallback: function(viewModel) {
        viewModel.status('Rendered!');
    },
};

ko.applyBindings(outerViewModel);
</script>
</body>
</html>

【问题讨论】:

  • 我想你的页面上有 2 个 foo-component,我认为你应该使用
  • 第二个foo-component被注释掉了,我测试的时候还真把它删了。至于按钮,我知道那个语法;我的目标是测试后渲染器。请注意,我的问题不是关于如何使用按钮;这就是为什么警告框出现两次的原因。
  • 对不起,我滚动得太快了,所以我没有看到 ko.components 上面的任何代码......无论如何,你能确认 componentLoaded 只被调用一次吗?
  • 啊,当我加载页面时,componentLoaded 被调用了两次。为什么?我在 if(params) ... 正上方放置了一个警报,并且该警报显示了两次。

标签: javascript jquery knockout.js


【解决方案1】:

afterRender 在异步加载组件时总是被调用两次。一次用于占位符元素,再次用于真实事物。使用once 之类的东西来防止它被调用两次,或者使componentLoaded 可以安全地被多次调用。

另外,$('button') 会为您提供当前在 DOM 中的每个按钮。如果您有多个,它会将点击添加到所有这些。

【讨论】:

  • 谢谢。我使用“with”而不是“template”来修复它。不过我有一个不相关的问题:对于第三方组件,我如何声明 afterrenderer?现在,对于“我自己的”组件,我可以通过添加跨度来修改模板,就像上面一样。但是对于一些第三方组件,我不应该改变它的代码,那我怎么allw afterrenderer呢?
  • @Simo 不要使用with 代替template。它们完全不同。
  • @Simo 所以你不能修改源来添加afterRender?能否在父级中创建视图模型,然后将其传递给组件?
  • 不,我不应该更改库。所以,问题是我有嵌套的组件,一个在另一个里面。内部组件绑定到通过 ajax 检索的数据,因此它在外部组件之后加载一段时间。结果,当我在 $(document).ready .... 中使用 jquery 时,将 $('button').click 应用于内部组件中的按钮。结果是,当我点击按钮时什么都没有发生。如果按钮不在内部组件中,那么它可以工作。所以我的猜测是 $(document).ready 甚至在内部组件完成渲染之前也适用。
  • @Simo 你是对的。使用 Knockout 时不应使用 jQuery 修改 DOM 的原因之一。
【解决方案2】:

您收到两个警报,因为您的 componentLoaded 方法被调用了两次,将多个侦听器附加到按钮。

我相信您的问题出在&lt;span data-bind="template: { afterRender: componentLoaded() }"&gt;&lt;/span&gt; 部分。

例如,如果您将template 更改为with,则您的代码可以正常工作。有一个例子:

template: '<h1>Hello</h1><br><button>Click me</button>'
+ '<br><span data-bind="text: status">Template</span>'
+ '<span data-bind="with: { afterRender: componentLoaded() }"></span>'

但是,请记住,我从未使用过 Knockout,因此无法告诉您这样做是否正确。我只是指出发生了什么。

【讨论】:

  • 确实,componentLoaded 方法被调用了两次。知道如何解决吗?正如你所说,不知道如何用“with”更改“模板”。
  • 试试&lt;span data-bind="with: { afterRender: componentLoaded() }"&gt;&lt;/span&gt;
  • 哦,解决了!伟大的。请编辑您的答案,以便我将其标记为“答案”。
  • 我不会称之为答案。它看起来更像是一种解决方法,而不是我的答案。也许我们最好等待对框架有一定经验的人。
  • with 在这里做的是完全不同的事情。很确定它只是纯粹的巧合。它必须创建足够的延迟以允许首先创建视图模型,但我不会依赖它。
【解决方案3】:

只需将您的componentLoaded() 放入function 之类

 template: '<h1>Hello</h1><br><button>Click me</button><br><span data-bind="text: status">Template</span>'
       +'<span data-bind="template: { afterRender: function(){componentLoaded()} }"></span>'

【讨论】:

    猜你喜欢
    • 2017-03-06
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 2019-09-16
    • 2023-03-31
    • 2023-03-18
    • 1970-01-01
    • 2011-10-24
    相关资源
    最近更新 更多