0

我的应用基于 HotTowel 模板,因此它包括 Durandal、Knockout 和 Breeze。我有一页并排有 3 张桌子。第一个表有一个“模板”列表,第二个表显示所选“模板”的“部分”,第三个表显示所选“部分”的“项目”。“部分”和“项目”表是集合通过导航属性访问。我发现我遇到了间歇性绑定问题。“模板”表中的数据始终正确显示,但是相关的“部分”和“项目”有时会正确显示,而其他时候其中一个未填充。它似乎是一个时间问题。我的视图模型和视图在下面。我只是以错误的方式处理这一切吗?

define(['services/dataservice', 'services/logger', 'services/model'],
    function (ds, logger, model) {
        var templates = ko.observableArray();
        var selectedTemplate = ko.observable();
        var selectedSection = ko.observable();
        var selectedItem = ko.observable();
        var newTemplateTitle = ko.observable();
        var newSectionTitle = ko.observable();
        var newItemTitle = ko.observable();

        function activate() {
            newTemplateTitle('');
            newSectionTitle('');
            newItemTitle('');
            logger.log('Templates view activated', null, 'templates', false);
            return ds.getTemplatePartials(templates, false, false);//.then(succeeded);

            //function succeeded() {
            //    var firstTemplate = templates()[0];
            //    setSelectedTemplate(firstTemplate);
            //}
        }

        templates.subscribe(function() {
            var firstTemplate = templates()[0];
            setSelectedTemplate(firstTemplate);
        });

        var deactivate = function () {
            templates([]);
        };

        function refresh() {
            return ds.getTemplatePartials(templates, true, false);
        }

        var viewAttached = function (view) {
            bindEventToList(view, '#template-list', setSelectedTemplate);
            bindEventToList(view, '#section-list', setSelectedSection);
            bindEventToList(view, '#item-list', setSelectedItem);
            return true;
        };

        var addTemplate = function () {
            var newTemplate = ds.createEntity(model.entityNames.document);
            newTemplate.title(newTemplateTitle());
            newTemplate.isTemplate(true);
            newTemplate.organisation(ds.getCurrentOrganisation()());
            return ds.saveChanges().then(saveSucceeded);

            function saveSucceeded() {
                templates.push(newTemplate);
                templates.sort();
                newTemplateTitle('');
            }
        };

        var addSection = function () {
            var newSection = ds.createEntity(model.entityNames.section);
            newSection.title(newSectionTitle());
            newSection.isTemplate(true);
            newSection.document(selectedTemplate());
            return ds.saveChanges().then(saveSucceeded);

            function saveSucceeded() {
                newSectionTitle('');
            }
        };

        var addItem = function () {
            var newItem = ds.createEntity(model.entityNames.item);
            newItem.title(newItemTitle());
            newItem.isTemplate(true);
            newItem.section(selectedSection());
            return ds.saveChanges().then(saveSucceeded);

            function saveSucceeded() {
                newItemTitle('');
            }
        };

        var isTemplateSelected = function (template) {
            if (template && selectedTemplate()) {
                var thisId = ko.utils.unwrapObservable(selectedTemplate().id);
                return ko.utils.unwrapObservable(template.id) == thisId;
            }

            return false;
        };

        var isSectionSelected = function (section) {
            if (section && selectedSection()) {
                var thisId = ko.utils.unwrapObservable(selectedSection().id);
                return ko.utils.unwrapObservable(section.id) == thisId;
            }

            return false;
        };

        var isItemSelected = function(item) {
            if (item && selectedItem()) {
                var thisId = ko.utils.unwrapObservable(selectedItem().id);
                return ko.utils.unwrapObservable(item.id) == thisId;
            }

            return false;
        };

        var vm = {
            activate: activate,
            deactivate: deactivate,
            templates: templates,
            //sections: sections,
            //items: items,
            selectedTemplate: selectedTemplate,
            selectedSection: selectedSection,
            selectedItem: selectedItem,
            title: 'Template Maintenance',
            refresh: refresh,
            viewAttached: viewAttached,
            addTemplate: addTemplate,
            addSection: addSection,
            addItem: addItem,
            newTemplateTitle: newTemplateTitle,
            newSectionTitle: newSectionTitle,
            newItemTitle: newItemTitle,
            isTemplateSelected: isTemplateSelected,
            isSectionSelected: isSectionSelected,
            isItemSelected: isItemSelected
        };

        return vm;

        //#region internal methods
        function setSelectedTemplate(data) {
            if (data) {
                selectedTemplate(data);
                return selectedTemplate().entityAspect.loadNavigationProperty("sections").then(setFirstSectionSelected);
            } else {
                return false;
            }

            function setFirstSectionSelected() {
                setSelectedSection(selectedTemplate().sections()[0]);
            }
        }

        function setSelectedSection(data) {
            if (data) {
                selectedSection(data);
                return selectedSection().entityAspect.loadNavigationProperty("items").then(setFirstItemSelected);
            } else {
                selectedSection();
                selectedItem();
                return false;
            }

            function setFirstItemSelected() {
                setSelectedItem(selectedSection().items()[0]);
            }
        }

        function setSelectedItem(data) {
            if (data) {
                selectedItem(data);
            } else {
                selectedItem();
            }
        }

        function bindEventToList(rootSelector, selector, callback, eventName) {
            var eName = eventName || 'click';
            $(rootSelector).on(eName, selector, function () {
                var item = ko.dataFor(this); 
                callback(item);
                return false;
            });
        }
        //#region
    }
);

<section>
<div class="row-fluid">
    <header class="span12">
        <button class="btn btn-info pull-right push-down10" data-bind="click: refresh">
            <i class="icon-refresh"></i> Refresh</button>
        <h4 class="page-header" data-bind="text: title"></h4>
    </header>
