2

Попытка понять ko.mapping в сочетании с TypeScript и RequireJS. Насколько я понимаю, я могу создать модель представления, привязать ее к представлению и подвергнуть сложный объект моему представлению с помощью модели представления. Мне не повезло, что это сработало. Большинство примеров в Интернете хотят показать, как принять ответ веб-службы и связать его напрямую. Я ищу более простой пример, чем это - я просто хочу отобразить несвязанный объект на экран. Я мог бы, конечно, сделать это вручную, но я думаю, что этот инструмент был разработан для точной цели ...knockout.mapping с TypeScript и RequireJS

У меня есть две потребности:

  1. показать значение на начальном экране - возможно, пустой
  2. Используйте чтобы изменить значение.

Я играл с некоторым примером кода в качестве доказательства концепции, как самой базовой версии, которую я мог бы придумать. Идея состоит в том, чтобы представить представление кнопкой. Текст кнопки должен загружаться с помощью «Hello World!», А при нажатии на кнопку «Прощай луну ...».

Я думаю, что моя точка зрения модель нуждается в двух объектов ...

  1. POJO
  2. связывания объекта, инстанцирован быть ko.mapping.fromJS ({})

Мое понимание (что, скорее всего, неверно) заключается в том, что отображение будет принимать POJO и автоматически создать наблюдаемую версию POJO в объекте привязки. Представление привязано к объекту привязки. В любое время, например, одним нажатием кнопки, я могу увеличить POJO и перезагрузить объект привязки, и мое представление будет соответствующим образом обновляться.

Модель My View подключена, так как я могу установить точки останова и посмотреть, как они попадают. Не удалось загрузить страницу, потому что связанный объект недоступен. Если я перехожу от ko.mapping к стандартным наблюдаемым, он загружается штрафом.

Что мне не хватает при рассмотрении ko.mapping? Неужели мой подход полностью испорчен?


Основные POJO класса

class DefaultModel { 
    public myField: string; 
} 
export = DefaultModel; 

Посмотреть

<!DOCTYPE html> 
<html lang="en"> 
    <head> 
     <meta charset="utf-8" /> 
     <title>TypeScript HTML App</title> 
     <script data-main="Application/require-config" src="Scripts/require.js"></script> 
    </head> 
    <body> 
     <h1>TypeScript HTML App</h1> 
     <button id="myMethodTest" data-bind="text: boundModel().myField, click: function() { myButton_Click() }" ></button> 
    </body> 
</html> 

View Model

/// <reference path="../Scripts/typings/knockout/knockout.d.ts" /> 
/// <reference path="../Scripts/typings/knockout.mapping/knockout.mapping.d.ts" /> 

import DefaultModel = require("Models/DefaultModel"); 
import ko = require("knockout"); 

class DefaultViewModel { 
    public basicModelInstance: DefaultModel; 
    public boundModel: any; 

    constructor() { 
     // INSTANTIATE THE BOUND MODEL TO BE A BLANK KO MAPPED AWARE OBJECT 
     this.boundModel = ko.mapping.fromJS({}); 

     // SETUP A BASIC INSTANCE OF A POJO 
     this.basicModelInstance = new DefaultModel; 
     this.basicModelInstance.myField = "Hello World!"; 

     // LOAD THE POPULATED POJO INTO THE BOUND OBVSERVABLE OBJECT 
     this.boundModel = ko.mapping.fromJS(this.basicModelInstance, {}, this.boundModel); 
    } 

    myButton_Click() { 
     // UPDATE THE POJO 
     this.basicModelInstance.myField = "Goodbye Moon..."; 

     // RELOAD THE POJO INTO THE BOUND OBJECT 
     this.boundModel = ko.mapping.fromJS(this.basicModelInstance, {}, this.boundModel); 
    } 
} 
export = DefaultViewModel; 

RequireJS Конфигурация

/// <reference path="../Scripts/typings/requirejs/require.d.ts" /> 

require.config({ 
    baseUrl: "", 
    paths: { 
     "jQuery": "Scripts/jquery-2.1.1", 
     "knockout": "Scripts/knockout-3.2.0.debug", 
     "utilities": "Application/utilities", 
     "ViewModelMapper": "Application/ViewModelMapper", 
     "komapping": "Scripts/knockout.mapping-latest.debug" 


    }, 

    shim: { 
     "jQuery": { 
      exports: "$" 
     }, 
     komapping: { 
      deps: ['knockout'], 
      exports: 'komapping' 
     } 
    }, 
}); 

