Twitter bootstrap 淘汰observableArray包含新项,但UI不';t更新

Twitter bootstrap 淘汰observableArray包含新项,但UI不';t更新,twitter-bootstrap,knockout.js,asp.net-mvc-5,Twitter Bootstrap,Knockout.js,Asp.net Mvc 5,在使用bootstrap 3模式时,是否有一个最佳实践(在这一点上,我将采取一个相当不错的实践)来为淘汰赛ObservalArray添加一个新项目?目前,我正在使用一个深度嵌套的数据结构,我能够将数据添加到某个点。我有一个引导模式,它绑定到视图模型上的“selectedItem”。项目本身是observableArray的成员。当我想编辑现有项目,但尝试添加新项目时失败时,此操作有效。我遵循的过程是新建我要添加的对象(所有可观察属性),设置viewModel的SelectedItem属性=新对象

在使用bootstrap 3模式时,是否有一个最佳实践(在这一点上,我将采取一个相当不错的实践)来为淘汰赛ObservalArray添加一个新项目?目前,我正在使用一个深度嵌套的数据结构,我能够将数据添加到某个点。我有一个引导模式,它绑定到视图模型上的“selectedItem”。项目本身是observableArray的成员。当我想编辑现有项目,但尝试添加新项目时失败时,此操作有效。我遵循的过程是新建我要添加的对象(所有可观察属性),设置viewModel的SelectedItem属性=新对象,以便模态中的绑定工作,然后将新项推送到observableArray并显示模态。在模式中,我为一些属性添加了值,包括在新项上填充三个ObservableArray(此时也会失败,可能是同一个问题),然后关闭模式。奇怪的是,添加新项目只会在视觉上失败。当我将viewmodel保存回服务器并重新加载页面时,我刚才添加的项目现在就在那里并正确呈现。我假设我正在破坏我正在添加对象的observableArray,但我在想怎么做。我试着把它做成一把小提琴,但由于它的复杂性,我最终不得不把它简化到可以使用的程度,或者我不再解释它的行为。欢迎对Ryan Niemeyer建议之外的故障排除提出任何建议!当我使用pre标记时,我可以看到添加到ObservalArray的新数据,但UI没有响应。男人该怎么办

编辑:页面的相关部分发布在下面

视图:

@*style=“@showSchedule”>*@
计划时间表
拯救一切
添加
星期
单击“添加周”按钮开始将训练添加到您的计划中

周 白天 @**@ &时代; 训练 选择训练类型 @EnumDropDownListFor(m=>m.workUtenum,新的{@class=“form control”,data_bind=“value:Type”}) 热身 基于距离的 基于时间的 以“15分钟”(Model.IsRPE?)的形式输入一个新的基于时间的间隔,添加到此训练的热身部分,RPE5“轻松” @EnumDropDownListFor(m=>m.TimeUnits,新的{@class=“form control”,id=“timeUnitWU”}) @(Model.IsRPE?Html.EnumDropDownListFor(m=>m.RPEUnitsEnum,new{@class=“form control”,id=“rpeunitimewu”}):Html.EnumDropDownListFor(m=>m.heartRateZoneNum,new{@class=“form control”,id=“hrUnitTimeWU”})) 拯救 取消 以“10英里”(Model.IsRPE?)的风格输入新的基于距离的间隔时间,添加到此训练的热身部分,RPE5'“轻松” @EnumDropDownListFor(m=>m.DistanceUnitsEnum,新的{@class=“form control”,id=“distUnitWU”}) @(Model.IsRPE?Html.EnumDropDownListFor(m=>m.RPEUnitsEnum,new{@class=“form control”,id=“rpeUnitDistWU”}):Html.EnumDropDownListFor(m=>m.heartRateZoneNum,new{@class=“form control”,id=“hrUnitDistWU”})) 拯救 取消
  • @如果(基于模型的){ } 否则{ }
