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),
...
}
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.
.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.