require(["jQuery"], function ($) { 
    $(document).ready(function() { 
     // alert('dom ready'); 

     require(["utilities", "knockout", "ViewModelMapper", "komapping"], (utilities, knockout, viewModelMapper, komapping) => { 
      utilities.defineExtensionMethods($); 
      knockout.mapping = komapping; 

      var url = window.location; 
      var location = utilities.getLocation(url); 
      var urlPath = location.pathname; 
      var urlPathWithoutExtension = urlPath.replace(".html", ""); 

      var viewModel = viewModelMapper.getViewModel(urlPathWithoutExtension); 
      knockout.applyBindings(viewModel); 
     }); 
    }); 
}); 

ответ

1

A) В своем коде View Model, вызов ko.mapping.fromJS нужно только первые два параметра.Поскольку он возвращает связанную модель, вам не нужно передавать свою связанную модель. Оно должно быть:

this.boundModel = ko.mapping.fromJS(this.basicModelInstance, {}); 

B)viewModel.boundModel не является функцией, это объект. Так что в вашем HTML, ваши привязки text: boundModel().myField должно быть text: boundModel.myField

C) Вы недопониманием пути связывание должно работать. После того, как у вас есть связанная модель, нет необходимости обновлять «POJO», а затем воссоздавать связанную модель каждый раз, когда что-то в вашей модели просмотра изменяется. Двусторонняя привязка данных, которая предлагает нокауты, сохранит вашу модель просмотра и ваш ui (html) в синхронизации, и поэтому вам нужно будет работать только с вашей моделью просмотра. Когда вам нужно взять то, что находится в вашей модели просмотра, и обновить «POJO», который должен быть только тогда, когда вам нужно обновить сервер, вы можете использовать функцию ko.mapping.toJS, которая выполняет функцию ko.mapping.fromJS. Вы передаете свою связанную модель, и она вернет вам объект Vanilla JS «POJO», удалив все наблюдаемые.

+0

Что касается импорта комапинга - это не ko.mapping плагин ko, и как таковой он не импортирует автономно, а импортирует его как часть нокаута, когда нокаут импортируется, если он установлен? – barrypicker

+0

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

+0

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

2

Я получил это @wired_in за предоставленную помощь. Здесь я предоставлю рабочую версию кода, которая, наконец, решила мою проблему.

Моя теория - если отображение может принимать результат вызова AJAX и автоматически отображать его на наблюдаемое, почему не может быть обычного POJO? Ну, это может! Эта базовая способность освобождает. Теперь я свободен создавать модели, не загрязняя их «наблюдаемыми». Модели могут вести себя как любой обычный объект без специальной обработки. Манипулируйте желаемой моделью, а затем, когда это необходимо для представления на представлении, свяжите ее с помощью вызова ko.mapping.fromJS.

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


Basic POJO Класс:

class DefaultModel { 
    public myField: string; 
} 
export = DefaultModel; 

Вид:

<!DOCTYPE html> 
<html lang="en"> 
    <head> 
     <meta charset="utf-8" /> 
     <title>TypeScript HTML App</title> 
     <script data-main="Application/require-config" src="Scripts/require.js"></script> 
    </head> 
    <body> 
     <h1>TypeScript HTML App</h1> 
     <button id="myMethodTest" data-bind="text: boundModel.myField, click: function() { myButton_Click() }" ></button> 
    </body> 
</html> 

вид Модель:

/// <reference path="../Scripts/typings/knockout/knockout.d.ts" /> 
/// <reference path="../Scripts/typings/knockout.mapping/knockout.mapping.d.ts" /> 

import DefaultModel = require("Models/DefaultModel"); 
import ko = require("knockout"); 


class DefaultViewModel { 
    public basicModelInstance: DefaultModel; 
    public boundModel: KnockoutObservable<DefaultModel>; 

    constructor() { 
     // SETUP A BASIC INSTANCE OF A POJO 
     this.basicModelInstance = new DefaultModel; 
     this.basicModelInstance.myField = "Hello World!"; 

     // LOAD THE POPULATED POJO INTO THE BOUND OBVSERVABLE OBJECT 
     this.boundModel = ko.mapping.fromJS(this.basicModelInstance); 
    } 