</div>

<div class="row-fluid">
    <section class="span3">
        <header class="input-append">
            <input id="newTemplateName"
                   type="text"
                   data-bind="realTimeValue: newTemplateTitle" 
                   placeholder="New template name"
                   class="input-medium" />
            <button class="btn btn-info add-on" data-bind="click: addTemplate, disable: newTemplateTitle() === ''">
                <i class="icon-plus"></i> Add</button>
        </header>

        <article>
            <table class="table table-striped table-bordered table-hover">
                <thead>
                    <tr>
                        <th>Templates</th>
                    </tr>
                </thead>
                <tbody>
                <!-- ko foreach: templates -->
                    <tr id="template-list" data-bind="css: { 'selected': $root.isTemplateSelected($data) }">
                        <td data-bind="text: title" />
                    </tr>
                <!-- /ko -->
                </tbody>
            </table>
            <span>Count: <span data-bind="text: templates().length"></span></span>
        </article>
    </section>

    <section class="span5">
        <header class="input-append">
            <input id="newSectionName"
                   type="text"
                   data-bind="realTimeValue: newSectionTitle" 
                   placeholder="New section name"
                   class="input-medium" />
            <button class="btn btn-info add-on" data-bind="click: addSection, disable: newSectionTitle() === ''">
                <i class="icon-plus"></i> Add</button>
        </header>

        <article data-bind="if: selectedTemplate">
            <table class="table table-striped table-bordered table-hover" >
                <thead>
                    <tr>
                        <th data-bind="text: 'Sections for ' + selectedTemplate().title()"></th>
                    </tr>
                </thead>
                <tbody>
                <!-- ko foreach: selectedTemplate().sections() -->
                    <tr id="section-list" data-bind="css: { 'selected': $root.isSectionSelected($data) }">
                        <td data-bind="text: title" />
                    </tr>
                <!-- /ko -->
                </tbody>
            </table>
            <span>Count: <span data-bind="text: selectedTemplate().sections().length"></span></span>
        </article>
    </section>

    <section class="span4">
        <header class="input-append">
            <input id="newItemName"
                   type="text"
                   data-bind="realTimeValue: newItemTitle" 
                   placeholder="New item name"
                   class="input-medium" />
            <button class="btn btn-info add-on" data-bind="click: addItem, disable: newItemTitle() === ''">
                <i class="icon-plus"></i> Add</button>
        </header>

        <article data-bind="if: selectedSection">
            <table class="table table-striped table-bordered table-hover">
                <thead>
                    <tr>
                        <th data-bind="text: 'Items for ' + selectedSection().title()"></th>
                    </tr>
                </thead>
                <tbody>
                <!-- ko foreach: selectedSection().items() -->
                    <tr id="item-list" data-bind="css: { 'selected': $root.isItemSelected($data) }">
                        <td data-bind="text: title" />
                    </tr>
                <!-- /ko -->
                </tbody>
            </table>
            <span>Count: <span data-bind="text: selectedSection().items().length"></span></span>
        </article>
    </section>
</div>

4

2 回答 2

0

有很多代码需要通过并尝试在想象中理解。

你似乎说它在某些时候有效。这听起来像是一个时间问题。

令人不安的一件事是setSelectedTemplate返回 false 或 promise 的异步方法(例如 )。不知道为什么不一致。但这可能不是真正的问题。

setTimeout(..., 10)您可以尝试在退出异步方法之前放置一个。看看这是否会改变行为。

如果这不能揭示问题,您将不得不将其归结为那些揭示问题的要素。

可悲的是,今天没有突然的洞察力。

6 月 3 日更新

我首先担心的是您的一些方法返回 promises 和一些返回值false。如果他们都兑现承诺,我会感觉更舒服。看Q.resolve()

其他代码让我感到困惑。例如,这种模式的许多变体中发生了什么:

功能 setSelectedItem(数据) {
            如果(数据){
                选定项(数据);
            } 别的 {
                选定项();
            }
        }

else{...}没有做任何有用的事情。它解开selectedItem属性...并丢弃该值。重点是什么?

这之间有什么区别:

thisId = ko.utils.unwrapObservable(selectedTemplate().id); // 晦涩难懂

和这个?

thisId = selectedTemplate().id(); // 清除

您不确定selectedTemplate().idKO 是否可观察?使用的唯一原因unwrapObservable是当您不确定时。

至于时间,你可以让 KO 知道它应该通过调用valueHasMutated()observable 来重新刷新绑定。例如:

函数 setFirstItemSelected() {
    setSelectedItem(selectedSection().items()[0]);
    selectedItem.valueHasMutated();
}

它可能会有所帮助。我不知道......你正在堆积一长串依赖关系,而且很难看出哪些是什么时候救助的。这将需要研究实际数据流以及代码。您不能合理地期望您的 StackOverflow 受众免费努力工作。

尝试将这种方式简化为一个显示麻烦行为的超级简单示例。

祝你好运。

于 2013-05-31T20:50:02.787 回答
0

类似的问题(相同的服务器,相同的客户端,但有时一些导航器无法在微风中工作)发生在我身上。在我看来,这可能是一个计时错误。或实体计数的创建/加载顺序。

我已经从服务器更改了这个并行异步加载实体:

return promise = Q.all([                
            getPackage(),                
            getClubPartials(null, true),
            getAddressPartials(null, true),
            getEventPartials(null, true)
            ]).then(success);

为此,请一一获取:

return getClubPartials(null, true).then(function () {
            getAddressPartials(null, true).then(function () {
                getEventPartials(null, true).then(function () {
                    return getPackage().then(success);
                })
            })
        });

我的问题消失了!

于 2013-06-02T23:06:56.870 回答