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

D3, вложенные добавления и поток данных

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

Скажем, что мы создаем инструмент, который дает пользователю список продуктов с соответствующими изображениями. И добавим дополнительное ограничение, чтобы каждый элемент списка был помечен уникальным идентификатором, чтобы его можно было связать с другим видом. Моя первая интуиция для решения этой проблемы - создать список <div>, каждый со своим собственным идентификатором, где каждый div имеет свои собственные <p> и <img>. Полученный HTML-код будет выглядеть примерно так:

<div id="chocolate">
  <p>Chocolate Cookie</p>
  <img src="chocolate.jpg" />
</div>
<div id="sugar">
  <p>Sugar Cookie</p>
  <img src="sugar.jpg" />
</div>

Данные для этого инструмента находятся в массиве JSON, где отдельный JSON выглядит следующим образом:

{ "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }

Есть ли способ сделать HTML-код одним махом? Начиная с базового случая добавления div, код может выглядеть примерно так:

d3.select(containerId).selectAll('div')                                                          
   .data(food)
   .enter().append('div')
   .attr('id', function(d) { return d.label; });

Теперь, как насчет добавления <div> с <p> в нем? Моя первоначальная мысль заключалась в том, чтобы сделать что-то вроде:

d3.select(containerId).selectAll('div')                                                          
   .data(food)
   .enter().append('div')
   .attr('id', function(d) { return d.label; })
       .append('p').text('somethingHere');

Но я вижу две проблемы с этим: (1) как вы получаете данные из элемента div и (2) как вы можете добавить несколько дочерних элементов к одному и тому же родительскому объекту в одной декларативной цепочке? Я не могу придумать способ сделать третий шаг, на котором я бы добавил на img.

Я нашел упоминание о вложенном выборе в другом сообщении, которое указывало на http://bost.ocks.org/mike/nest/. Но является ли вложенный выбор и, следовательно, разбивает приложения на три части, подходящие/идиоматические для этой ситуации? Или есть действительно хорошо построенный способ сформировать эту структуру в одной цепочке деклараций? Похоже, что может быть способ с подвыборками, упомянутыми в https://github.com/mbostock/d3/wiki/Selections, но я недостаточно хорошо знаком с языком для проверки этой гипотезы.

С концептуального уровня эти три объекта (div, p и img) обрабатываются больше как одна группа, а не отдельные сущности, и было бы неплохо, если бы код тоже отражал это.

4b9b3361

Ответ 1

Вы не можете добавить несколько дочерних элементов в одну цепочку. Вам нужно будет сохранить родительский выбор в переменной. Это должно делать то, что вы хотите:

var data = [{ "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" },
        { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }];

var diventer = d3.select("body").selectAll("div")
    .data(data)
  .enter().append("div")
    .attr("id", function(d) { return d.label; });

diventer.append("p")
    .text(function(d) { return d.text; });

diventer.append("img")
    .attr("src", function(d) { return d.img; });​

См. рабочую скрипту: http://jsfiddle.net/UNjuP/

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

Это распространение данных не является уникальным для метода append. Это происходит со следующими методами selection: append, insert и select.

Например, для selection.append:

selection.append(имя)

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

Не стесняйтесь спрашивать о деталях, если что-то неясно.


ИЗМЕНИТЬ

Вы можете добавить несколько дочерних элементов без сохранения выбора в переменной с помощью метода selection.each. Затем вы также можете напрямую обращаться к данным от родителя:

var data = [{ "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" },
        { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }];

d3.select("body").selectAll("div")
    .data(data)
  .enter().append("div")
    .attr("id", function(d) { return d.label; })
    .each(function(d) {
        d3.select(this).append("p")
          .text(d.text);
        d3.select(this).append("img")
          .attr("src", d.img);
    });

Ответ 2

Опять же, не существенно отличается, но моим предпочтительным методом было бы использовать "вызов"

var data = [{ "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" },
        { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" }];

d3.select("body").selectAll("div")
    .data(data)
  .enter().append("div")
    .attr("id", function(d) { return d.label; })
  .call(function(parent){
    parent.append('p').text(function(d){ return d.text; });
    parent.append('img').attr("src", function(d) { return d.img; });​
  });

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

Ответ 3

Это не принципиально отличается от ответа nautat, но я думаю, что код можно сделать немного чище, сохранив выбор обновлений, а не выбор ввода, и получить от него выбор ввода для одной операции, которая ему нужна (добавление окружающего div).

Когда вы вставляете или добавляете элемент в выбор enter(), он добавляется к выбору обновлений, с которым вы можете работать позже. Это означает, что вы можете присоединиться к данным, а затем добавить div с выбором ввода, а затем, когда вы добавите его с выбором обновления, вы будете добавлять в div, которые вы добавили в поле ввода:

var cookies = [
  { "label": "sugar", "text": "Sugar Cookie", "img": "sugar.jpg" },
  { "label": "chocolate", "text": "Chocolate Cookie", "img": "chocolate.jpg" }];

var cookie = d3.select("#cookie-jar").selectAll().data(cookies);
cookie.enter().append("div");
cookie.append("p").text(function(d){ return d.text });
cookie.append("img").attr("src",function(d){ return d.img });
#cookie-jar div { border: solid 1px black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="cookie-jar"></div>