通过异步 API 调用加载数据后,如何渲染数据并初始化此数据的 UI 事件处理?

问题描述 投票:0回答:4

我在名为 data.json 的文件中有一些嵌套的 Json。我正在使用

fetch
读取文件,然后希望根据用户是否在网站的下拉列表中选择了特定选项来进行一些过滤。

var jsonData = [{"type": "FeatureCollection",
       "features": [
          {"type": 'Feature', "properties": {"id": 1}, "geometry": {"type": "Point", "coordinates": [-80.71, 28.34]}},
          {"type": 'Feature', "properties": {"id": 2}, "geometry": {"type": "Point", "coordinates": [-79.89, 28.45]}},
          {"type": 'Feature', "properties": {"id": 2}, "geometry": {"type": "Point", "coordinates": [-60.79, 28.32]}}
       ]}
]

我想根据

"properties": {"id": #}
字段对这一系列特征进行一些过滤。例如。如果用户选择与该 id 匹配的值,请将其保留在我的结果显示数据中,否则将其删除。

我正在尝试通过如下的 Promise 来做一些事情。我是 JavaScript 新手,但在下面的代码中使用

.filter
是我尝试让它工作的尝试。

我想要实现的理想解决方案:

我正在美国地图上显示数据地图,其中包含与属于我提到的

id
字段的某些位置相关的点。我希望用户能够通过 Javascript 中的下拉工具单击其中一个 ID,然后通过他们的选择,将 JSON 数据过滤为仅属于该 ID 的功能。例如。您对数据使用的传统过滤器。

function filterIds(data) {
    let idFilter= document.getElementById("id-filter");
    let selection = idFilter.addEventListener('change', function(event) {
        return this.value;
    });

    data.features.map((element) => {
        // spread out our array of data and filter on specific property (namely, the id key)
        return {...element, properties: element.filter((property) => property.id=== selection)};
    });

async function getData(url) {
    let response = await fetch(url);
    return await response.json();
};
getData("../data/processed/data.json") // fetch raw data
    .then(data => filterIds(data));
javascript asynchronous data-structures event-handling rendering
4个回答
2
投票

假设OP最初需要保持获取的数据与下拉列表同步...下拉选项的id值毕竟必须反映获取数据的

features
数组的特征项...建议的步骤主要初始化过程如下...

  1. 从获取数据开始。
  2. 从解析数据的
    features
    数组中创建特定于id的特征项的地图/索引。因此,稍后甚至可以避免过滤任务,因为只需根据所选 id 查找特定于 id 的功能列表。
  3. 根据 ids 列表渲染下拉选项,这些 ids 是刚刚创建的地图的键。
  4. 实施
    'change'
    处理。 (以下示例代码支持处理程序的显式数据绑定方法)。

// ... mocked API call ...
async function fetchData() {
  return new Promise(resolve =>
    setTimeout(() => resolve({
      data: [{
        "type": "FeatureCollection",
        "features": [{
          "type": 'Feature',
          "properties": {
            "id": 1
          },
          "geometry": {
            "type": "Point",
            "coordinates": [-80.71, 28.34]
          }
        }, {
          "type": 'Feature',
          "properties": {
            "id": 2
          },
          "geometry": {
            "type": "Point",
            "coordinates": [-79.89, 28.45]
          }
        }, {
          "type": 'Feature',
          "properties": {
            "id": 2
          },
          "geometry": {
            "type": "Point",
            "coordinates": [-60.79, 28.32]
          }
        }]
      }]
    }), 2000)
  );
}

// one time data transformation in order to avoid filtering later.
function getFeaturesMapGroupedByPropertiesId(featureList) {
  return featureList.reduce((map, featureItem) => {

    const { properties: { id } } = featureItem;

    (map[id] ??= []).push(featureItem);

    return map;

  }, {});
}

function renderDropdownOptions(node, idList) {
  const { options } = node;

  // delete/reset the `options` collection.
  options.length = 0;
  // put/add initial default selected option item.
  options.add(new Option('select an id', '', true, true));

  idList.forEach(id =>
    options.add(new Option(`feature ${ id }`, id))
  );
}

function handleFeatureChangeWithBoundFeaturesMap({ currentTarget }) {
  const idBasedFeaturesMap = this;
  const featureId = currentTarget.value;

  console.log(
    `id specific feature list for id "${ featureId }" ...`,
    idBasedFeaturesMap[featureId],
  );
}

async function main() {
  console.log('... trigger fetching data ...');

  const { data } = await fetchData();
  console.log('... fetching data ... done ...', { data });

  const idBasedFeaturesMap =
    getFeaturesMapGroupedByPropertiesId(data[0].features);

  // console.log({ idBasedFeaturesMap });
  // //console.log(Object.keys(idBasedFeaturesMap));

  const dropdownNode = document.querySelector('select#feature');
  if (dropdownNode) {
  
    console.log('... synchronize dropdown data ...');

    renderDropdownOptions(
      dropdownNode,
      Object.keys(idBasedFeaturesMap),
    );
    dropdownNode
      .addEventListener(
        'change',
        handleFeatureChangeWithBoundFeaturesMap.bind(idBasedFeaturesMap)
      );  
    console.log('... synchronize dropdown data ... done!');
  }
}
main();
.as-console-wrapper {
  min-height: 100%;
  width: 80%;
  left: auto!important;
}
body { margin: 0; }
<select id="feature">
  <option value="">... initializing ...</option>
  <!--
  <option value="1">feature 1</option>
  <option value="2">feature 2</option>
  //-->
</select>


0
投票

这里有一个可以尝试的技术,它将流程稍微改变为:

  • 用户加载页面
  • 页面为下拉列表分配一个事件监听器
  • 页面将 data.json 加载到全局变量中

仅当用户更改下拉列表时,它才会检查以确保 data.json 已加载,然后执行所需的过滤。

// global var to store "data" from getData
let processedData;

// assign the listener once, on page load
document.getElementById("id-filter").addEventListener('change', function(event) {
  if (!processedData) return; // still no processedData available
  
  let selection = this.value;
  
  let newData = processedData.features.map((element) => {
    // spread out our array of data and filter on specific property (namely, the id key)
    return {...element, properties: element.filter((property) => property.id === selection)};
  });

  // do something with newData
});


const setData = (data) => {
  // this just stores data in the global processedData variable
  processedData = data;
};

async function getData(url) {
    let response = await fetch(url);
    let data = await response.json();
    return data;
};

// call getData once, on page load
getData("../data/processed/data.json") // fetch raw data
    .then(data => setData(data)); 


0
投票

您需要对过滤功能进行一些更改 -

function filterIds(data) {
  let idFilter= document.getElementById("id-filter");
  let value = null;
  let selection = idFilter.addEventListener('change', function(event) {
    value = this.value;
});
data.features.map((element) => {
      const properties = element.filter((property) => 
      property.id=== value);
      return {...element, properties};
});
}

要修复

Uncaught (in promise) TypeError: element.filter is not a function
,您需要对 getData 函数进行以下更改 -

async function getData(url) {return await fetch(url).then((res) => res.json())};

此外,您必须在全局级别的过滤器函数之外设置事件侦听器。


0
投票

也许没有必要进行过滤,因为您是在它之前建立索引的。 根据您正在做的事情,它可能会更快。

表格或图表的基本示例。

var jsonData = [{"type": "FeatureCollection",
       "features": [
          {"type": 'Feature', "properties": {"id": 1}, "geometry": {"type": "Point", "coordinates": [-80.71, 28.34]}},
          {"type": 'Feature', "properties": {"id": 2}, "geometry": {"type": "Point", "coordinates": [-79.89, 28.45]}},
          {"type": 'Feature', "properties": {"id": 2}, "geometry": {"type": "Point", "coordinates": [-60.79, 28.32]}}
       ]}
]

// Create a select element
document.body.append(Object.assign(document.createElement("select"), {id: "select" }))

// Count features and create options
for (let i = 0; i < jsonData[0].features.length; i++){
  document.getElementById("select").append(
    Object.assign(document.createElement("option"), {textContent: i })
  )
}

// Read by index at selection change
select.addEventListener("change", (e) => {
  let selected = +e.target.selectedOptions[0].textContent // Cast to integer
  console.log(jsonData[0].features[selected])
})

© www.soinside.com 2019 - 2024. All rights reserved.