主要 这里需要一些内容。
冷静下来 这里需要一些内容。
    <div id="SchedulePanel" class="panel panel-default" > @*style="@showSchedule">*@ <div class="panel-heading"> <h3>Plan Schedule</h3> <a class="btn btn-primary btn-sm" data-bind="visible: scheduleDirty" style="margin-left: 10px; margin-bottom: 7px;">Save All</a> <div> Add <input id="numWeeks" type="text" class="form-control" placeholder="number" value="1" /> <a id="btnAddWeek" class="btn btn-primary" data-bind="click: addWeek">Week(s)</a> </div> </div> <div class="panel-body"> <div id="ScheduledInstructions"> Click the Add Week button to get started adding workouts to your schedule </div> <hr /> <div id="weeks" data-bind="foreach: Schedule"> <div style="margin-left: auto; margin-right: auto;"> <h4 style="display: inline-block;" >Week <span data-bind="text: Name"></span></h4> <span class="glyphicon glyphicon-floppy-disk" style="color: red;" data-bind="visible: IsDirty"></span>&nbsp; <span class="glyphicon glyphicon-share" data-bind="visible: !IsDirty()" title="Click to copy this week and add to the end of the schedule"></span> <div data-bind="foreach: Days "> <div class="dayBuilder"> Day <span data-bind="text: DayNumber"></span> @*<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>*@ <div data-bind="foreach: Workouts "> <div class="workout" title="Edit Workout" style="cursor: pointer;"> <span data-bind="text: Type"></span> <span class="glyphicon glyphicon-remove pull-right removeWorkout" title="Remove Workout"></span> </div> </div> <div class="newWorkout addWorkout"> <span class="glyphicon glyphicon-plus" title="Add Workout"></span> </div> </div> </div> </div> </div> </div> </div> <div class="modal fade" id="newWorkout" tabindex="-1" role="dialog" aria-labelledby="newWorkoutLabel" aria-hidden="true" data-bind="with: $root.SelectedWorkout"> <div class="modal-dialog modal-wide"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <h3 class="modal-title" id="newWorkoutLabel"><span data-bind="text: Type"></span> Workout</h3> </div> <div class="modal-body"> <form > <div class="form-group"> <label class="control-label">Select workout type</label> @Html.EnumDropDownListFor(m => m.WorkoutEnum, new { @class="form-control", data_bind="value: Type"}) </div> <div class="well well-sm"> <h3 class="workoutHeader">Warmup</h3><span class="glyphicon glyphicon-plus-sign" data-section="warmup" title="Add Interval"></span> <div id="newWarmupInterval" style="display: none;"> <label class="radio-inline"> <input type="radio" name="WUDistance" id="WUDistanceBasedT" class="wuDistance" value="true" checked> Distance Based </label> <label class="radio-inline"> <input type="radio" name="WUDistance" id="WUDistanceBasedF" class="wuDistance" value="false"> Time Based </label> <div id="timeBasedWU" style="display: none;" class="tp-intervalInputContainer form-inline"> <div style="margin-bottom:10px;">Enter a new time based interval to be added to the Warmup portion of this workout, in the style of '15 minutes @(Model.IsRPE ? "at RPE5'" : "easy'") </div> <input type="text" id="timeValueWU" class="form-control" placeholder="Enter Time" style="width: 60px;"/> @Html.EnumDropDownListFor(m => m.TimeUnits, new { @class="form-control", id="timeUnitWU"}) @(Model.IsRPE ? Html.EnumDropDownListFor( m => m.RPEUnitsEnum, new {@class = "form-control", id="rpeUnitTimeWU"} ) : Html.EnumDropDownListFor( m => m.HeartRateZoneEnum, new {@class = "form-control", id="hrUnitTimeWU"} )) <div id="btnSaveWU" class="btn btn-success btn-sm">Save</div> <div id="btnCancelWU" class="btn btn-danger btn-sm">Cancel</div> </div> <div id="distanceBasedWU" class="tp-intervalInputContainer form-inline"> <div style="margin-bottom:10px;">Enter a new distance based interval to be added to the Warmup portion of this workout, in the style of '10 miles @(Model.IsRPE ? "at RPE5'" : "easy'") </div> <input type="text" id="distValueWU" class="form-control" placeholder="Enter Distance" style="width: 60px;"/> @Html.EnumDropDownListFor(m => m.DistanceUnitsEnum, new { @class="form-control", id="distUnitWU"}) @(Model.IsRPE ? Html.EnumDropDownListFor( m => m.RPEUnitsEnum, new {@class = "form-control", id="rpeUnitDistWU"} ) : Html.EnumDropDownListFor( m => m.HeartRateZoneEnum, new {@class = "form-control", id="hrUnitDistWU"} )) <div id="btnSaveWU" class="btn btn-success btn-sm btnSaveWU">Save</div> <div id="btnCancelWU" class="btn btn-danger btn-sm">Cancel</div> </div> <hr /> </div> <ul id="WarmupIntervals" data-bind="template: { name: 'WorkoutTemplate', foreach: WarmUp }"> <li> @if ( Model.IsTimeBased ) { <span data-bind="Text: TimeValue"></span> <span data-bind="Text: TimeUnit"></span> <span data-bind="Text: RPEValue, visible: $root.IsRPE"></span> <span data-bind="Text: HRValue, visible: !$root.IsRPE()"></span> } else { <span data-bind="Text: DistanceValue"></span> <span data-bind="Text: DistanceUnit"></span> <span data-bind="Text: RPEValue, visible: $root.IsRPE"></span> <span data-bind="Text: HRValue, visible: !$root.IsRPE()"></span> } </li> </ul> </div> <div class="well well-sm"> <h3 class="workoutHeader">Main</h3><span class="glyphicon glyphicon-plus-sign" data-section="main" title="Add Interval"></span> <div id="newMainInterval" style="display: none;"> Need to get some content in here. </div> <hr /> <ul id="MainIntervals" data-bind="template: { name: 'WorkoutTemplate', foreach: Main }"> </ul> </div> <div class="well well-sm"> <h3 class="workoutHeader">Cool Down</h3><span class="glyphicon glyphicon-plus-sign" data-section="cooldown" title="Add Interval"></span> <div id="newCooldownInterval" style="display: none;"> Need to get some content in here. </div> <hr /> <ul id="CoolDownIntervals" data-bind="template: { name: 'WorkoutTemplate', foreach: CoolDown }"> </ul> </div> </form> </div> <div class="modal-footer"> @*<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>*@ <button type="button" class="btn btn-primary saveWorkout">Save</button> </div> </div> </div> </div>
