Вложенные выборки не обновляются

1

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

Я создал эту скрипку (http://jsfiddle.net/f0m574wp/17/).

У меня есть некоторые вложенные данные, которые мое приложение извлекает асинхронно, конструирует и отображает по мере поступления данных.

dat = [{
  key: 'asdf',
  values:[{
      key:'1',
      value:'hello'
    },{
      key:'2',
      value:''
    }]
  },{
  key:'xyz',
  values:[{
    key:'1',
    value:'foo'
  },{
    key:'3',
    value:'bar'
  }]
}];

Поскольку объект данных обновляется, я хочу повторно запустить функцию отображения, чтобы обновить визуализацию.

function display(mydata){
  //let x = mydata;
  let x = JSON.parse(JSON.stringify(mydata));
  console.log('x', x);
  let root = d3.select('div');

  // create the outer groupings
  let outer = root.selectAll('div.outer').data(x, d=>d.key);
  let outerEnter = outer.enter()
    .append('div')
    .classed('outer', true)
    .attr('id', d=>d.key);

  // add a heading and body to contain data
  outerEnter.append('div').classed('heading', true).append('h1');
  outerEnter.append('div').classed('body', true);
  outer.exit().remove();

  // get prevoius and existing outer groups
  let outerall = outerEnter.merge(outer);
  let headings = outerall.selectAll('div.heading');
  let bodies = outerEnter.selectAll('div.body')
  headings.selectAll('h1').text(d=>d.key); // update outer group heading

  //lets work with the inner arrays of data
  let innerGroup = bodies.selectAll('div.mid')
    .data(d=>{ console.log(d); return d.values}, k=>k.key);

  let innerGpEnter = innerGroup.enter()
    .append('div')
    .classed('mid', true);

  let innerGpAll = innerGpEnter.merge(innerGroup);

  // inner heading
  let inner = innerGpAll.selectAll('h4.inner')
    //.call(function(sel){console.log(sel.nodes());})
    .data(d=>{ console.log(d); return [d];}, k=>k.key);
  let innerEnter = inner.enter()
    .append('h4')
    .classed('inner', true);
  innerEnter.merge(inner).text(k=>{console.log(k); return k.key});

  // inner detail
  let p = innerGpAll.selectAll('p.inner').data(d=>[d], k=>k.key);
  let pEnter = p.enter().append('p').classed('inner', true);
  pEnter.merge(p).text(v=>v.value);
}

Проблема, похоже, связана с внутренней привязкой данных:

//lets work with the inner arrays of data
let innerGroup = bodies.selectAll('div.mid')
  .data(d=>{ console.log(d); return d.values}, k=>k.key);

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

Теги:
d3.js

1 ответ

3
Лучший ответ

Основной проблемой здесь является использование двух цепочек selectAll. У тебя есть...

let bodies = outerEnter.selectAll('div.body');

... с последующим...

let innerGroup = bodies.selectAll('div.mid').etc...

что дает нам это:

let innerGroup = outerEnter.selectAll('div.body').selectAll('div.mid').etc...

Во-первых, вы должны выбрать outerall, а не outerEnter, так как outerall - это выбор обновления. Но главная проблема здесь - selectAll: нет привязки данных после первой, сразу после второй. Из-за этого данные не переносятся на внутренние выборы.

Важно иметь в виду, что, в отличие от select, selectAll не распространяет данные. Посмотрите на эту таблицу, которую я сделал:

+-------------------+----------------------------------+----------------------------+
| Method            |            select()              |         selectAll()        |
+-------------------+----------------------------------+----------------------------+
| Selection         | selects the first element        | selects all elements that  |
|                   | that matches the selector string | match the selector string  |
+-------------------+----------------------------------+----------------------------+
| Grouping          | Does not affect grouping         | Affects grouping           |
+-------------------+----------------------------------+----------------------------+
| Data propagation  | Propagates data                  | Doesn't propagate data     |
+-------------------+----------------------------------+----------------------------+

Обратите внимание на распространяемые данные, а не на распространяемые данные.

Решение:

Давайте сделаем это одним selectAll, а затем метод data:

let innerGroup = outerall.selectAll('div.mid').etc...

Вот ваш код с этим изменением:

dat = [{
  key: 'asdf',
  values: [{
    key: '1',
    value: 'hello'
  }, {
    key: '2',
    value: ''
  }]
}, {
  key: 'xyz',
  values: [{
    key: '1',
    value: 'foo'
  }, {
    key: '3',
    value: 'bar'
  }]
}];

function display(mydata) {
  //let x = mydata;
  let x = JSON.parse(JSON.stringify(mydata));
  console.log('x', x);
  let root = d3.select('div');

  // create the outer groupings
  let outer = root.selectAll('div.outer').data(x, d => d.key);
  let outerEnter = outer.enter()
    .append('div')
    .classed('outer', true)
    .attr('id', d => d.key);

  // add a heading and body to contain data
  outerEnter.append('div').classed('heading', true).append('h1');
  outerEnter.append('div').classed('body', true);
  outer.exit().remove();

  // get prevoius and existing outer groups
  let outerall = outerEnter.merge(outer);
  let headings = outerall.selectAll('div.heading');
  headings.selectAll('h1').text(d => d.key); // update outer group heading

  //lets work with the inner arrays of data
  let innerGroup = outerall.selectAll('div.mid')
    .data(d => {
      console.log(d);
      return d.values
    }, k => k.key);

  let innerGpEnter = innerGroup.enter()
    .append('div')
    .classed('mid', true);

  let innerGpAll = innerGpEnter.merge(innerGroup);

  // inner heading
  let inner = innerGpAll.selectAll('h4.inner')
    //.call(function(sel){console.log(sel.nodes());})
    .data(d => {
      console.log(d);
      return [d];
    }, k => k.key);
  inner.exit().remove();
  let innerEnter = inner.enter()
    .append('h4')
    .classed('inner', true);
  innerEnter.merge(inner).text(k => {
    console.log(k);
    return k.key
  });

  // inner detail
  let p = innerGpAll.selectAll('p.inner').data(d => [d], k => k.key);
  p.exit().remove();
  let pEnter = p.enter().append('p').classed('inner', true);
  pEnter.merge(p).text(v => v.value);
}

display(dat);
setTimeout(function() {
  console.log('Send New data');
  //dat[0].key = 'abc';
  dat[0].values[1].value = 'good bye';
  dat[1].values.push({
    key: 2,
    value: 'baz'
  });
  display(dat);
}, 3000)
* {
  margin: 2px;
  padding: 2px;
  border: 0;
}

body {
  background: #ffd;
}

.outer {
  border: 1px solid green;
  width:
}

.mid {
  border: 1px solid red;
}

.inner {
  border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
</div>

Решение 2:

Согласно вашему комментарию, вы должны сохранить эту структуру HTML. В этом случае используйте select:

let bodies = outerall.select('div.body');

Вы можете смело использовать select здесь, потому что вы добавляете только один <div> к body класса.

Вот новый фрагмент:

dat = [{
  key: 'asdf',
  values: [{
    key: '1',
    value: 'hello'
  }, {
    key: '2',
    value: ''
  }]
}, {
  key: 'xyz',
  values: [{
    key: '1',
    value: 'foo'
  }, {
    key: '3',
    value: 'bar'
  }]
}];

function display(mydata) {
  //let x = mydata;
  let x = JSON.parse(JSON.stringify(mydata));
  console.log('x', x);
  let root = d3.select('div');

  // create the outer groupings
  let outer = root.selectAll('div.outer').data(x, d => d.key);
  let outerEnter = outer.enter()
    .append('div')
    .classed('outer', true)
    .attr('id', d => d.key);

  // add a heading and body to contain data
  outerEnter.append('div').classed('heading', true).append('h1');
  outerEnter.append('div').classed('body', true);
  outer.exit().remove();

  // get prevoius and existing outer groups
  let outerall = outerEnter.merge(outer);
  let headings = outerall.selectAll('div.heading');
  let bodies = outerall.select('div.body');
  headings.selectAll('h1').text(d => d.key); // update outer group heading

  //lets work with the inner arrays of data
  let innerGroup = bodies.selectAll('div.mid')
    .data(d => {
      console.log(d);
      return d.values
    }, k => k.key);

  let innerGpEnter = innerGroup.enter()
    .append('div')
    .classed('mid', true);

  let innerGpAll = innerGpEnter.merge(innerGroup);

  // inner heading
  let inner = innerGpAll.selectAll('h4.inner')
    //.call(function(sel){console.log(sel.nodes());})
    .data(d => {
      console.log(d);
      return [d];
    }, k => k.key);
  inner.exit().remove();
  let innerEnter = inner.enter()
    .append('h4')
    .classed('inner', true);
  innerEnter.merge(inner).text(k => {
    console.log(k);
    return k.key
  });

  // inner detail
  let p = innerGpAll.selectAll('p.inner').data(d => [d], k => k.key);
  p.exit().remove();
  let pEnter = p.enter().append('p').classed('inner', true);
  pEnter.merge(p).text(v => v.value);
}

display(dat);
setTimeout(function() {
  console.log('Send New data');
  //dat[0].key = 'abc';
  dat[0].values[1].value = 'good bye';
  dat[1].values.push({
    key: 2,
    value: 'baz'
  });
  display(dat);
}, 3000)
* {
  margin: 2px;
  padding: 2px;
  border: 0;
}

body {
  background: #ffd;
}

.outer {
  border: 1px solid green;
  width:
}

.mid {
  border: 1px solid red;
}

.inner {
  border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
</div>
  • 0
    Это меняет структуру HTML. Мне нужно, чтобы div.mid был внутри div.body. Я вкладываю панели начальной загрузки (v3). 'div.mid' - это начальная загрузка 'div.panel.panel-default', содержащая еще один 'div.panel-heading' и 'div.panel-body'. Как я могу правильно распространять данные?
  • 0
    Затем используйте select , а не selectAll , или, в качестве альтернативы, повторно свяжите данные.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню