Подтвердить что ты не робот

KnockoutJS - Разочарование с помощью ko.computed и запроса AJAX

Я просто пытаюсь вытащить некоторые данные из запроса ajax. Работает вызов ajax - я знаю, что данные извлекаются. Но это просто не устанавливает значение ko.computed...

        function viewModel() {
            this.Id = ko.observable("@Model.Identity()");
            this.Points = ko.computed({ 
                read: function () {
                    $.ajax({
                        url: '/skills/points',
                        data: { id: this.Id },
                        type: 'get',
                        success: function (data) {
                            return data;
                        }
                    });
                },
                owner: this
            });
        }

Итак, вызов вроде...

<span data-bind="text: Points"></span>

Просто не работает. Может ли кто-нибудь помочь мне разобраться, что я делаю неправильно?

Update

Я использую следующий код, следуя предложению RPN, и я просто не могу заставить его функционировать вообще. Он не будет смотреть на контроллер, он не вернет данные... он просто ничего не делает. Я пробовал все три примера без успеха.

<script type="text/javascript">
    //an observable that retrieves its value when first bound
    ko.onDemandObservable = function (callback, target) {
        var _value = ko.observable();  //private observable

        var result = ko.computed({
            read: function () {
                //if it has not been loaded, execute the supplied function
                if (!result.loaded()) {
                    callback.call(target);
                }
                //always return the current value
                return _value();
            },
            write: function (newValue) {
                //indicate that the value is now loaded and set it
                result.loaded(true);
                _value(newValue);
            },
            deferEvaluation: true  //do not evaluate immediately when created
        });

        //expose the current state, which can be bound against
        result.loaded = ko.observable();
        //load it again
        result.refresh = function () {
            result.loaded(false);
        };

        return result;
    };

    $(document).ready(function () {
        function User(id, name) {
            this.Id = ko.observable(id);
            this.Name = ko.observable(name);
        }
        function viewModel() {
            var self = this;

            this.User = ko.onDemandObservable(this.Update, this);

            this.Update = function () {
                return $.ajax({
                    url: '/home/user/',
                    data: { id: 1 },
                    dataType: 'json'
                }).pipe(function (data) {
                    return new User(data.Id, data.Name);
                });
            };
        };
        var model = new viewModel();
        ko.applyBindings(model);
        model.User();
    });
</script>

<span data-bind="text: User.Name"></span>

Обновление (2)

Следуя дальнейшим инструкциям, я сузил часть проблемы. Определение callback как функции на viewModel, похоже, не работает (я не уверен, почему), но объявление встроенной функции дает... что-то другое. Тем не менее, все еще не работает. Вот обновление.

<script type="text/javascript">
    //an observable that retrieves its value when first bound
    ko.onDemandObservable = function (callback, target) {
        var _value = ko.observable();  //private observable

        var result = ko.computed({
            read: function () {
                //if it has not been loaded, execute the supplied function
                if (!result.loaded()) {
                    callback.call(target);
                }
                //always return the current value
                return _value();
            },
            write: function (newValue) {
                //indicate that the value is now loaded and set it
                result.loaded(true);
                _value(newValue);
            },
            deferEvaluation: true  //do not evaluate immediately when created
        });

        //expose the current state, which can be bound against
        result.loaded = ko.observable();
        //load it again
        result.refresh = function () {
            result.loaded(false);
        };

        return result;
    };

    $(document).ready(function () {
        function User(id, name) {
            this.Id = ko.observable(id);
            this.Name = ko.observable(name);
        }
        function viewModel() {
            var self = this;

            this.User = ko.onDemandObservable(function(){
                $.ajax({
                    url: '/home/user/',
                    data: { id: 1 },
                    dataType: 'json',
                    success: function (data) {
                        self.User(new User(data.Id, data.Name));
                    }
                });
            }, this);

            //this.Update = function () {
            //  $.ajax({
            //      url: '/home/user/',
            //      data: { id: 1 },
            //      dataType: 'json',
            //      success: function (data) {
            //          self.User(new User(data.Id, data.Name));
            //      }
            //  });
            //};
        };
        var model = new viewModel();
        ko.applyBindings(model);
        model.User();
    });
</script>

И затем попытка отобразить любую из полученных данных все еще не удается.

<span data-bind="text: User.Name"></span>

Обновление (3)

Немного прорыва! Если я изменяю декларативное привязку, чтобы выглядеть так.

<span data-bind="with: User">
    <span data-bind="text: Id"></span>
    <span data-bind="text: Name"></span>
</span>

Затем я начинаю видеть результаты. Я думаю, что я почти добираюсь туда...

4b9b3361

Ответ 1

Как отметил СЛАКС, вы не можете сделать это так, потому что вызов выполняется асинхронно, то есть функция "читать" возвращается до получения ответа. Я бы рекомендовал что-то вроде этого:

function viewModel() {
    var self = this;
    this.Id = ko.observable("@Model.Identity()");
    this.Points = ko.observable(0);

    var refreshPoints = function() {
        $.ajax({
            url: '/skills/points',
            data: { id: self.Id() }, // <-- you need () here!
            type: 'get',
            success: function (data) {
                self.Points(data);
            }
        });
    };

    // Now subscribe to the ID observable to update the points whenever the 
    // ID gets changed:
    this.Id.subscribe(refreshPoints);
}

Ответ 2

Просто привяжите наблюдаемую переменную к переменной html и обновите это поле в поле ko.put. Не привязывайте поле ko.computed к переменной html напрямую.

self.htmlUserName = ko.observable();

self.computedUserName = ko.computed(function () {
    $.ajax(
    ....
    success: function (result) {
    self.htmlUserName(result);
    }
}

Ответ 3

Связывание нокаутом не поддерживает асинхронные вычисления.

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

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