Więc obecnie używam tego kodu do renderowania listy elementów i (miejmy nadzieję) wyznaczenia aktywnego elementu, który może się zmienić po kliknięciu jednego z elementów.

let active_experiment_id = null;

function set_experiments() {
    const u = d3.select('#experiments')
          .selectAll('li')
          .data(Object.values(experiments).sort((a, b) => {
              const a_time = moment(a['created']),
                    b_time = moment(b['created']);
              if (a_time.isBefore(b_time))
                  return 1;
              else if (a_time.isAfter(b_time))
                  return -1;
              else
                  return 0;
          }), (d) => d);

    const entering = u.enter()
          .append('li')
          .classed('nav-item', true)
          .classed('active', (d) => d.active_experiment)
          .on('click', d => {
              if (d.experiment_id !== active_experiment_id)
                  set_active_experiment(d.experiment_id);
          })
          .append('a')
          .classed('nav-link', true)
          .classed('active', (d) => d.active_experiment)
          .append('p')
          .text((d) => { return d.name; });

    u.exit().remove();
}

function set_active_experiment(experiment_id) {
    if (experiment_id === null) {
        active_experiment_id = null;
        set_classes(null);
        set_workflows(null);
        set_tasks(null);
        set_seleced_task(null, null);
        return;
    }
    if (active_experiment_id !== null)
        experiments[active_experiment_id].active_experiment = false;
    active_experiment_id = experiment_id;
    experiments[active_experiment_id].active_experiment = true;

    fetch_tasks(experiment_id)
        .then(() => {
            set_experiments();
            set_classes(experiment_id);
            set_workflows(experiment_id);
            set_tasks(experiment_id);
            set_selected_task(experiment_id, experiments[experiment_id].root_task_id);
        });
}

Obecnie mam na liście dwa elementy danych i mogę kliknąć drugi i pobrać klasę active do zastosowania. Jednak nie mogę uzyskać pierwszego kliknięcia. Próbowałem debugować, ale funkcja enter nie została nawet wywołana przy pierwszym zadaniu (Experiment_id = 2 poniżej). Elementy wyglądają następująco:

experiments = {1: {
  experiment_id: 1,
  name: "Experiment One",
  created: (before 2)
  ...
}, 2: {
  experiment_id: 2,
  name: "Experiment Two",
  created: (after 1),
  ...
}
0
iHowell 20 listopad 2019, 08:07
1
Niezwiązane z Twoim problemem, a bardziej kwestia stylu i gustu: całe powiązanie danych można znacznie uprościć do .data(Object.values(experiments).sort((a, b) => moment(b.created).diff(moment(a.created))), d => d). moment.fn.diff() oceni do znaku, który Cię interesuje, dla funkcji porównawczej.
 – 
altocumulus
20 listopad 2019, 19:40

1 odpowiedź

Myślę, że rozwiązałem problem. Ponieważ użyłem Object.values, jakoś tylko drugi eksperyment utrzymał swoją pozycję. Następnie, ponieważ dane zostały bezpośrednio powiązane przez odniesienie (przy użyciu klucza (d) => d), d3 porówna ze sobą element jako kluczowy test. Tak więc, bez względu na wybraną metodę klucza, d3 nadal porównywałby klucz obiektu ze sobą i nie rejestrował, że obiekt został zaktualizowany. Aby to naprawić, za każdym razem tworzę rzutowaną kopię danych i używam nowej metody klucza, aby zmniejszyć liczbę obiektów przechodzących przez enter i exit.

function set_experiments() {
    const key = d => `${d.experiment_id}${d.active_experiment}`;

    const data = Object.values(experiments).sort((a, b) => {
        const a_time = moment(a['created']),
              b_time = moment(b['created']);
        if (a_time.isBefore(b_time))
            return 1;
        else if (a_time.isAfter(b_time))
            return -1;
        else
            return 0;
    })
          .map(d => ({
              "experiment_id": d.experiment_id,
              "created": d.created,
              "name": d.name,
              "active_experiment": d.active_experiment,
          }));

    const u = d3.select('#experiments')
          .selectAll('li')
          .data(data, key);

    const entering = u.enter()
          .append('li')
          .classed('nav-item', true)
          .classed('active', (d) => {
              num_entered++;
              return d.active_experiment;
          })
          .on('click', d => {
              if (d.experiment_id !== active_experiment_id) {
                  set_active_experiment(d.experiment_id);
              }
          })
          .append('a')
          .classed('nav-link', true)
          .classed('active', (d) => {
              return d.active_experiment;
          })
          .append('p')
          .text((d) => { return d.name; });

    u.exit().remove();
}

Chociaż nie jest to najbardziej wydajne, ponieważ nadal muszę skopiować wszystkie dane, skopiowane dane nie wpływają na DOM. Tylko zaktualizowane wartości są takie, ponieważ nowa metoda klucza usuwa zbędne dodawanie i usuwanie.

0
iHowell 20 listopad 2019, 18:08