2014-02-11 5 views
-1

Я написал собственный обработчик привязки, чтобы привязать данные вида viewmodel к диаграмме высоких диаграмм. У этого действительно есть 2 части, одна связывает исходную конфигурацию, необходимую для высоких диаграмм, вторая привязывает серию к диаграмме.Пользовательский перехват нокаута срабатывает дважды, неожиданно

вот bindingHandler код

ko.bindingHandlers.highchart = { 
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
     var value = valueAccessor(); 
     var valueUnwrapped = ko.unwrap(value); 
     console.log ('update',element.id,valueUnwrapped);   
     if(allBindings.get('series')){ 
      var series = allBindings.get('series'); 
      var seriesUnwrapped = ko.unwrap(series); 
      if(!$.isArray(seriesUnwrapped)){ 
       seriesUnwrapped = [seriesUnwrapped]; 
      } 
      console.log('seriesUnwrapped',element.id,seriesUnwrapped) 
      valueUnwrapped.series = seriesUnwrapped; 
     } 
     $(element).highcharts(valueUnwrapped);   
    } 
} 

Теперь у меня есть 2 набора тестов для этого, первые работы, как и ожидалось. Он связывает диаграмму с несколькими рядами, и когда я добавляю к наблюдаемому массиву, связанному с series, он обновляет график только один раз. Посмотрите на this fiddle и посмотрите консоль, когда вы нажимаете кнопку «Добавить». Выход вы получаете

обновление контейнера объекта {диаграмма = {...}}
seriesUnwrapped контейнер [Объект {Name = "Scenario0", цвет = "красный", данные = [9]} , Object {name = "Scenario1", color = "green", data = [9]}]

Указывая, что мы прошли вышеуказанный код только один раз.

Теперь проверьте мою вторую скрипку: http://jsfiddle.net/8j6e5/9/. Это немного отличается, поскольку начальная конфигурация старших карт - это computed, наблюдаемая, как и серия. Когда вы нажмете кнопку «Добавить» на этом, вы увидите, что привязка выполняется дважды:

update container2 Object {chart = {...}, xAxis = {...}, series = [ 1]}
seriesUnwrapped container2 [Object {name = "Scenario2", color = "blue", data = [2]}]
update container2 Object {chart = {...}, xAxis = {...} }
seriesUnwrapped container2 [Объект {имя = "Scenario2", цвет = "синий", данные = [2]}]

Я предполагаю, что использование allBindings.get('series') в пределах моих Highcharts привязки обработчика я установил зависеть от него, и когда обе привязки меняют свое выполнение двойных привязок в два раза. Мой вопрос в том, есть ли способ остановить это или написать эту функцию любым другим способом, чтобы этого не произошло?

+1

Вы должны решить, когда вы хотите, чтобы ваша функция обновления будет называться. Поэтому, когда изменяется связанное свойство или изменяется связанное свойство 'series'. И затем используйте 'peek()' вместо 'ko.unwrap':' var seriesUnwrapped = series.peek(); 'или' var valueUnwrapped = value.peek(); 'вам, конечно, потребуется проверка для проверки' value' или 'series' являются действительно наблюдаемыми, прежде чем вызывать peek на них. – nemesv

+0

@nemesv - Это очень хорошее начало, это само по себе устраняет проблему двойного вызова. Однако после выполнения этого простого изменения моя первая скрипка больше не будет обновляться (поскольку она привязана к вычисленной серии, но статическое свойство highchart). У вас 2/3 пути к очень хорошему ответу. – Jamiec

+1

Если вы делаете это со значением 'var valueUnwrapped = ko.isObservable (value)? value.peek(): value; 'исправляет оба. Но в случае, когда ваша привязка - это «highchart: yourProp, series: otherProp» и только «yourProp» изменяется, а «otherProp» - нет, значит, он не обновит вашу диаграмму правильно ... – nemesv

ответ

3

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

Однако, я потратил немного времени на это, поэтому я покажу вам, что я придумал, и надеюсь, что это поможет.

Fiddle here

Я не знал о методе заглядывать(), что nemesv упомянутый (большой наконечник), так что я посмотрел в то, почему он обновлял дважды на основе computeds и т.д.

я увидел, что ваш self.breakdownChart имел доступ к наблюдаемому currentScenario, и когда я удалил это как тест, второе обновление не произошло.

Так вот, что заставило меня задуматься, зачем вам это нужно, чтобы установить ось x.

Так что добавили новое свойство к вашему сценарию, чтобы вернуть имя текущего сценария

self.name='Scenario' + self.number; 

А потом для базового сценария, изменил это на «Базовый сценарий», чтобы гарантировать, что заголовок правильно отображается только для этой серии ,

Чтобы легенда/ось правильно, я добавил новое свойство объекта диаграммы называется baseSeriesName

self.breakdownChart = ko.computed(function(){ 
    return {  
     baseSeriesTitle: baseScenario.name, 

и устанавливается на имя baseScenario в.

Наконец, чтобы связать, что все вместе в BindingHandler, я обновляю XAxis там:

 //set the xAxis titles, only add the second title if different from the base 
     valueUnwrapped.xAxis={ 
      categories: [valueUnwrapped.baseSeriesTitle, valueUnwrapped.baseSeriesTitle!=seriesUnwrapped[0].name ? seriesUnwrapped[0].name:''] 
     } 

Это немного рефакторинга, но он достигает своей цели; Надеюсь, поможет.

О, я также добавил диаграмму типа, наблюдаемую для модели представления, и использовал ее в определении диаграммы (вычисленный breakdownChart), чтобы проверить двойное обновление, не произойдет, если диаграмма обновится на другом наблюдаемом и что она все еще правильно инициализирован - так скрипта показывает обновление chartType без двойного обновления.

3

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

Один из способов решения этой проблемы - использовать технику для задержки обновлений привязки. Самый простой способ сделать это состоит в использовании Deferred Updates plugin, как показано здесь: http://jsfiddle.net/mbest/8j6e5/15/

Отложенного обновление использует setTimeout для выполнения обновления, а это значит, что обновление происходит асинхронно. Если вы хотите, чтобы быть синхронными для конкретного обновления, вы можете использовать ko.tasks.processImmediate:

ko.tasks.processImmediate(function() { 
    self.scenarios.push(newScenario); 
    self.currentScenario(newScenario); 
});    
Смежные вопросы