Quando usar ko.utils.unwrapObservable?

114

Eu escrevi algumas ligações personalizadas usando KnockoutJS. Ainda não tenho certeza de quando usar. ko.utils.unwrapObservable(item)Olhando para o código, essa chamada basicamente verifica se itemé um observável. Se for, retorna o valor (), se não, apenas retorna o valor. Observando a seção Knockout sobre a criação de vínculos personalizados, eles têm a seguinte sintaxe:

var value = valueAccessor(), allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);

Nesse caso, eles invocam o observável via, ()mas também chamam ko.utils.unwrapObservable. Estou apenas tentando entender quando usar um ou outro ou se devo sempre seguir o padrão acima e usar os dois.

arb
fonte

Respostas:

142

Você deve usar ko.utils.unwrapObservablenos casos em que não sabe se recebeu um observável ou não. Isso normalmente seria em uma vinculação personalizada onde um observável ou não observável poderia ser vinculado a ela.

No código que você tem acima, a chamada para valueAccessor()não está realmente desembrulhando um observável. Ele está apenas recuperando o valor que foi passado para a vinculação no contexto correto (ele é empacotado em uma função para protegê-lo). O valor de retorno de valueAccessor()pode ser observável ou não. É tudo o que foi passado para a encadernação.

RP Niemeyer
fonte
4
Isso depende da situação. Algumas associações personalizadas são projetadas para funcionar apenas com observáveis, portanto, você pode verificar antecipadamente (ko.isObservable) se é um observável e então você estará livre para desembrulhá-lo com (). Se você estiver recebendo um objeto que pode ter observáveis ​​aninhados, é melhor fazer um ko.toJS(yourObject)do que usar ko.utils.unwrapObservable, se estiver tentando fazer com que uma versão desembrulhada do objeto passe para um widget ou biblioteca de terceiros. Em geral, é mais seguro usar ko.utils.unwrapObservablepara apoiar observáveis ​​e não observáveis.
RP Niemeyer
2
Acho que estou confuso com o propósito de ko.utils.unwrapObservable. Olhando para o código, ele apenas verifica se é observável e, se for, o Knockout invoca ()para obter o valor do observável, caso contrário, ele apenas retorna o valor para não observável. Se tudo o que estou interessado é no valor dos dados passados ​​para a ligação, por que não posso simplesmente usar sempre ()?
arb
17
Você não sabe se está sendo passado um observável ou não observável em uma ligação. Dizer que tenho myBindinge alguém liga, ou seja, gosta data-bind="myBinding: myValue". myValuepode ser um observável ou pode ser apenas uma propriedade simples no modelo de exibição. Se for apenas uma propriedade e eu a chamar como uma função, você receberá um erro. ko.utils.unwrapObservableo retornará com segurança o valor, independentemente de você ter sido aprovado em um observável ou não.
RP Niemeyer
10
Eu também recomendo usar a abreviação de 'ko.unwrap' porque 'ko.utils.unwrapObservable' é uma expressão muito longa.
Ivan Nikitin
3
@IvanNikitin - claro, só queria destacar que ko.unwrapestá disponível no 3.0+. Se você estiver usando uma versão anterior à 3.0, ele ko.utils.unwrapObservableainda estará lá.
RP Niemeyer
12

A resposta anterior está correta, mas frequentemente passo funções para associações personalizadas (uma função que verifica as permissões ou determina o que fazer com base em outra coisa, etc). O que eu realmente precisava era desembrulhar qualquer função, mesmo que não fosse observável.

O seguinte desembrulha recursivamente TUDO:

ko.utils.unwrapFunction = function (func) {
    if (typeof func != 'function') {
        return func;
    }
    else {
        return ko.utils.unwrapFunction(func());
    }
};

Aqui está um exemplo de uma ligação personalizada simples que escrevi:

//replaces single and double 'smart' quotes users commonly paste in from word into textareas and textboxes with normal text equivalents
//USAGE:
//data-bind="replaceWordChars:true
//also works with valueUpdate:'keyup' if you want"

ko.bindingHandlers.replaceWordChars = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var bindingValue = ko.utils.unwrapFunction(valueAccessor);

        if (bindingValue) {
            $(element).val(removeMSWordChars(allBindingsAccessor().value())); //update DOM - not sure why I should need to do this, but just updating viewModel doesn't always update DOM correctly for me
            allBindingsAccessor().value($(element).val()); //update viewModel
        }
    }
}

Dessa forma, bindingValue sempre contém um valor. Não preciso me preocupar se passei uma função, um observável, um valor ou mesmo uma função dentro de um observável. Isso vai desembrulhar tudo corretamente até chegar ao objeto que desejo.

Espero que ajude alguém.

Pilavdzice
fonte