当HTML与文档

时间:2017-04-25 08:12:34

标签: knockout.js

我使用淘汰赛进行SPA应用。

我通过将HTML加载到javascript中的元素对象,然后根据需要附加/分离该元素对象来管理它。

(这个问题中的代码是TypeScript,如果你认为它有点搞笑寻找JavaScript。)

装载

this.Element = $(this.Template)[0];
this.ViewModel = new this.ViewModelConstructor();

ko.applyBindings(this.ViewModel, this.Element);

附加

this._containerElement.children().detach();
this._containerElement.append(this._currentPage.Element);

通过这种方式,页面只被加载并初始化一次,这是我们正在使用的一些控件的烦人要求,并且是交换的,而不是在每次页面加载时重新初始化和重新呈现。 / p>

令我高兴的是,Knockout处理这个问题就像一个冠军!

我使用jQuery detach来保留元素的淘汰数据,然后重新附加attach

这很好用,直到我开始尝试使用Knockout Components

我发现viewmodel还活着,而且observable正在更新,但是对html元素的绑定正在被删除。

我通过在计算的observable中放入一些代码来诊断这一点,这些代码在更新时将其转储信息:

public CurrentCompanyName = ko.computed(() => {
    let name = "None";
    let currentCompany = this.AccountManager.CurrentCompany();

    if (!Utils.IsNullOrUndefined(currentCompany))
        name = currentCompany.Name;

    if (this.CurrentCompanyName) {
        let subs = this.CurrentCompanyName.getSubscriptionsCount();
        let deps = this.CurrentCompanyName.getDependenciesCount();

        logger.Log(`CompanySelectorViewModel: CurrentCompany: ${name}, Sub Count: ${subs}, Deps Count: ${deps}, ID: ${this.InstanceID}`, logger.Level.HighLight);
    }

    return name;
});

我发现在第一次通话时,在分离之前,有1个用户,但在分离和重新连接之后降至0。

我进行了搜索,并在不久前找到了这个question

  

RP Niemeyer:

     

单个数据绑定中绑定的评估被包装在一个计算的observable中,该observable在重新计算时会自行处理并识别出它不是当前文档的一部分。

     

所以,没有一个简单的解决方法可以让你做你正在尝试的事情。您可以在进行更新时隐藏元素,然后取消隐藏它们。

这是真的,但仅限于组件,如上所述我也有很多"正常"绑定,他们工作得很好。

我花了很长时间在GitHub阅读淘汰源代码,很遗憾,我无法找到负责检测组件何时不再是身体一部分的代码

有没有人知道,我如何阻止组件检测到它们已经脱离,或者我可以找到执行此检测的代码,以便我能理解它是如何工作的?

由于分离绑定似乎对非组件非常有用,如果我无法使组件工作,我可能会根据基于敲除的方式编写自己的组件加载器。

更新1

我以为我可能已经找到了支票的执行地点。

https://github.com/knockout/knockout/blob/master/src/binding/bindingAttributeSyntax.js#L70

https://github.com/knockout/knockout/blob/master/src/utils.js#L353

我厌倦了覆盖该功能以始终返回true,但它并没有解决问题。对观察者的订阅仍然丢失。

// A hack to get component bindings working
// THIS DOESN'T WORK!
// https://github.com/knockout/knockout/blob/master/src/binding/bindingAttributeSyntax.js#L70
(<any>ko.utils).anyDomNodeIsAttachedToDocument = () => {
    logger.Log("anyDomNodeIsAttachedToDocument", logger.Level.HighLight);
    return true;
}

更新2

我找了一种方法来监控取消订阅绑定的内容。

我发现ko.subscription有一个dispose函数。

我使用包装器覆盖了该函数,该包装器允许我添加日志记录以查看它何时被调用,然后允许我轻松地删除断点以使用调试器检查callstack。

let originalDispose = (<any>ko).subscription.prototype.dispose;

(<any>ko).subscription.prototype.dispose = function () {
    logger.Log("DISPOSE", logger.Level.HighLight);
    originalDispose.call(this);
};

看着callstack,我发现它不是anyDomNodeIsAttachedToDocument,而是domNodeIsAttachedToDocument

https://github.com/knockout/knockout/blob/master/src/subscribables/dependentObservable.js#L246

所以,我在Update 1中的黑客攻击很接近,它只需要应用于稍微不同的功能。

(<any>ko.utils).domNodeIsAttachedToDocument = () => {
    logger.Log("domNodeIsAttachedToDocument", logger.Level.HighLight);
    return true;
}

此时我应该指出这是一个讨厌的黑客!

它正在搞乱第三方图书馆的私人内部,并且通过这样做我很可能并且可能为我自己造成其他问题。

第一个明显的问题是,这会破坏自动处理ifwhen等处理程序的绑定。

因此,虽然这是一个表面上工作的解决方案,但我现在必须更深入地调查并决定:

有没有办法实现这种黑客攻击只会影响我关心的情况。

如果影响的范围不受限制,那么它们是否可以接受任何替代方法。

如果有任何进一步的影响不太明显但必须考虑到。

更新3

正如在Update 2中发现的那样,我可以通过全局禁用自动释放来解决问题。

然而,我对在全球范围内做出这个决定感到不舒服,因为它可能会产生意想不到的后果,对于其他开发者来说,这将是一个令人讨厌的隐藏陷阱。

所以我提出了一个绑定处理程序,我可以将它们应用于它们的正常绑定旁边的元素,并且这会将该元素标记为自动处理应该忽略的元素。

实施例

<span data-bind="text: CurrentCompanyName, disableAutoDispose: true"></span>

绑定处理程序

let disableAutoDisposeDataKey = "__ko__disableAutoDispose";
let originalDomNodeIsAttachedToDocument = (<any>ko.utils).domNodeIsAttachedToDocument;

(<any>ko.utils).domNodeIsAttachedToDocument = function (node: Node) {
    let disable = $(node).data(disableAutoDisposeDataKey);

    if (disable)
        return true;

    return originalDomNodeIsAttachedToDocument.call(this, node);
};

ko.bindingHandlers.disableAutoDispose = {
    init: (element, valueAccessor, allBindingsAccessor) => {
        $(element).data(disableAutoDisposeDataKey, true);
    }
};

这仍然是hacky和脆弱。

未来对淘汰赛的改变可能会破坏这段代码,而且由于它是内部的,他们没有义务/义务优雅地进行这些改变(例如分阶段弃用)或者甚至公开宣布它们。

但是我感到非常高兴的是它现在仅限于绑定处理程序,并且在每个绑定级别是可选的。

我很快就会将这些更新转换为正式答案,因为我找到了解决方案。

虽然我确实希望别人会提出一些好的建议! ;)

1 个答案:

答案 0 :(得分:0)

查看this

不完全确定这是否符合您的标准,但这是我在使用淘汰赛构建SPA应用程序时所使用的,据我所知,我从未碰到像您这样的问题。

我把不同的页面放在HTML中

<script id="name-of-page" type="text/html">

标签所以HTML始终存在于DOM中,并且因为它的敲除自己的绑定可以希望他们知道如何处理他们自己的组件:)

希望它有所帮助!