在淘汰js重新绑定视图模型后,UI分解

时间:2012-04-18 08:34:01

标签: jquery-ui knockout.js

正如它在标题中所写:当我将一个对象添加到ko.observableArray类型的属性时,knockoutjs会更新视图,这对我来说很好。

问题是视图更新后左侧的视图边距约为100px。令人惊讶的是,当我通过鼠标左键单击选择整个视图时,视图修复和左边距影响消失。顺便说一句,这发生在IE 8中。

此外,我在Chrome中尝试了同样的事情:不知何故左边距应用于视图;相反,这次选择视图并不像我在前面的阶段中描述的那样解决问题。

这可能与jquery有关,但我真的不确定。

你可能会看到下面的视图和视图模型(它有点复杂。)我还放了与问题相关的截图(在IE8上)。

在我点击Enter键之前:“http://img560.imageshack.us/img560/6598/minqbeforeenter.png”

点击Enter键后:“http://img221.imageshack.us/img221/1905/minqafterenter.png”

用鼠标左键选择后:“http://img689.imageshack.us/img689/882/minqafterselect.png”

我正在使用的脚本(当我按下文本框上的Enter键时,AdvancedSearchControlViewModel的searchValue_keydown方法正常工作)

function SearchControlViewModel(context) {
    var normalizeSelectItems = function (selectItems) {
        var normalizedItems = [];

        if (!(selectItems && $.isArray(selectItems) && selectItems.length > 0))
            return normalizedItems;

        for (var index = 0; index < selectItems.length; index++) {
            var item = selectItems[index];
            if (item.Items && $.isArray(item.Items)) {
                if (item.Items.length <= 0)
                    continue;
                var group = { Value: item.Value, Description: item.Description };
                for (var index2 = 0; index2 < item.Items.length; index2++) {
                    var subItem = item.Items[index2];
                    normalizedItems.push(new SelectItem({ Value: subItem.Value, Description: subItem.Description, Group: group }));
                }
            } else {
                normalizedItems.push(new SelectItem({ Value: item.Value, Description: item.Description, Group: { Description: ''} }));
            }
        }

        return normalizedItems;
    };
    var normalizedSelectItems = normalizeSelectItems(context.SelectItems);
    context.SelectedValues = normalizeSelectItems(context.SelectedValues);
    $.each(context.SelectedValues, function (selectedIndex, selectedItem) {
        var selItem;
        $.each(normalizedSelectItems, function (selectIndex, selectItem) {
            if (selectItem.Value === selectedItem.Value) {
                selItem = selectItem;
                return false;
            }
        });
        if (selItem) {
            context.SelectedValues[selectedIndex] = selItem;
        }
    });

    var self = this;

    self.allOptions = 'All';

    self.selectItems = ko.observableArray(normalizedSelectItems);

    self.searchContext = new SearchContext(context);

    self.DropDownCheckList = function (elements) {

        $('select', elements).dropdownchecklist({
            firstItemChecksAll: true,
            forceMultiple: true,
            onItemClick: function (checkBox, originalSelect) {
                var selectedItem;
                for (var selectedItemIndex = 0; selectedItemIndex < originalSelect.options.length; selectedItemIndex++) {
                    if (originalSelect.options[selectedItemIndex].value === checkBox.val()) {
                        selectedItem = originalSelect.options[selectedItemIndex];
                        break;
                    }
                }
                selectedItem.selected = checkBox.attr('checked') === 'checked';
            },
            maxDropHeight: 300,
            width: 250,
            emptyText: 'Please Select...'
            /*,
            textFormatFunction: function (options) {
            return 'Filtre';
            }*/
        });
    };

    self.Validate = function () {
        var check;

        check = self.searchContext.searchValue();
        if (check === null || check === undefined ||
            check === '')
            return false;

        check = self.searchContext.selectedItems();
        if (check === null || check === undefined ||
            !$.isArray(check) || check.length === 0)
            return false;

        var hasAtLeastOneItem = false;
        $.each(check, function (index) {
            if (check[index]) {
                hasAtLeastOneItem = true;
                return false;
            }
        });

        return hasAtLeastOneItem;
    };

    self.GetConvertedJSONObj = function () {
        var obj = {
            SelectedValues: [],
            SearchValue: self.searchContext.searchValue()
        };

        var selectedItems = self.searchContext.selectedItems();
        if (selectedItems) {
            for (var i = 0; i < selectedItems.length; i++) {
                var selItem = selectedItems[i];
                if (selItem === null || selItem === undefined)
                    continue;

                var pushArray;
                if (selItem.Group.Description !== '') {
                    var group = undefined;
                    $.each(obj.SelectedValues, function (index, item) {
                        if (item.Items && $.isArray(item.Items)) {
                            if (item.Value === selItem.Group.Value) {
                                group = item;
                                return false;
                            }
                        }
                    });
                    if (group === undefined) {
                        pushArray = [];
                        obj.SelectedValues.push({
                            Value: selItem.Group.Value,
                            Items: pushArray
                        });
                    } else {
                        pushArray = group.Items;
                    }

                } else {
                    pushArray = obj.SelectedValues;
                }

                pushArray.push({
                    Value: selItem.Value
                });
            }
        }

        return obj;
    };
    self.ConvertToJSON = function () {
        var obj = self.GetConvertedJSONObj();

        return JSON.stringify(obj);
    };
}
function SearchContext(context) {
    var self = this;

    self.selectedItems = ko.observableArray(context.SelectedValues);

    self.searchValue = ko.observable(context.SearchValue);
}
function SelectItem(context) {
    var self = this;

    self.Value = context.Value;
    self.Text = context.Description;
    self.Group = context.Group;
}