var trainingPlan;
var schedule;
var workoutNumber = 0;

$(document).ready(function() {

    // the plan object is a training plan with a few properties that describe the plan
    // along with a list of weeks that contain the workouts.  The weeks are broken down into days,
    // each day has a list of workouts, each workout has a list of intervals for its warm up, main and cool down
    // properties. 

    var planID = { id: $('#ID').val() };

    var Week = function() {
        var self = this;
        self.ID = '';
        self.IsDirty = ko.observable(false);
        self.PlanID = ko.observable();
        self.StartDate = ko.observable();
        self.EndDate = ko.observable();
        self.Name = ko.observable();
        self.Days = ko.observableArray([]); // a list of day objects
    };
    var Day = function() {
        var self = this;
        self.ID = '';
        self.DayNumber = ko.observable();
        self.TodaysDate = ko.observable();
        self.Name = ko.observable();
        self.Workouts = ko.observableArray([]); // a list of workout objects
    };
    var Workout = function() {
        var self = this;
        self.ID = '';
        self.Type = ko.observable(); //need to figure out what/how to handle enums
        self.WarmUp = ko.observableArray([]); // a list of intervals
        self.Main = ko.observableArray([]); // a list of intervals
        self.CoolDown = ko.observableArray([]); // a list of intervals
        self.Status = ko.observable();
        self.Completed = ko.observable();
    };
    var Interval = function() {
        var self = this;
        self.IsTimeBased = ko.computed(function() {
            return !self.IsDistanceBased;
        });
        self.IsDistanceBased = ko.observable();
        self.TimeValue = ko.observable();
        self.TimeUnit = ko.observable();
        self.RPEUnits = ko.observable();
        self.HeartRateZone = ko.observable();
        self.Description = ko.observable();
        self.DistanceValue = ko.observable();
        self.DistanceUnit = ko.observable();
    };

    $.getJSON('/PlanBuilder/GetPlanJson', planID, function(model) {

        // map model into a knockout viewModel
        trainingPlan = ko.mapping.fromJSON(model.Message);

        // setup regular and computed observables
        trainingPlan.SelectedWorkout = ko.observable();
        trainingPlan.SelectedInterval = ko.observable();
        trainingPlan.SelectedDay = ko.observable();
        trainingPlan.Weeks = ko.computed(function() {
            return trainingPlan.Schedule().length;
        });
        trainingPlan.TotalWeeks = ko.computed(function() {
            trainingPlan.Weeks = trainingPlan.Schedule.length || 0;
            return trainingPlan.Schedule.length || 0;
        }, this);
        trainingPlan.TotalDays = ko.computed(function() {
            return trainingPlan.Schedule.length * 7 || 0;
        }, this);

        // setup $root methods
        trainingPlan.addWeek = function() {
            var numberOfWeeks = $('#numWeeks').val();

            if (numberOfWeeks < 1)
                numberOfWeeks = 1;

            for (var w = 1; w <= numberOfWeeks; w++) {

                var wk = new Week();
                wk.IsDirty(true);
                var wkNum = this.Schedule().length + 1;
                var firstDay = wkNum > 1 ? (wkNum - 1) * 7 + 1 : 1; // day number of first day of new week

                wk.Name = wkNum;
                wk.PlanID = $('#ID').val();

                for (var i = firstDay; i < firstDay + 7; i++) {
                    var dy = new Day();
                    dy.DayNumber = i;
                    wk.Days.push(dy);
                }

                //var summ = new day();
                //summ.DayNumber = "Summary";
                //wk.Days.push(summ);

                this.Schedule.push(wk);
            }

            // update # of weeks in training plan
            trainingPlan.Weeks = this.Schedule().length + 1;
        };
        trainingPlan.addWorkout = function() {
            var wrk = new Workout();
            trainingPlan.SelectedWorkout(wrk);
            $('#newWorkout').modal('show');
        };
        trainingPlan.saveWorkout = function (wrk) {
            trainingPlan.SelectedDay().Workouts().push(wrk);
            $('#newWorkout').modal('hide');
        };
        trainingPlan.addInterval = function (workoutSection) {
            var intr = new Interval();
            trainingPlan.SelectedInterval(intr);

            var container = $(this).parent();
            intr.IsDistanceBased($('#WUDistanceBasedT').is(':checked'));

            if (intr.IsDistanceBased()) {
                intr.DistanceUnit(container.find('#distUnitWU').val());
                intr.DistanceValue(container.find('#distValueWU').val());
                if (!trainingPlan.IsRPE()) {
                    intr.HeartRateZone(container.find('#hrUnitDistWU').val());
                } else {
                    intr.RPEUnits(container.find('#rpeUnitDistWU').val());
                }
            } else {
                intr.TimeUnit(container.find('#timeUnitWU').val());
                intr.TimeValue(container.find('#timeValueWU').val());
                if (!trainingPlan.IsRPE()) {
                    intr.HeartRateZone(container.find('#hrUnitTimeWU').val());
                } else {
                    intr.RPEUnits(container.find('#rpeUnitTimeWU').val());
                }
            }

            trainingPlan.SelectedWorkout().WarmUp().push(intr);
        };
        trainingPlan.copyWeek = function(index) {
            var weeks = trainingPlan.Schedule().slice(index, index + 1);
            var week = weeks[0];

            var newWeek = new Week();
            var weekNum = parseInt(trainingPlan.Schedule().length) + 1;

            newWeek.EndDate = ko.observable(ko.utils.unwrapObservable(week.EndDate));
            newWeek.IsDirty = ko.observable(true);
            newWeek.Name = ko.observable(weekNum);
            newWeek.PlanID = ko.observable(ko.utils.unwrapObservable(week.PlanID));
            newWeek.StartDate = ko.observable(ko.utils.unwrapObservable(week.StartDate));

            var dayNumber = 1;

            week.Days().forEach(function(day, dayIndex) {
                var newDay = new Day();
                var daysBase = trainingPlan.Schedule().length * 7;
                var currentDay = daysBase + dayIndex + 1;

                newDay.Name = ko.observable(ko.utils.unwrapObservable(day.Name));
                newDay.DayNumber = ko.observable(currentDay);
                newDay.Name = ko.observable("Day " + currentDay);

                day.Workouts().forEach(function(workout) {
                    var newWorkout = new Workout();
                    newWorkout.Completed = false;

                    workout.WarmUp().forEach(function(interval) {
                        var newInterval = new Interval();
                        newInterval.Description = ko.observable(ko.utils.unwrapObservable(interval.Description));
                    });

                    newWorkout.Type = ko.observable(ko.utils.unwrapObservable(workout.Type));

                    newDay.Workouts.push(newWorkout);
                });

                newWeek.Days.push(newDay);
            });

            trainingPlan.Schedule.push(newWeek);
        };
        trainingPlan.scheduleDirty = ko.computed(function() {
            var dirty = false;

            for (var i = 0; i < trainingPlan.Schedule().length; i++) {
                if (trainingPlan.Schedule()[i].IsDirty())
                    dirty = true;
            }
            ;
            return dirty;
        }, this);

        // lets get it on
        ko.applyBindings(trainingPlan);
    });

    $('#weeks').on('click', '.removeWorkout', function() {
        var context = ko.contextFor(this);
        var workouts = context.$parent.Workouts;
        workouts.remove(context.$data);
        context.$parents[1].IsDirty(true);
    });

    $('#weeks').on('click', '.addWorkout', function() {
        var context = ko.contextFor(this);
        context.$parent.IsDirty(true);
        var wrk = new Workout();
        wrk.Type("Flying");
        //trainingPlan.SelectedWorkout(wrk);
        context.$data.Workouts().push(wrk);
        //$('#newWorkout').modal('show');
    });

    $('#newWorkout').on('click', '.saveWorkout', function () {
        $('#newWorkout').modal('hide');
    });

    $('#weeks').on('click', '.workout', function() {
        var context = ko.contextFor(this);
        context.$root.SelectedWorkout(context.$data);
        $('#newWorkout').modal('show');
    });


    // Week functions
    $('#weeks').on('click', 'span.glyphicon-floppy-disk', function() {
        var context = ko.contextFor(this);
        var weekData = ko.mapping.toJSON(context.$data);

        var $btn = $(this);

        // save week
        $.ajax({
            url: "/PlanBuilder/SaveWeek",
            type: "POST",
            data: weekData,
            contentType: 'application/json',
            error: function(data) {
                console.log(data.responseText);
                $btn.find('span.glyphicon-floppy-disk').addClass('btn-danger');
            },
            success: function(data) {
                if (data.IsError) {
                    $btn.find('span.glyphicon-floppy-disk').addClass('btn-danger');
                    $btn.text('Error');
                } else {
                    $btn.removeClass('glyphicon-floppy-disk').addClass('glyphicon-floppy-saved');
                    $btn.css('color', 'green');
                    // add newly generated ID to knockout week object
                    context.$data.ID = data.Message;
                    context.$data.IsDirty(false);
                    $btn.css('color', 'red');

                    // after 3 seconds fadeOut save button and reset the glyphicon classes
                    setTimeout(function() {
                        $btn.removeClass('glyphicon-floppy-saved').addClass('glyphicon-floppy-disk');
                    }, 3000);
                }
            }
        });

    });
    $('#weeks').on('click', 'span.glyphicon-share', function() {
        var context = ko.contextFor(this);
        var index = context.$index();
        context.$root.copyWeek(index);
    });

     //Workout functions
    $('#newWorkout').on('click', 'span.glyphicon-plus-sign', function () {

        var section = $(this).attr('data-section');

        if (section === 'warmup') {
           $('#newWarmupInterval').slideToggle();
        }

        if (section === 'main') {
            $('#newMainInterval').slideToggle();
        }

        if (section === 'cooldown') {
            $('#newCoolDownInterval').slideToggle();
        }


    });

    $('#newWorkout').on('click', '.wuDistance', function () {
        if ($(this).attr('id') == 'WUDistanceBasedT') {
            $('#timeBasedWU').fadeOut(function () {
                $('#distanceBasedWU').fadeIn();
            });
        } else {
            $('#distanceBasedWU').fadeOut(function () {
                $('#timeBasedWU').fadeIn();
            });
        }
    });

    $('#newWorkout').on('click', '.btnSaveWU', function () {
        var context = ko.contextFor(this);



    });

    $('#btnSaveM').click(function () { });
    $('#btnSaveCD').click(function () { });



    $('#btnSavePlan').click(function() {

        // if form is valid, let's save this shiz
        if ($('#NewPlanForm').valid()) {

            var mappingOptions = {
                'ignore': ["addWorkout", "removeWorkout", "Schedule"]
            };

            var datum = ko.mapping.toJSON(trainingPlan, mappingOptions);

            var $btn = $(this);

            // save plan
            $.ajax({
                url: "/PlanBuilder/SavePlan",
                type: "POST",
                data: datum,
                contentType: 'application/json',
                error: function(data) {
                    console.log(data.responseText);
                    $btn.firstChild().addClass('btn-danger');
                },
                success: function(data) {
                    if (data.IsError) {
                        $btn.firstChild().addClass('btn-danger');
                        $btn.text('Error');
                    } else {
                        $btn.addClass('btn-success');
                        $btn.text('Success');
                        $('#ID').val(data.Message);

                        $('#SchedulePanel').show();

                        setTimeout(function() {
                            $btn.removeClass('btn-success').removeClass('btn-danger').addClass('btn-primary');
                        }, 4000);
                    }
                }
            });
        }
    });
});
    trainingPlan.saveWorkout = function (wrk) {
            //trainingPlan.SelectedDay().Workouts().push(wrk);
            trainingPlan.SelectedDay().Workouts.push(wrk);
            $('#newWorkout').modal('hide');