I get this error
TypeError: Cannot read property 'slice' of undefined
at Object.renderResults (searchView.js:93)
at _callee$ (index.js:85)
at tryCatch (runtime.js:65)
at Generator.invoke [as _invoke] (runtime.js:303)
at Generator.prototype.(:8081/anonymous function) [as next] (webpack:///./node_modules/babel-polyfill/node_modules/regenerator-runtime/runtime.js?:117:21)
at step (index.js:41)
at eval (index.js:41)
after searching for let's say 'pizza' in the search bar in my project, it's my GitHub repo:
I believe there might be an error in this part of the code:
export const renderResults = (recipes, page = 1, resPerPage = 10) => {
// render results of currente page
const start = (page - 1) * resPerPage;
const end = page * resPerPage;
recipes.slice(start, end).forEach(recipe => renderRecipe(recipe));
// render pagination buttons
renderButtons(page, recipes.length, resPerPage);
};
in file 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 = '';
};
export const highlightSelected = id => {
const resultsArr = Array.from(document.querySelectorAll('.results__link'));
resultsArr.forEach(el => {
el.classList.remove('results__link--active');
});
document.querySelector(`.results__link[href*="${id}"]`).classList.add('results__link--active');
};
/*
// 'Pasta with tomato and spinach'
acc: 0 / acc + cur.length = 5 / newTitle = ['Pasta']
acc: 5 / acc + cur.length = 9 / newTitle = ['Pasta', 'with']
acc: 9 / acc + cur.length = 15 / newTitle = ['Pasta', 'with', 'tomato']
acc: 15 / acc + cur.length = 18 / newTitle = ['Pasta', 'with', 'tomato']
acc: 18 / acc + cur.length = 24 / newTitle = ['Pasta', 'with', 'tomato']
*/
export const limitRecipeTitle = (title, limit = 17) => {
const newTitle = [];
if (title.length > limit) {
title.split(' ').reduce((acc, cur) => {
if (acc + cur.length <= limit) {
newTitle.push(cur);
}
return acc + cur.length;
}, 0);
// return the result
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 prev page
button = createButton(page, 'prev');
}
elements.searchResPages.insertAdjacentHTML('afterbegin', button);
};
export const renderResults = (recipes, page = 1, resPerPage = 10) => {
// render results of currente page
const start = (page - 1) * resPerPage;
const end = page * resPerPage;
recipes.slice(start, end).forEach(recipe => renderRecipe(recipe));
// render pagination buttons
renderButtons(page, recipes.length, resPerPage);
};
The code which calls renderResults
function is in index.js
file. Here is that part of the code:
/**
* SEARCH CONTROLLER
*/
const controlSearch = async () => {
// 1) Get query from view
const query = searchView.getInput();
if (query) {
// 2) New search object and add to state
state.search = new Search(query);
// 3) Prepare UI for results
searchView.clearInput();
searchView.clearResults();
renderLoader(elements.searchRes);
try {
// 4) Search for recipes
await state.search.getResults();
// 5) Render results on UI
clearLoader();
searchView.renderResults(state.search.result);
} catch (err) {
console.log(err);
alert('Something wrong with the search...');
clearLoader();
}
}
}
elements.searchForm.addEventListener('submit', e => {
e.preventDefault();
controlSearch();
});
and full index.js file:
import Search from './models/Search';
import Recipe from './models/Recipe';
import List from './models/List';
import Likes from './models/Likes';
import * as searchView from './views/searchView';
import * as recipeView from './views/recipeView';
import * as listView from './views/listView';
import * as likesView from './views/likesView';
import { elements, renderLoader, clearLoader } from './views/base';
/** Global state of the app
* - Search object
* - Current recipe object
* - Shopping list object
* - Liked recipes
*/
const state = {};
/**
* SEARCH CONTROLLER
*/
const controlSearch = async () => {
// 1) Get query from view
const query = searchView.getInput();
if (query) {
// 2) New search object and add to state
state.search = new Search(query);
// 3) Prepare UI for results
searchView.clearInput();
searchView.clearResults();
renderLoader(elements.searchRes);
try {
// 4) Search for recipes
await state.search.getResults();
// 5) Render results on UI
clearLoader();
searchView.renderResults(state.search.result);
} catch (err) {
console.log(err);
alert('Something wrong with the search...');
clearLoader();
}
}
}
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);
}
});
/**
* RECIPE CONTROLLER
*/
const controlRecipe = async () => {
// Get ID from url
const id = window.location.hash.replace('#', '');
if (id) {
// Prepare UI for changes
recipeView.clearRecipe();
renderLoader(elements.recipe);
// Highlight selected search item
if (state.search) searchView.highlightSelected(id);
// Create new recipe object
state.recipe = new Recipe(id);
try {
// Get recipe data and parse ingredients
await state.recipe.getRecipe();
state.recipe.parseIngredients();
// Calculate servings and time
state.recipe.calcTime();
state.recipe.calcServings();
// Render recipe
clearLoader();
recipeView.renderRecipe(
state.recipe,
state.likes.isLiked(id)
);
} catch (err) {
console.log(err);
alert('Error processing recipe!');
}
}
};
['hashchange', 'load'].forEach(event => window.addEventListener(event, controlRecipe));
/**
* LIST CONTROLLER
*/
const controlList = () => {
// Create a new list IF there in none yet
if (!state.list) state.list = new List();
// Add each ingredient to the list and UI
state.recipe.ingredients.forEach(el => {
const item = state.list.addItem(el.count, el.unit, el.ingredient);
listView.renderItem(item);
});
}
// Handle delete and update list item events
elements.shopping.addEventListener('click', e => {
const id = e.target.closest('.shopping__item').dataset.itemid;
// Handle the delete button
if (e.target.matches('.shopping__delete, .shopping__delete *')) {
// Delete from state
state.list.deleteItem(id);
// Delete from UI
listView.deleteItem(id);
// Handle the count update
} else if (e.target.matches('.shopping__count-value')) {
const val = parseFloat(e.target.value, 10);
state.list.updateCount(id, val);
}
});
/**
* LIKE CONTROLLER
*/
const controlLike = () => {
if (!state.likes) state.likes = new Likes();
const currentID = state.recipe.id;
// User has NOT yet liked current recipe
if (!state.likes.isLiked(currentID)) {
// Add like to the state
const newLike = state.likes.addLike(
currentID,
state.recipe.title,
state.recipe.author,
state.recipe.img
);
// Toggle the like button
likesView.toggleLikeBtn(true);
// Add like to UI list
likesView.renderLike(newLike);
// User HAS liked current recipe
} else {
// Remove like from the state
state.likes.deleteLike(currentID);
// Toggle the like button
likesView.toggleLikeBtn(false);
// Remove like from UI list
likesView.deleteLike(currentID);
}
likesView.toggleLikeMenu(state.likes.getNumLikes());
};
// Restore liked recipes on page load
window.addEventListener('load', () => {
state.likes = new Likes();
// Restore likes
state.likes.readStorage();
// Toggle like menu button
likesView.toggleLikeMenu(state.likes.getNumLikes());
// Render the existing likes
state.likes.likes.forEach(like => likesView.renderLike(like));
});
// Handling recipe button clicks
elements.recipe.addEventListener('click', e => {
if (e.target.matches('.btn-decrease, .btn-decrease *')) {
// Decrease button is clicked
if (state.recipe.servings > 1) {
state.recipe.updateServings('dec');
recipeView.updateServingsIngredients(state.recipe);
}
} else if (e.target.matches('.btn-increase, .btn-increase *')) {
// Increase button is clicked
state.recipe.updateServings('inc');
recipeView.updateServingsIngredients(state.recipe);
} else if (e.target.matches('.recipe__btn--add, .recipe__btn--add *')) {
// Add ingredients to shopping list
controlList();
} else if (e.target.matches('.recipe__love, .recipe__love *')) {
// Like controller
controlLike();
}
});
I get this error
TypeError: Cannot read property 'slice' of undefined
at Object.renderResults (searchView.js:93)
at _callee$ (index.js:85)
at tryCatch (runtime.js:65)
at Generator.invoke [as _invoke] (runtime.js:303)
at Generator.prototype.(:8081/anonymous function) [as next] (webpack:///./node_modules/babel-polyfill/node_modules/regenerator-runtime/runtime.js?:117:21)
at step (index.js:41)
at eval (index.js:41)
after searching for let's say 'pizza' in the search bar in my project, it's my GitHub repo: https://github.com/damianjnc/finalforkify
I believe there might be an error in this part of the code:
export const renderResults = (recipes, page = 1, resPerPage = 10) => {
// render results of currente page
const start = (page - 1) * resPerPage;
const end = page * resPerPage;
recipes.slice(start, end).forEach(recipe => renderRecipe(recipe));
// render pagination buttons
renderButtons(page, recipes.length, resPerPage);
};
in file 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 = '';
};
export const highlightSelected = id => {
const resultsArr = Array.from(document.querySelectorAll('.results__link'));
resultsArr.forEach(el => {
el.classList.remove('results__link--active');
});
document.querySelector(`.results__link[href*="${id}"]`).classList.add('results__link--active');
};
/*
// 'Pasta with tomato and spinach'
acc: 0 / acc + cur.length = 5 / newTitle = ['Pasta']
acc: 5 / acc + cur.length = 9 / newTitle = ['Pasta', 'with']
acc: 9 / acc + cur.length = 15 / newTitle = ['Pasta', 'with', 'tomato']
acc: 15 / acc + cur.length = 18 / newTitle = ['Pasta', 'with', 'tomato']
acc: 18 / acc + cur.length = 24 / newTitle = ['Pasta', 'with', 'tomato']
*/
export const limitRecipeTitle = (title, limit = 17) => {
const newTitle = [];
if (title.length > limit) {
title.split(' ').reduce((acc, cur) => {
if (acc + cur.length <= limit) {
newTitle.push(cur);
}
return acc + cur.length;
}, 0);
// return the result
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 prev page
button = createButton(page, 'prev');
}
elements.searchResPages.insertAdjacentHTML('afterbegin', button);
};
export const renderResults = (recipes, page = 1, resPerPage = 10) => {
// render results of currente page
const start = (page - 1) * resPerPage;
const end = page * resPerPage;
recipes.slice(start, end).forEach(recipe => renderRecipe(recipe));
// render pagination buttons
renderButtons(page, recipes.length, resPerPage);
};
The code which calls renderResults
function is in index.js
file. Here is that part of the code:
/**
* SEARCH CONTROLLER
*/
const controlSearch = async () => {
// 1) Get query from view
const query = searchView.getInput();
if (query) {
// 2) New search object and add to state
state.search = new Search(query);
// 3) Prepare UI for results
searchView.clearInput();
searchView.clearResults();
renderLoader(elements.searchRes);
try {
// 4) Search for recipes
await state.search.getResults();
// 5) Render results on UI
clearLoader();
searchView.renderResults(state.search.result);
} catch (err) {
console.log(err);
alert('Something wrong with the search...');
clearLoader();
}
}
}
elements.searchForm.addEventListener('submit', e => {
e.preventDefault();
controlSearch();
});
and full index.js file:
import Search from './models/Search';
import Recipe from './models/Recipe';
import List from './models/List';
import Likes from './models/Likes';
import * as searchView from './views/searchView';
import * as recipeView from './views/recipeView';
import * as listView from './views/listView';
import * as likesView from './views/likesView';
import { elements, renderLoader, clearLoader } from './views/base';
/** Global state of the app
* - Search object
* - Current recipe object
* - Shopping list object
* - Liked recipes
*/
const state = {};
/**
* SEARCH CONTROLLER
*/
const controlSearch = async () => {
// 1) Get query from view
const query = searchView.getInput();
if (query) {
// 2) New search object and add to state
state.search = new Search(query);
// 3) Prepare UI for results
searchView.clearInput();
searchView.clearResults();
renderLoader(elements.searchRes);
try {
// 4) Search for recipes
await state.search.getResults();
// 5) Render results on UI
clearLoader();
searchView.renderResults(state.search.result);
} catch (err) {
console.log(err);
alert('Something wrong with the search...');
clearLoader();
}
}
}
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);
}
});
/**
* RECIPE CONTROLLER
*/
const controlRecipe = async () => {
// Get ID from url
const id = window.location.hash.replace('#', '');
if (id) {
// Prepare UI for changes
recipeView.clearRecipe();
renderLoader(elements.recipe);
// Highlight selected search item
if (state.search) searchView.highlightSelected(id);
// Create new recipe object
state.recipe = new Recipe(id);
try {
// Get recipe data and parse ingredients
await state.recipe.getRecipe();
state.recipe.parseIngredients();
// Calculate servings and time
state.recipe.calcTime();
state.recipe.calcServings();
// Render recipe
clearLoader();
recipeView.renderRecipe(
state.recipe,
state.likes.isLiked(id)
);
} catch (err) {
console.log(err);
alert('Error processing recipe!');
}
}
};
['hashchange', 'load'].forEach(event => window.addEventListener(event, controlRecipe));
/**
* LIST CONTROLLER
*/
const controlList = () => {
// Create a new list IF there in none yet
if (!state.list) state.list = new List();
// Add each ingredient to the list and UI
state.recipe.ingredients.forEach(el => {
const item = state.list.addItem(el.count, el.unit, el.ingredient);
listView.renderItem(item);
});
}
// Handle delete and update list item events
elements.shopping.addEventListener('click', e => {
const id = e.target.closest('.shopping__item').dataset.itemid;
// Handle the delete button
if (e.target.matches('.shopping__delete, .shopping__delete *')) {
// Delete from state
state.list.deleteItem(id);
// Delete from UI
listView.deleteItem(id);
// Handle the count update
} else if (e.target.matches('.shopping__count-value')) {
const val = parseFloat(e.target.value, 10);
state.list.updateCount(id, val);
}
});
/**
* LIKE CONTROLLER
*/
const controlLike = () => {
if (!state.likes) state.likes = new Likes();
const currentID = state.recipe.id;
// User has NOT yet liked current recipe
if (!state.likes.isLiked(currentID)) {
// Add like to the state
const newLike = state.likes.addLike(
currentID,
state.recipe.title,
state.recipe.author,
state.recipe.img
);
// Toggle the like button
likesView.toggleLikeBtn(true);
// Add like to UI list
likesView.renderLike(newLike);
// User HAS liked current recipe
} else {
// Remove like from the state
state.likes.deleteLike(currentID);
// Toggle the like button
likesView.toggleLikeBtn(false);
// Remove like from UI list
likesView.deleteLike(currentID);
}
likesView.toggleLikeMenu(state.likes.getNumLikes());
};
// Restore liked recipes on page load
window.addEventListener('load', () => {
state.likes = new Likes();
// Restore likes
state.likes.readStorage();
// Toggle like menu button
likesView.toggleLikeMenu(state.likes.getNumLikes());
// Render the existing likes
state.likes.likes.forEach(like => likesView.renderLike(like));
});
// Handling recipe button clicks
elements.recipe.addEventListener('click', e => {
if (e.target.matches('.btn-decrease, .btn-decrease *')) {
// Decrease button is clicked
if (state.recipe.servings > 1) {
state.recipe.updateServings('dec');
recipeView.updateServingsIngredients(state.recipe);
}
} else if (e.target.matches('.btn-increase, .btn-increase *')) {
// Increase button is clicked
state.recipe.updateServings('inc');
recipeView.updateServingsIngredients(state.recipe);
} else if (e.target.matches('.recipe__btn--add, .recipe__btn--add *')) {
// Add ingredients to shopping list
controlList();
} else if (e.target.matches('.recipe__love, .recipe__love *')) {
// Like controller
controlLike();
}
});
Share
Improve this question
edited May 19, 2024 at 15:14
Brian Tompsett - 汤莱恩
5,89372 gold badges61 silver badges133 bronze badges
asked Jan 20, 2019 at 20:37
DamianDamian
1131 gold badge2 silver badges9 bronze badges
2
|
6 Answers
Reset to default 4I was also getting the same error.
After getting this error I knew about that when array or anything which you use with slice()
undefined or null.
That's why this error happens.
response(dd.slice(0, 20));
Error
> Index:103 Uncaught TypeError: Cannot read property 'slice' of
> undefined
> at Object.success (Index:103)
> at fire (jquery-1.12.4.js:3232)
> at Object.fireWith [as resolveWith] (jquery-1.12.4.js:3362)
> at done (jquery-1.12.4.js:9840)
> at XMLHttpRequest.callback (jquery-1.12.4.js:10311)
slice works on string so make sure dd is string or do dd.toString().slice(to,from);
I was also getting the same error.
Its probably because your usage has reached its limit 50 calls for the day.
Wait for tomorrow or create another account and use new key.
that worked for me. :)
I faced the same error today, and solved it by initializing the array to an empty array before performing any slice() operations on it.
Search the slice to find this paragraph and add a sentence:
if (!antDir || !antDir.length) {
return;
}
To determine whether the string using the slice exists, and pages with similar situations can also be solved by this method
It's because the value is undefined, You need to do a conditional check before using slice() like this, recipes?.slice(start, end)
renderResults
is being called with no arguments, or with an undefined first argument? (but the code that calls it hasn't been posted...) – CertainPerformance Commented Jan 20, 2019 at 20:38