function SearchFilter(context) {
    var self = this;

    var getSelectedItemsDescription = function (selectedItems) {
        var text = '';
        var selectedValues = context.template.searchContext.selectedItems();
        for (var i = 0; i < selectedValues.length; i++) {
            var selectedValue = selectedValues[i];

            text += selectedValue.Text;
            if (i + 1 < selectedValues.length)
                text += ', ';
        }

        return text;
    };
    self.selectedValuesDescription = getSelectedItemsDescription(
        context.template.searchContext.selectedItems()
    );

    self.searchValueText = context.template.searchContext.searchValue();

    self.filterCombineType = context.defaultCombineType.key;

    self.GetConvertedJSONObj = function () {
        var obj = context.template.GetConvertedJSONObj();
        obj.CombineType = self.filterCombineType;

        return obj;
    };
    self.ConvertToJSON = function () {
        var obj = self.GetConvertedJSONObj();

        return JSON.stringify(obj);
    };
}
function AdvancedSearchControlViewModel(context) {
    var self = this;

    self.SearchControlViewModelTemplate = ko.observable(new SearchControlViewModel(context.data.TemplateSearch));

    // key , value
    self.SearchFilterCombineTypes = [];
    for (var ct in context.data.SearchCombineTypes) {
        self.SearchFilterCombineTypes.push({ key: ct, value: context.data.SearchCombineTypes[ct] });
    }

    self.searchFilters = [];
    if ($.isArray(context.data.Searchs) && context.data.Searchs.length > 0) {
        for (var i = 0; i < context.data.Searchs.length; i++) {
            var search = context.data.Searchs[i];
            var searchFilter;
            $.each(self.SearchFilterCombineTypes, function (index, element) {
                if (element.key === search.CombineType.toString()) {
                    searchFilter = element;
                    return false;
                }
            });

            search.SelectItems = context.data.TemplateSearch.SelectItems;

            self.searchFilters.push(
                new SearchFilter({ template: new SearchControlViewModel(search), defaultCombineType: searchFilter })
            );
        }
    }
    self.searchFilters = ko.observableArray(self.searchFilters);

    self.addSearchFilter = function () {
        if (self.SearchControlViewModelTemplate().Validate()) {
            self.searchFilters.push(new SearchFilter({ template: self.SearchControlViewModelTemplate(), defaultCombineType: self.SearchFilterCombineTypes[0] }));
            self.SearchControlViewModelTemplate(new SearchControlViewModel(context.data.TemplateSearch));
        }
    };

    self.searchValue_keydown = function (sender, event) {
        if (event.keyCode === $.ui.keyCode.ENTER) {
            // Code block to prevent submit
            /*var fnDontSubmit = function () {
            $(this).unbind('submit', fnDontSubmit);
            return false;
            };
            $(event.currentTarget.form).bind('submit', fnDontSubmit);*/

            self.SearchControlViewModelTemplate().searchContext.searchValue(event.srcElement.value);
            self.addSearchFilter();
        }
        return true;
    };

    self.iButton = function (elements, bindings) {
        var boundSearchFilter = bindings;

        var elem = $(elements).filter('[type="checkbox"]');

        if (boundSearchFilter.filterCombineType === self.SearchFilterCombineTypes[1].key)
            elem.attr('checked', 'checked');
        else
            elem.removeAttr('checked');

        elem.iButton({
            labelOn: self.SearchFilterCombineTypes[1].value,
            labelOff: self.SearchFilterCombineTypes[0].value,
            change: function (sender, event) {
                var selectedVal = self.SearchFilterCombineTypes[sender.is(':checked') ? 1 : 0];
                boundSearchFilter.filterCombineType = selectedVal.key;
                // if you remove this line and 
                // make filterCombineType an observable 
                // the iButton animation wont work. So We're binding the checkbox manually.
                ko.applyBindings(self, $('#' + context.valueId)[0]);
            }
        });

        var isLastElem = $(self.searchFilters()).last()[0] === boundSearchFilter;
        if (isLastElem)
            elem.iButton('disable');
    };

    self.ConvertToJSON = function () {
        var jsonFilterArr = [];
        for (var i = 0; i < self.searchFilters().length; i++) {
            var searchFilter = self.searchFilters()[i];
            jsonFilterArr.push(searchFilter.GetConvertedJSONObj());
        }

        return JSON.stringify({
            Searchs: jsonFilterArr
        });
    };
}

