Knockout 数据绑定 bootstrap 轮播问题

Knockout data-bound bootstrap carousel issue

我在 updating/rerendering 绑定到 Knockout 可观察数组 (self.Notes) 的 bootstrap 旋转木马控制数据时遇到困难。我已经大大简化了提供的代码,因为它太多了。

页面加载时一切正常,但是当调用 self.RemoveNoteFromReviewSetup 并修改数组时,轮播完全消失。当我检查 dom 时,所有标记都在那里并且标记反映了 knockout 应该对 dom 进行的更改。然而,这是奇怪的部分,当仅更新 self.Notes 的第一个(索引 0)元素时,轮播会根据更新后的数组 self.Notes 再次呈现。当数组中的任何后续项目发生更改时,轮播就会消失。

我遗漏了很多代码,所以如果您需要查看其他内容,请告诉我。

这让我发疯,我正在考虑只触发一个页面重新加载命令,以便一切从头开始呈现。然而,这当然破坏了整个淘汰赛方法。

不胜感激。

Html:

<!-- ko foreach: Reviews -->
<div class="flashcards-controls">
<button id="btnSlideRight" class="btn btn-custom pull-right flatleft flatright" data-bind="click: NextNote"><i class="fa fa-angle-double-right"></i></button><button id="btnSlideLeft" class="flatleft flatright btn btn-custom pull-left" data-bind="click: PrevNote"><i class="fa fa-angle-double-left"></i></button>
<div class="container text-center">
    <span class="badge badge-notecounter"><span data-bind="html: NoteCounter"></span> / <span data-bind="html: Notes().length"></span></span>
</div>
</div>
<div class="container flashcards" id="flashcards-container">

<!-- Carousel
    ================================================== -->
<div id="fashcardsCarousel" class="carousel slide" data-wrap="false" data-interval="false">
    <!-- Indicators -->
    @*    <ol class="carousel-indicators" data-bind="visible: Notes().length > 1, foreach: Notes">
            <li data-target="#fashcardsCarousel" data-bind="attr: { 'data-slide-to': $index }, css: { 'active': $index() == $parent.CurrentNoteIndex() }, click: $parent.NextNote"></li>
        </ol>*@
    <!-- Slides -->
    <div class="carousel-inner" data-bind="foreach: { data: Notes, afterRender: $parent.postCarouselRenderEvent }">
        <div class="item" data-bind="css: { 'active': $index() == $parent.CurrentNoteIndex() }">
            <div class="row">
                <div class="col-xs-12">
                    <div class="panel panel-default">
                        <div class="panel-heading clearfix">
                            <div class="btn-group pull-right">
                                <button data-bind="click: ToggleTagEditor, attr: { id: 'btnTagEditor' + GlobalClientId() }" class="btn btn-sm btn-custom">
                                    <i class="fa fa-tags fa-fw"></i>
                                </button>
                                <button class="btn btn-sm btn-custom">
                                    <i class="fa fa-book fa-fw"></i>
                                </button>
                                <button data-bind="click: ShowInfo, attr: { id: 'btnInfo' + GlobalClientId() }" class="btn btn-sm btn-custom">
                                    <i class="fa fa-info fa-fw"></i>
                                </button>
                            </div>
                            <h5><strong data-bind="    text: Title"></strong></h5>
                        </div>
                        <div class="panel-body" data-bind="html: Content, visible: Content">
                        </div>
                        <div class="panel-body" data-bind="style: { display: IsLoading() == false ? 'none' : '' }">
                            <div class="text-center text-muted">
                                <i class="fa fa-refresh fa-spin"></i><br />
                                Loading note content
                            </div>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>
    <!-- Controls -->
    <!-- ko if: Notes().length > 1 -->
    @*<a class="left carousel-control"We added support for all common image types. In other words, pictures will now show up when you review your @evernote notes :-) href="#fashcardsCarousel" data-slide="prev" data-bind="click: PrevNote"><span class="glyphicon glyphicon-chevron-left"></span></a>
        <a class="right carousel-control" href="#fashcardsCarousel" data-slide="next" data-bind="click: NextNote"><span class="glyphicon glyphicon-chevron-right"></span></a>*@
    <!-- /ko -->
</div>
<!-- /.carousel -->

</div>
<div id="popoverStagingArea" data-bind="foreach: { data: Notes, afterRender: $parent.postTagPopoversRenderEvent }" style="display: none;">
<div data-bind="attr: { id: 'tagEditor' + GlobalClientId() }">
    <select multiple></select>
    <p class="text-muted text-center" data-bind="visible: TagsUpdating" style="margin: 0;"><i class="fa fa-refresh fa-spin"></i></p>
    <div data-bind="attr: { id: 'tagRemovalConfirmation' + GlobalClientId() }" style="display: none">
        <button class="btn btn-sm btn-custom btn-block" data-bind="click: RemoveTagPendingRemoval">Only remove tag from note</button>
        <button class="btn btn-sm btn-custom-warning btn-block" data-bind="click: RemoveTagAndDeleteNoteFromReviewSetup">Also remove note from filter</button>
        <button class="btn btn-sm btn-default btn-block" data-bind="click: CancelTagRemoval">Cancel</button>
        <p class="text-center" style="margin-top: 3px;">(<a data-bind="click: TagRemovalExplainer().slideDown('fast')" href="#">what is this?</a>)</p>
        <div data-bind="attr: { id: 'tagRemovalExplainer' + GlobalClientId() }" style="display: none;">
            <p>
                When you setup filter <strong data-bind="text: $parent.SetupName"></strong> you selected tag <span class="label label-success" data-bind="text: TagPendingRemoval().Name"></span> as part of the search criteria. We thought you might want to have the option to either just remove the tag from the note and save it to Evernote, or also delete this note from any subsequent reviews that are part of <strong data-bind="text: $parent.SetupName"></strong>.
            </p>
            <p class="text-center">
                (<a data-bind="click: TagRemovalExplainer().slideUp('fast')" href="#">hide</a>)
            </p>
        </div>
    </div>
</div>
</div>
<!-- /ko -->

JS:

        function Review(reviewItem) {
        //code removed//
            self.RemoveNoteFromReviewSetup = function (note) {                   
                var apiRemoveNoteString = "/api/review/" + self.Guid + "/note/" + note.Guid;
                $.ajaxq("notes", {
                    type: "DELETE",
                    url: apiRemoveNoteString,
                    headers: { "Authorization": "Token " + $.cookie("rat") },
                    success: function (replacementNote) {
                        var originalNoteIdx = self.Notes.indexOf(note);
                        self.Notes.replace(self.Notes()[originalNoteIdx], new Note(replacementNote, self));
                        self.Notes()[originalNoteIdx].GetContent();
                        console.log(self.Notes()[originalNoteIdx]);
                        self.Notes()[originalNoteIdx].InitTagEditor();
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        alert("We could not remove a note for you at this time. Please try again later.");
                    },
                    complete: function () {

                    },
                    dataType: "json"
                });
            }
        }

好的,我明白了。这与淘汰赛无关。在仔细查看重新呈现的旋转木马标记后,我注意到没有一张幻灯片具有 class "active",Bootstrap 用于确定当前幻灯片。添加此 class manually/programmatically 后,如下所示:

var slide = $("#yourselector")[slideIndex];
$(slide).addClass("active");

整个轮播再次可见,所有行为都完好无损:-)。

这是我生命中的另外 1.5 天,我不会回来,但我会安慰自己,告诉自己我已经获得了关于 Knockout 和 Bootstrap 的内部库工作的宝贵知识...