我应该使用更改处理程序从项目类型数组中填充项目数组吗?

Should I use a change handler to populate an array of items from an array of item types?

我正在创建一个包含 Javascript 部件数组的表单。用户将从下拉菜单中 select 一个部件类型,然后从下一个下拉菜单中 select 该部件类型中的一个项目。我相信我的零件类型中的更改处理程序是可行的方法,但无法单独在我的 .js 文件中找到使用它的示例。这是我目前所拥有的:

//Creates List of Part Types
function populateTypeList() {
   let typeSelectList = document.getElementById("partTypes");
   for (const item of types) {
       let el = document.createElement("option");
       el.value = item;
       el.textContent = item;
       typeSelectList.appendChild(el);
   } 
}
//Creates Parts List when user selects Part Type
function populateSelectList() {
   let partsSelectList = document.getElementById("autoParts");
   for (const part of partArray) {
       let el = document.createElement("option");
       if (part.description.length > 30) {
           el.textContent = part.description.substring(0, 30) + " ...";
       }
       else {
           el.textContent = part.description;
       }   
       el.value = part;
       partsSelectList.appendChild(el);
   }
}

改变事件绝对是前进的方向,但您首先需要一种方法来获取过滤器列表,然后使用过滤器列表中的单个项目来创建具有该特定过滤器的部件数组。

第一步是获取整个 partArray 并使用 Set 创建类型列表。 Set 将删除所有重复值,这样列表中的过滤器类型不会超过一次。这是通过以下代码完成的。

const types = [...new Set(
  partArray.map(({ type }) => type)
)];

它遍历整个数组并使用map创建一个新数组,每个数组的值只有type。所以结果将是 ['AIR CONDITIONING', 'BODY', 'BRAKES', ...etc]。该结果被放入 Set 中,这使得删除所有重复项。然后 ... 扩展运算符用于将 Set 的每个值放入一个新数组中。

现在您需要一种从 partsArray 中获取正确部件的方法。创建一个函数来过滤 partsArray 数组和 returns 具有该特定类型的新零件数组。 filter 方法对此很有用。

const getPartsByType = filterType => partsArray
  .filter(({ type }) => type === filterType);

下面的示例使用您提供的数据和上面的方法来创建类型列表,并使用这些类型作为值将部分过滤到类型组中。

我还添加了一种方法 select 从部分 <select> 元素中选择的部分。

const parts = [{
    "id": 20,
    "status": "Active",
    "type": "AIR CONDITIONING",
    "category": "",
    "subcategory": "",
    "item": "Orifice Tube",
    "description": "Orifice Tube",
    "descriptionfull": "Orifice Tube 38623",
    "qoh": 18,
    "cost": 1.32,
    "price": 11.06
  },
  {
    "id": 62,
    "status": "Active",
    "type": "BODY",
    "category": "PANELS",
    "subcategory": "",
    "item": "Hood, Bare",
    "description": "Hood, Bare",
    "descriptionfull": "Hood, Bare",
    "qoh": 1,
    "cost": 609.15,
    "price": "1,228.29"
  },
  {
    "id": 159,
    "status": "Active",
    "type": "BRAKES",
    "category": "REAR",
    "subcategory": "Rear Brake Kit",
    "item": "Split Pin",
    "description": "Split pin for Rear Brake Kit, requires 2",
    "descriptionfull": "Split pin for the Rear Brake Kit, requires 2",
    "qoh": -2,
    "cost": 0.12,
    "price": 0.23
  }
];

const typeSelect = document.getElementById('type');
const partSelect = document.getElementById('part');
const partOutput = document.getElementById('selected-part');

/**
 * Loop over the parts array and create a new 
 * array that only contains unique types.
 */
const types = [...new Set(parts
  .map(({ type }) => type)
)];

/**
 * Function to get parts based on a type.
 * Returns an array with part objects.
 */
const getPartsByType = filterType => parts
  .filter(({ type }) => type === filterType);
  
/**
 * Function to get a single part based on
 * the item.
 */
const getPartByItem = filterItem => parts
  .find(({ item }) => item === filterItem);
  
/**
 * Take the first type from the types array and 
 * gets all the items that have that type.
 */
const initialParts = getPartsByType(types[0])
  
/**
 * Takes in description string and cuts the string down
 * to max 30 characters and adds an ellipsis if necessary.
 */
const truncate = (description, limit = 30) =>
  typeof description === 'string' && description.length > limit ?
    description.substring(0, limit) + '...' :
    description;

/**
 * Create option function.
 * Has options for setting a selected and / or disabled element.
 * A disabled and selected element makes a nice prefilled option.
 */
const createOption = (label, value, selected = false, disabled = false) => {
  const option = document.createElement('option');
  option.value = value;
  option.textContent = label;
  option.selected = selected;
  option.disabled = disabled;
  return option;
}

/**
 * Removes all options from a select element.
 */
const removeOptions = ({ options }) => {
  for (let i = options.length - 1; i >= 0; i--) {
    options.remove(i);
  }
}

/**
 * Populate types.
 */
const popuplateTypes = types => {
  for (const type of types) {
    const label = type.toLowerCase();
    const option = createOption(label, type);
    typeSelect.append(option);
  }
}

/**
 * Populate parts and remove previous parts.
 */
const populateParts = parts => {
  const defaultOption = createOption('Choose part', '', true, true);
  removeOptions(partSelect);
  partSelect.append(defaultOption);
  for (const { item, description } of parts) {
    const label = truncate(description); // Description limited to 30 characters
    const option = createOption(label, item);
    partSelect.append(option);
  }
}

// Listen for a change of type, then update the choices
// of the parts list based on that choice.
typeSelect.addEventListener('change', ({ target }) => {
  const { value } = target;
  
  // Get the parts based on the selected option value.
  const selectedParts = getPartsByType(value); 
  
  // Populate parts select with new parts.
  populateParts(selectedParts);
  
  // Clears the output element's content.
  partOutput.innerHTML = ''
});

// Listens for a change of item, then gets that single item
// from the parts array and outputs it as JSON.
partSelect.addEventListener('change', ({ target }) => {
  const { value } = target;
  
  // Get a single part based on the value.
  const part = getPartByItem(value);
  
  // Outputting the part as JSON in a <pre> tag will make it readable.
  // The <code> tag is to let the browser know it's code :).
  partOutput.innerHTML = `
    <pre><code>${JSON.stringify(part, null, 2)}</code></pre>
  `;
});

// Instantly populate the types and parts.
// The parts list will be based on the first available type
// in the types array.
popuplateTypes(types);
populateParts(initialParts);
select {
  display: block;
  text-transform: capitalize;
  width: 100%;
  padding: 0.25rem;
  margin: 0 0 1rem;
}

#selected-part {
  display: block;
  border: 1px solid rgba(118, 118, 118);
  border-radius: 0.25em;
  min-height: 5rem;
  width: 100%;
}

pre {
  padding: 0 1em;
}
<label for="type">Type</label>
<select id="type" name="type"></select>

<label for="part">Part</label>
<select id="part" name="part"></select>

<label for="output">Chosen Part</label>
<output id="selected-part"></output>