if (!$.data(document, "BindKnockoutjsControl")) {
    $.data(document, "BindKnockoutjsControl", function (context) {
        switch (context.controlType) {
            case 'SearchControl':

                var viewModel = new SearchControlViewModel(context.dataForViewModel);

                ko.applyBindings(viewModel, $('#' + context.controlDisplayIdToBind)[0]);
                ko.applyBindings(viewModel, $('#' + context.controlValueIdToBind)[0]);

                break;
            case "AdvancedSearchControl":

                var viewModel = new AdvancedSearchControlViewModel({ data: context.dataForViewModel, valueId: context.controlValueIdToBind });

                ko.applyBindings(viewModel, $('#' + context.controlDisplayIdToBind)[0]);
                ko.applyBindings(viewModel, $('#' + context.controlValueIdToBind)[0]);

                break;
            default:
                throw 'Unknown controlType: ' + context.controlType;
                break;
        }
    });
}

视图

<script id="AdvancedSearchControl_View" type="text/html">
    <table style="display:inline-block; width: 25%;">
        <thead data-bind="template: { name: 'SearchControl_View' }">
        </thead>
        <tbody data-bind="template: { name: 'AdvancedSearchControlItem_View', foreach: $data.searchFilters }">
        </tbody>
    </table>
</script>
<script id="SearchControl_View" type="text/html">
    <tr>
        <td>
            @Html.LabelFor(model => model.AdvancedSearchViewModel, new { @class = "SearchControlCaption" })
        </td>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <td style="padding-top:9px;">
            <select multiple="multiple" data-bind="options: SearchControlViewModelTemplate().selectItems, selectedOptions: SearchControlViewModelTemplate().searchContext.selectedItems, optionsText: 'Text', optionsGroup: function(item){return item.Group !== '' ? item.Group.Description : ''}, optionsCaption: SearchControlViewModelTemplate().allOptions"></select>
        </td>
        <td>
            <input type="text" class="SearchValue" data-bind="
                    value: SearchControlViewModelTemplate().searchContext.searchValue,
                    event: {
                        keydown: searchValue_keydown
                    }
                "
            />
        </td>
        <td>
            <input type="image" src="@Url.Content("~/Content/Images/" + Models.ResourceFiles.Resources._SearchButtonFileName)" 
                data-bind="enable: $data.searchFilters().length > 0" />
        </td>
    </tr>
</script>
<script id="AdvancedSearchControlItem_View" type="text/html">
    <tr>
        <td data-bind="text: $data.selectedValuesDescription"></td>
        <td data-bind="text: $data.searchValueText"></td>
        <td />
    </tr>
    <tr>
        <td data-bind="template: { name: 'AdvancedSearchControlItemRadios_View', afterRender: $root.iButton }">
        </td>
        <td />
        <td />
    </tr>
</script>
<script id="AdvancedSearchControlItemRadios_View" type="text/html">
    <input type="checkbox" name="AndOr" />
</script>

1 个答案:

答案 0 :(得分:0)

我已经更改了searchValue_keydown函数,如下所示;

self.searchValue_keydown = function (sender, event) {
        if (event.keyCode === $.ui.keyCode.ENTER) {
            self.SearchControlViewModelTemplate().searchContext.searchValue(event.currentTarget.value);
            self.addSearchFilter();
            /* ADDED the code below to fix the issue; While knockoutjs updates */
            /* the ui but not forcing to apply css styles; thought I can update */
            /* the ui again by inserting into DOM again with dummy empty text. */
            /* And I succeded. */
            $('#' + context.displayId).append(' ');
        }
        return true;
    };