    myButton_Click() { 
     // UPDATE THE POJO 
     this.basicModelInstance.myField = "Goodbye Moon..."; 

     // RELOAD THE POJO INTO THE BOUND OBJECT 
     this.boundModel = ko.mapping.fromJS(this.basicModelInstance, this.boundModel); 
    } 
} 
export = DefaultViewModel; 

RequireJS Конфигурация:

/// <reference path="../Scripts/typings/requirejs/require.d.ts" /> 

require.config({ 
    baseUrl: "", 
    paths: { 
     "jQuery": "Scripts/jquery-2.1.1", 
     "knockout": "Scripts/knockout-3.2.0.debug", 
     "utilities": "Application/utilities", 
     "ViewModelMapper": "Application/ViewModelMapper", 
     "komapping": "Scripts/knockout.mapping-latest.debug" 


    }, 

    shim: { 
     "jQuery": { 
      exports: "$" 
     }, 
     komapping: { 
      deps: ['knockout'], 
      exports: 'komapping' 
     } 
    }, 
}); 

require(["jQuery"], function ($) { 
    $(document).ready(function() { 
     // alert('dom ready'); 

     require(["utilities", "knockout", "ViewModelMapper", "komapping"], (utilities, knockout, viewModelMapper, komapping) => { 
      utilities.defineExtensionMethods($); 
      knockout.mapping = komapping; 

      var url = window.location; 
      var location = utilities.getLocation(url); 
      var urlPath = location.pathname; 
      var urlPathWithoutExtension = urlPath.replace(".html", ""); 

      var viewModel = viewModelMapper.getViewModel(urlPathWithoutExtension); 
      knockout.applyBindings(viewModel); 
     }); 
    }); 
}); 

Вывод:

В конце концов, я застрял на 3-х вещей ...

  1. мой взгляд неправильно называют привязки объекта в окне просмотра модели. Спасибо @wired_in за помощь в этой области
  2. В конструкторе я пропускал слишком много параметров.Благодаря @wired_in для указания этого. Документация для KnockoutJS.mapping неясно в этой области. Я думаю, что использование 1 против 3 параметров необязательно.
  3. В методе myButton_Click мне нужно передать ссылку на существующий, уже связанный объект (A.K.A., viewmodel внутри модели ). Это было ключом к разрешению обновлений существующей связанной модели .

Теперь я могу контролировать, когда мой взгляд изменится на основе данных, управляемых под обложками. Независимо от того, получены ли данные из вызова AJAX или внутренней вычислительной манипуляции из сторонней системы из загруженного файла - независимо от того, я теперь могу видеть данные, видимые в представлении. Довольно круто.

В конце вопрос: «Почему у данных есть несвязанные POJO? Почему бы просто не использовать связанный объект и не манипулировать им как наблюдаемый?» - Я думаю, что ответ «переносимость». Я хочу, чтобы свобода пропускала обычный объект из базы кода и из него без особого рассмотрения. Это понятие маркировки объекта как наблюдаемого является ограничением, налагаемым каркасом - обходным путем для возможного связывания. Нежелательно требовать применения атрибута «наблюдаемый» повсюду. Разделение забот Baby! В любом случае, с моего мыльного ящика сейчас ...

Спасибо @wired_in.

+0

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

+0

Используя ваш метод, каждый раз, когда POJO изменяется под капотом, вам нужно вызвать код сопоставления, чтобы вручную обновить связанный объект, чтобы обновить представление. С другой стороны, каждый раз, когда элемент пользовательского интерфейса, такой как текстовое поле, обновляет связанную модель, вам нужно вручную отобразить обратно POJO, чтобы все синхронизировалось. Вы нарушаете цель привязки данных. Я понимаю, что начать работу с нокаутом и использовать наблюдаемые кажется противоречивым и странным, но как только вы привыкнете к нему, он очень мощный и простой. –

+0

Если вы все еще не уверены и используете пользовательский объект для управления вашими данными, просто не сработает для вас, я бы предложил посмотреть на angularJS, который предлагает метод привязки данных, который позволяет вам просто использовать обычные объекты JS без необходимости наблюдения. –

Смежные вопросы