Uncaught (in promise) TypeError: Cannot read property 'slice' of undefined

Uncaught (in promise) TypeError: Cannot read property 'slice' of undefined

我正在构建一个示例食谱应用程序,名为:使用 Javascript、NPM、Babel 和 Webpack 进行 Forkify,我在其中使用自定义 API
API URL : forkify-api.herokuapp.com

搜索结果

Returns 特定查询的食谱列表

路径:https://forkify-api.herokuapp.com/api/search

示例 URL:https://forkify-api.herokuapp.com/api/search?q=pizza

GET

Returns关于特定配方的详细信息

路径:https://forkify-api.herokuapp.com/api/get

示例 URL:https://forkify-api.herokuapp.com/api/get?rId=47746

当我 运行 使用命令 npm start 运行 在命令行中进行项目时,当我在搜索框中输入查询披萨时,出现以下错误(附上图片)

下面是代码文件:

index.js

/*
Global state of the app
- search object
- current recipe object
- shopping list object
- liked recipe
*/

import Search from "./models/Search";
import Recipe from "./models/Recipe";
import * as searchView from "./views/searchView";
import { elements, renderLoader, clearLoader } from "./views/base";
const state = {};

/* SEARCH CONTROLLER */

const controlSearch = async () => {
    // 1. Get query from the view.
    const query = searchView.getInput(); //TODO
    // console.log(query);
    if (query) {
        // 2. New search object and add it to state.
        state.search = new Search(query);

        // 3. Prepare UI for results.
        searchView.clearinput();
        searchView.clearResults();
        renderLoader(elements.searchRes);

        // 4. Search for recipes.
        await state.search.getResults();

        // 5. Render results on UI.
        clearLoader();
        searchView.renderResults(state.search.result);
    }
}

elements.searchForm.addEventListener("submit", e => {
    e.preventDefault();
    controlSearch();
});

elements.searchResPages.addEventListener("click",e=>{
    const btn=e.target.closest(".btn-inline");
    if (btn) {
        const goToPage=parseInt(btn.dataset.goto,10);
        searchView.clearResults();
        searchView.renderResults(state.search.result,goToPage);
    }
});

Search.js

import axios from "axios";
// import {proxy} from "../config";
export default class Search{
    constructor(query){
        this.query=query;
    }

    async getResults() {
        try{
        const res = axios(`https://forkify-api.herokuapp.com/api/search?q=${this.query}`);
        this.result = res.data.recipes;
        // console.log(this.result);
        }
        catch(error){
            alert(error);
        }
    };

}

searchView.js

import { elements } from "./base";

export const getInput = () => elements.searchInput.value;

export const clearinput = () => {
    elements.searchInput.value = "";
};

export const clearResults = () => {
    elements.searchResList.innerHTML = "";
    elements.searchResPages.innerHTML = "";
};

/*
"pasta with tomato and spinach"
acc:0/acc+curr.length=5 /newTitle =['pasta']
acc:5/acc+curr.length=9 /newTitle =['pasta','with']
acc:9/acc+curr.length=15 /newTitle =['pasta','with','tomato']
acc:15/acc+curr.length=18 /newTitle =['pasta','with','tomato']
acc:18/acc+curr.length=25 /newTitle =['pasta','with','tomato']
*/

const limitRecipeTitle = (title, limit = 17) => {
    const newTitle = [];
    if (title.length > limit) {
        title.split(" ").reduce((acc, curr) => {
            if (acc + curr.length <= limit) {
                newTitle.push(curr);
            }
            return acc + curr.length;
        }, 0);
        // return the results
        return `${newTitle.join(' ')}...`;
    }
    return title;
};

const renderRecipe = recipe => {
    const markup = `
    <li>
        <a class="results__link" href="#${recipe.recipe_id}">
            <figure class="results__fig">
                <img src="${recipe.image_url}" alt="${recipe.title}">
            </figure>
            <div class="results__data">
                <h4 class="results__name">${limitRecipeTitle(recipe.title)}</h4>
                 <p class="results__author">${recipe.publisher}</p>
            </div>
        </a>
    </li>
    `;
    elements.searchResList.insertAdjacentHTML("beforeend", markup);
};

// type: "prev" or "next"
const createButton = (page, type) => `
<button class="btn-inline results__btn--${type}" data-goto=${type === "prev" ? page - 1 : page + 1}>
<span>Page ${ type === "prev" ? page - 1 : page + 1}</span>
<svg class="search__icon">
    <use href="img/icons.svg#icon-triangle-${ type === "prev" ? "left" : "right"}"></use>
</svg>
</button>
`


const renderButtons = (page, numResults, resPerPage) => {
    const pages = Math.ceil(numResults / resPerPage);
    let button;
    if (page === 1 && pages > 1) {
        // Only button to go to next page.
        button = createButton(page, "next");
    }
    else if (page < pages) {
        // Both buttons
        button = `
        ${createButton(page, "prev")}
        ${createButton(page, "next")}
        `;
    }
    else if (page === pages && pages > 1) {
        // Only button to go to previous page.
        button = createButton(page, "prev");
    }
    elements.searchResPages.insertAdjacentHTML("afterbegin", button);
}

export const renderResults = (recipes, page = 1, resPerPage = 10) => {
    // render results of current page
    const start = (page - 1) * resPerPage;
    const end = page * resPerPage;
    // recipes.slice(start,end).forEach(renderRecipe);

    recipes.slice(start, end).forEach(renderRecipe);

    // render pagination buttons
    renderButtons(page, recipes.length, resPerPage);
};

Recipe.js

import axios from "axios";
// import {key} from "../config";
export default class Recipe{
    constructor (id){
        this.id=id;
    }

    async getRecipe(){
        try {
            const res=await axios(`https://forkify-api.herokuapp.com/api/get?rId=${this.query}`);
            console.log(res);
        } catch (error) {
            console.log(error);
        }
    }
};

base.js

export const elements = {
    searchForm: document.querySelector(".search"),
    searchInput: document.querySelector(".search__field"),
    searchRes: document.querySelector(".results"),
    searchResList: document.querySelector(".results__list"),
    searchResPages:document.querySelector(".results__pages")
};

export const elementStrings = {
    loader: "loader"
};

export const renderLoader = parent => {
    const loader = `
    <div class="${elementStrings.loader}">
        <svg>
            <use href="img/icons.svg#icon-cw">
            </use>
        </svg>
    </div>
    `;
    parent.insertAdjacentHTML("afterbegin", loader);
};

export const clearLoader = () => {
    const loader = document.querySelector(`.${elementStrings.loader}`);
    if (loader) loader.parentElement.removeChild(loader);
};

有什么解决办法吗?

您在 Search.js 中的 axios 调用中缺少 await 关键字:

const res = await axios(`https://forkify-api.herokuapp.com/api/search?q=${this.query}`);
this.result = res.data.recipes;

这就是您看到错误的原因:

TypeError: Cannot read property 'recipes' of undefined

然后 undefined 被传递给 renderResults,这就是您看到控制台错误的原因:

TypeError: Cannot read property 'slice' of undefined

可以将recipes参数默认为空数组,保证即使传了undefined也能调用slice

export const renderResults = (recipes = [], page = 1, resPerPage = 10) => {
  // ...
}