KnockoutJS: затухание после затухания чего-то еще

Скажем, у меня есть набор из 3 переключателей:

<div>
    <label>
        <input type="radio" name="Who" value="Myself" 
            checked="@isMyselfChecked" data-bind="checked: who" />
        Mine
    </label>
    <label>
        <input type="radio" name="Who" value="MemberId" 
            checked="@isMemberIdChecked" data-bind="checked: who" />
        I know the member's ID
    </label>
    <label>
        <input type="radio" name="Who" value="MemberUrl" 
            checked="@isMemberUrlChecked" data-bind="checked: who" />
        I know the member's URL
    </label>
</div>

Когда пользователь выбирает первый переключатель (Mine/Myself), никаких дополнительных действий не требуется. Однако при выборе второго или третьего требуется дополнительный ввод :

<div>
    <input type="text" name="MemberId" placeholder="Enter Member ID" 
        data-bind="toggleWho: who()" style="display: none" />
    <input type="text" name="MemberUrl" placeholder="Enter Member URL" 
        data-bind="toggleWho: who()" style="display: none; width: 450px;" />
</div>

Достаточно просто иметь data-bind="visible: who() === '[MemberId|MemberUrl]'" в зависимых текстовых полях. Однако что, если я хочу добавить переходы постепенного увеличения/уменьшения?

Я опробовал пример кастома fadeVisible bindingHandler с нокаут-сайта и понял, как он работает. Однако это будет исчезать и исчезать в текстовых полях одновременно. Если выбрано радио 'MemberId', а пользователь выбирает радио 'MemberUrl', я хочу, чтобы текстовое поле MemberId исчезало до появления текстового поля MemberUrl.

Ниже то, что у меня есть сейчас, и это работает, но я не думаю, что это оптимально. Как еще можно заставить нокаут не выполнять постепенное появление до тех пор, пока не исчезнет предыдущий элемент? Нужен ли мне еще один ko.observale или, возможно, ko.computed?

var viewModel = {
    fadeSpeed: 150,
    who: ko.observable($('input[type=radio][name=Who]:checked').val())
};

ko.bindingHandlers.toggleWho = {
    init: function (element, valueAccessor) {
        var value = valueAccessor();
        var unwrapped = ko.utils.unwrapObservable(value);
        if (unwrapped === element.name)
            $(element).show();
    },
    update: function (element, valueAccessor) {
        var value = valueAccessor();
        var unwrapped = ko.utils.unwrapObservable(value);

        // when selected value is myself, fade out the visible one, if any
        if (unwrapped === 'Myself') {
            $('input[type=text][name=MemberId]:visible')
                .fadeOut(viewModel.fadeSpeed);
            $('input[type=text][name=MemberUrl]:visible')
                .fadeOut(viewModel.fadeSpeed);
        }

            // when selected value is memberid, may need to fade out url first
        else if (unwrapped === 'MemberId') {
            if ($('input[type=text][name=MemberUrl]:visible').length > 0) {
                $('input[type=text][name=MemberUrl]:visible')
                    .fadeOut(viewModel.fadeSpeed, function () {
                        $('input[type=text][name=MemberId]')
                            .fadeIn(viewModel.fadeSpeed);
                    });
            } else {
                $('input[type=text][name=MemberId]')
                    .fadeIn(viewModel.fadeSpeed);
            }
        }

            // when selected value is memberurl, may need to fade out id first
        else if (unwrapped === 'MemberUrl') {
            if ($('input[type=text][name=MemberId]:visible').length > 0) {
                $('input[type=text][name=MemberId]:visible')
                    .fadeOut(viewModel.fadeSpeed, function () {
                        $('input[type=text][name=MemberUrl]')
                            .fadeIn(viewModel.fadeSpeed);
                });
            } else {
                $('input[type=text][name=MemberUrl]')
                    .fadeIn(viewModel.fadeSpeed);
            }
        }
    }
};

ko.applyBindings(viewModel);

person danludwig    schedule 11.07.2012    source источник


Ответы (3)


Вам придется немного адаптировать это, чтобы соответствовать вашему примеру, но мне нужно было упростить его для тестирования в этой скрипте.

Вот привязка:

var previousElement = null;
ko.bindingHandlers.fadeSwitcher = {
    init: function(element, valueAccessor) {
        var value = valueAccessor();
        $(element).toggle(ko.utils.unwrapObservable(value));
    },
    update: function(element, valueAccessor) {

        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value) {
            if (previousElement == null) { // initial fade
                $(element).fadeIn();
            } else {
                $(previousElement).fadeOut('fast', function() {
                    $(element).fadeIn();
                });
            }
            previousElement = element;
        }        
    }
};
person Kyeotic    schedule 11.07.2012

Немного опоздал на вечеринку, но, возможно, это пригодится кому-то еще.

Я взял ответ Тирсиуса и изменил его, чтобы исправить свои собственные потребности; эта версия имеет дело с наблюдаемым свойством и будет постепенно исчезать/исчезать в старом/новом значении всякий раз, когда оно изменяется.

Usage example: <span data-bind="fadeSwitcher: myObservable"></span>

ko.bindingHandlers.fadeSwitcher = {
    init: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        element.setAttribute('previousValue', value);
        ko.bindingHandlers.text.update(element, ko.observable(value));
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var previousValue = element.getAttribute('previousValue');
        if (value !== previousValue) {
            $(element).fadeOut('fast', function () {
                ko.bindingHandlers.text.update(element, ko.observable(value));
                $(element).fadeIn();
            });
            element.setAttribute('previousValue', value);
        }
    }
};
person MartijnK    schedule 25.08.2015

Спасибо Tyrsius за ответ. Мне пришлось адаптировать его от скрипки. Мне удалось использовать слегка измененную привязку, чтобы заставить ее работать с теми же радиостанциями, что и в вопросе (без foreach):

@* radios same as in question *@
<div>
    <input type="text" name="MemberId" placeholder="Enter Member ID" 
        data-bind="whoFader: who() === 'MemberId'" style="display: none" />
    <input type="text" name="MemberUrl" placeholder="Enter Member URL" 
        data-bind="whoFader: who() === 'MemberUrl'" 
        style="display: none; width: 450px;" />
</div>

ko.bindingHandlers.whoFader = {
    previousElement: null,
    init: function (element, valueAccessor) {
        var value = valueAccessor();
        $(element).toggle(ko.utils.unwrapObservable(value));
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value) {
            if (this.previousElement == null) {
                $(element).fadeIn('fast');
            } else {
                $(this.previousElement).hide();
                $(element).fadeIn('fast');
            }
            this.previousElement = element;
        }
        else {
            $(element).fadeOut('fast');
        }
    }
};
person danludwig    schedule 11.07.2012
comment
Рад, что смог помочь. Возможно, вы захотите рассмотреть более ориентированное на модель решение. С KO все будет проще, если вы будете следовать шаблону MVVM. - person Kyeotic; 11.07.2012
comment
@Tyrsius, под модельно-ориентированным решением вы имеете в виду использование foreach для рендеринга радиостанций из виртуальной машины вместо их явного вывода в HTML? - person danludwig; 11.07.2012
comment
Да, между прочим. Идея шаблона MVVM заключается в том, что представление не содержит никаких данных, оно знает только, как отображать вашу модель. Все данные должны быть в модели. Это позволяет использовать как представление, так и модель для любых данных. - person Kyeotic; 11.07.2012