Cannot read property 'episodes' of undefined in a array [vuejs] - javascript

Problem
Hi devs,
I am building an app of movies and series with an api that I developed. I have the problem that when I want to access a video section it does not let me in since the value of season_list [season_selected - 1] .episodes is undefined.
I have three options available in the section:
1 Option: Select season
2 Option: Select chapter
3 Option: Select videos
I can enter the section if I change javascripts season_list [season_selected - 1].episodes by javascripts season_list[season_selected - 1].
Here is the example of json, particularly in the part of season_list
https://cinemanight.chrismichael.now.sh/api/v1/search/elite
You will notice that the season_list has two values, season and episodes. This is why I need to use episodes like season_list [season_selected - 1] .episodes
Error
"vue.runtime.esm.js? 2b0e: 1888 TypeError: Cannot read property 'episodes' of undefined"
The error is referring to the following line
season_list[season_selected - 1].episodes
Template Section
<select class="container" v-model="season_selected">
<option disabled value="">Temporadas</option>
<option v-for="(season , index) in Array.from({length: total_seasons}, (v , k) => k + 1)" :value="season" :key="index">
{{ season }}
</option>
</select>
<select class="container" v-model="episode_selected">
<option disabled value="">Episodios</option>
<option v-for="(epis , index) in season_list[season_selected - 1].episodes" :value="epis" :key="index">
{{ epis }}
</option>
</select>
<select class="container" v-model="option">
<option disabled value="">Videos</option>
<option v-for="(video , index) in serie_video.map(xs => xs.iframe)" :value="video" :key="index">
{{ video }}
</option>
</select>
Script Section
<script>
import {value , watch} from 'vue-function-api';
import {useState , useStore , useRouter} from '#u3u/vue-hooks';
export default{
name: 'SerieVideo',
setup(){
const store = useStore();
const {route} = useRouter();
const state = {
...useState(['serie_video' , 'isLoading'])
};
const params = {
id: value(route.value.params.id),
title: value(route.value.params.title),
sinopsis: value(route.value.params.sinopsis),
extra: value(route.value.params.extra)
};
const values = {
title: params.title.value,
sinopsis: params.sinopsis.value,
channel: params.extra.value[0].channel,
first_air_date: params.extra.value[0].first_air_date.replace('First air date' , ''),
last_air_date: params.extra.value[0].last_air_date.replace('Última transmisión' , ''),
creator_member: params.extra.value[0].cast_members,
members_list: params.extra.value[0].cast_members,
season_list: params.extra.value[0].season_list
};
const total_seasons = value(values.season_list);
const season_list = value(values.season_list);
const season_selected = value(null);
const episode_selected = value(null);
const id = value(null)
const option = value("");
watch(() =>
episode_selected.value , (value) =>{
episode_selected.value = value;
const eps = episode_selected.value;
const id = `${params.id.value.replace('ver-' , '')}-${eps}`
store.value.dispatch("GET_VIDEO_SERIES" , id)
}
);
return{
...state,
...values,
option,
season_selected: season_selected.value,
episode_selected: episode_selected.value,
season_list: season_list.value,
total_seasons: total_seasons.value.length
}
}
}
</script>
I have a solution but it has a bug
And is that in episodeList every time I select a season it makes me push and if I choose the same season again it makes me push. And I can see this quickly when I have a select for episodes, it shows me a list of the seasons I have selected and not just one.
I want to find a way not to push using episodesList
Template seccion
<select class = "container" v-model = "episode_selected">
  <option disabled value = ""> Episodes </option>
  <option v-for = "(epis, index) in episodesList [season_selected - 1]": value = "epis": key = "index">
   {{epis}}
   </option>
</select>
Sscript Seccion
const episodesList = value ([])
watch (() =>
  season_selected.value, (value) => {
  season_selected.value = value
  const selected = season_selected.value; // season selected
  episodesList.value.push (season_list [selected - 1] .episodes); // list of episodes
 }
);
FINAL SOLUTION
Problem corrected by adding the value 1 to the variable const season_selected = value(1);. Then a watch will be used to be aware that its value is updated.
Setting the value 1 will let me start the video section and having season 1 selected. But then the user can make the change in the select.

Related

2 input boxes, how to update the second value depending on the first and vice versa?

I have the following code in my jsx file
const [DataOne, setDataOne] = useState(null);
const [DataTwo, setDataTwo] = useState(null);
const [focused, setFocused] = useState(false);
function getValueOne(val) {
setDataOne(val.target.value)
console.log(val.target.value);
}
function getValueTwo(val) {
setDataTwo(val.target.value)
console.log(val.target.value);
}
const onFocus = () => setFocused(true);
return (
<div>
<div>
<select>
<option value={0}>Currency to send:</option>
<option value={1}>Dollar</option>
<option value={2}>Euro</option>
<option value={3}>Pound</option>
</select>
</div>
<div>
<select>
<option value={0}>Currency to receive:</option>
<option value={1}>Dollar</option>
<option value={2}>Euro</option>
<option value={3}>Pound</option>
</select>
</div>
<div>
<input
type={'number'}
placeholder={'Fruit in'}
onChange={getValueOne}
value={DataTwo === null ? 0 : (focused ? DataTwo / 2 : DataOne)}
onFocus={onFocus}
></input>
</div>
<div>
<input
type={'number'}
placeholder={'Fruit out'}
onChange={getValueTwo}
value={DataOne === null ? 0 : (focused ? DataOne / 2 : DataTwo)}
></input>
</div>
</div>
)
I need to send a request to api after after each change in the input field (both of them) as follows -
exchangerate-api.com?from=USD?to=EUR?val=100 (response: 94.67)
where val is a value of one of input boxes, response should fill the second input field.
Now I just try to update the remaining input box value (This is an intermediate step before I'll start to implement api fetch),
like if I type 1, 2 should appear in the second box.
If I type 2 in the second box, 1 should appear in the first.
So far I can see that the values are changing, but things are not going as planned, not even close.
Very unpredictable.
How can I update the opposite input value wherever I change the number?
You can see complex logic here
value={DataOne === null ? 0 : (focused ? DataOne / 2 : DataTwo)}
But it dosen't work, I just tried to make previous version work, here it is
value={DataOne === null ? : DataTwo)}
Obvious
value={DataOne}
Doesn't work too.
I certainly switched DataOne to DataTwo for the opposite input box every time.
I would approach this problem a teensy bit differently. As you mentioned, the logic is a bit complex, why calculate it in the value, when you can just calculate it in the onChange function?
import { useState } from 'react';
import './App.css';
function App() {
const [DataOne, setDataOne] = useState(null);
const [DataTwo, setDataTwo] = useState(null);
const [focused, setFocused] = useState(false);
function getValueOne(val) {
setDataOne(val.target.value);
setDataTwo(val.target.value * 2);// If you put 1 in, put 2 for value 2
console.log(val.target.value);
}
function getValueTwo(val) {
setDataTwo(val.target.value);
setDataOne(val.target.value / 2);// If you put 2 in, put 1 for value 1
}
const onFocus = () => setFocused(true);
// Now your DataOne and DataTwo values are calculated, so just plug them in their respective spots
return (
<div>
<div>
<select>
<option value={0}>Currency to send:</option>
<option value={1}>Dollar</option>
<option value={2}>Euro</option>
<option value={3}>Pound</option>
</select>
</div>
<div>
<select>
<option value={0}>Currency to receive:</option>
<option value={1}>Dollar</option>
<option value={2}>Euro</option>
<option value={3}>Pound</option>
</select>
</div>
<div>
<input
type={'number'}
placeholder={'Fruit in'}
onChange={getValueOne}
value={DataOne}
onFocus={onFocus}
></input>
</div>
<div>
<input
type={'number'}
placeholder={'Fruit out'}
onChange={getValueTwo}
value={DataTwo}
></input>
</div>
</div>
)
}
export default App;
This way, you do the calculations you need to do whenever an input changes

VueJS - when updating the list of options, the input label changes (the value remains the same)

I have a custom X-Select that acts like a select input. It expects two props - value and options.
The problem is that when options are reloaded in the parent component (root), they are usually prepended with a new item. In that case, select changes its label (like the index stayed the same) even if value hasn't changed.
Example:
options = [{value:'3', text:'3'},{value:'2', text:'2'},{value:'1', text:'1'},}]
current value = '2', current label = '2'
// options have been reloaded and one more item appeared at the beginning
options = [{value:'4', text:'4'},{value:'3', text:'3'},{value:'2', text:'2'},{value:'1', text:'1'},}]
current value = '2', current label = '3'
As you can see the value now does not correspond to the label. I think it's because something wasn't refreshed properly (label '2' was on index 1 and now label '3' is on that index).
<template>
<select id="type" name="type" :value="value" #input="$emit('input',$event.target.value)"
class="select form-control w-75 mx-auto">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
</template>
<script>
module.exports = {
name: "XSelect",
props: ['options', 'value'],
}
</script>
ROOT
<x-select :key="form.sourceUniAccountId" v-model="form.sourceUniAccountId"
:options="sourceInputOptions"
class="form-select"></x-select>
COMPUTED
sourceAppUniAccounts() {
if (!this.userUniAccounts) return []
return this.userUniAccounts.filter(obj => obj.name_or_provider === this.journeySourceAppName)
},
sourceInputOptions() {
return this.sourceAppUniAccounts.map(obj => ({text: obj.identifier, value: obj.uni_account_id}))
}
The variable userUniAccounts is the one that is in fact reloaded.
Do you know how to make this work? Setting key to combination of value and options works but I think there is a more "elegant" option.
<x-select :key="form.sourceUniAccountId+sourceInputOptions.length" v-model="form.sourceUniAccountId"
:options="sourceInputOptions"
class="form-select"></x-select>

filter rendered elements in react using select

I'm trying to make this app, it's a simple react app with google books API. My question is how can I filter rendered elements depending on select value? I've tried useEffect and .filter method but I just can't figure it out. Here's what I got so far:
function App() {
const[book, setBook] = useState("");
const[result, setResult] = useState([]);
const[apiKey, setApiKey] = useState("MY API KEY");
const[sortBy, setSortBy]= useState("relevance")
const [categorie, SetCategorie]= useState("all")
function handleChange(event){
const book = event.target.value;
setBook(book)
};
function handleSubmit(event){
event.preventDefault()
axios.get("https://www.googleapis.com/books/v1/volumes?q="+book+"&key="+apiKey+"&maxResults=30&orderBy="+sortBy)
.then(function(res){
setResult(res.data.items)
console.log(res.data.items)
})
};
And that's when the problem starts
useEffect(function(){
result.filter(function(book){
const filteredBooks =
book.volumeInfo.categories.includes(categorie)
console.log(book)
})
},[categorie])
return (
<div className="container mt-4 ">
<h1>Search for books API</h1>
<form onSubmit={handleSubmit}>
<div className="form-group ">
<input onChange={handleChange}
type="text" className="form-control "
autoComplete="off"
placeholder="enter book name">
</input>
</div>
<button type="submit" className="btn btn-success"> Search now</button>
<select onChange={function(event){
let selectedSortBy = event.target.value;
setSortBy(selectedSortBy)
}}>
I've also needed to change API requests with select values, but that bit seems to work fine
<option value="relevance">Relevance</option>
<option value="newest">Newest</option>
</select>
</form>
<select onChange = {function(event){
let selectedCategorie = event.target.value;
SetCategorie(selectedCategorie);
}}>
<option value="all">All</option>
<option value="Art">Art</option>
<option value="Biography">Biography</option>
<option value="Computers">Computers</option>
<option value="History">History</option>
<option value="Medical">Medical</option>
<option value="Poetry">Poetry</option>
</select>
<div class="row">
{result.map(book => {
return <div class="col">
<div class="box">
<Card
img = {book.volumeInfo.imageLinks === undefined
? ""
: `${book.volumeInfo.imageLinks.thumbnail}`}
category = {book.volumeInfo.categories}
title = {book.volumeInfo.title}
authors = {book.volumeInfo.authors}
/>
</div>
</div>
})}
</div>
</div>
);
}
export default App;
As I understand, you have select component which contains book categories list and once user changes the category you wish to show books only with selected categories.
const onCategoryChange = (event) => {
const selectedCategory = event.target.value;
const filteredBooks = result.filter((book) =>
book.volumeInfo.categories.includes(selectedCategory));
setCategories(selectedCategory);
setResult(filteredBooks);
};
<select onChange = {onCategoryChange}>
Get all categories
Filter out the books using the categories
Set categories and books.
Im not too familiar with react hooks, however if I understand correctly the function runs every time the category variable changes. The filter method needs to return truish value if you want to keep an item and falseish if not. It creates a new array which is returned. In your case you only set a const and log a value, returning void (implicit).
This should update the result to match your filtered books.
useEffect(function(){
result = result.filter(function(book){
const includesCategory = book
.volumeInfo
.categories
.includes(categorie);
console.log(book)
return includesCategory;
})
},[categorie])

How to reset items in select from dropwdown itself

I want to clear/remove/reset select options from dropdown itself and not from external button or allowClear
Let's say the icon of a trash in select option will reset all values:
However, I'm quite stuck on how to reset the value with this my current following code:
import st from "./AddToCartDropdown.module.css";
import {useState} from 'react';
import { Select } from 'antd';
import { DeleteFilled } from '#ant-design/icons';
const { Option } = Select;
function loopStock(n, selectedIndex){
var elements = [];
for(let i = 1; i <= n; i++){
const qty = new String(i);
const resultQty = qty.concat(" in cart");
elements.push(<Option value={i}> <span className={st.addToCartSelect}> {i === selectedIndex ? resultQty : i} </span></Option>);
}
return elements;
}
const AddToCart = () => {
const [selectedIndex, setSelectedIndex] = useState(-1);
const onChange = (newSelectedIndex) => {
setSelectedIndex(newSelectedIndex);
}
return (
<div >
<Select defaultValue="Add to Cart" onChange={onChange} className={st.addToCartDefault} bordered={false}>
<Option value="delete"> <DeleteFilled /> </Option>
{loopStock(5, selectedIndex)}
</Select>
</div>
);
};
export default AddToCart;
When I select the trash icon, it supposed to show me Add to Cart instead of trash icon:
The problem is I'm confused how to set the state after I selected the icon of trash in order to reset all options and go back to Add to Cart default value of Select.
I'm new to React/JavaScript and still learning. After searching all solutions, I think it's the best to create my own question in here. Thanks!
You can make your select as a controlled component by passing a value prop and having a state. set the initial state to null instead of -1 so that we can use the placeholder.
const [selectedIndex, setSelectedIndex] = React.useState(null);
when the selected value is delete we are setting the state to null
const onChange = (newSelectedIndex) => {
if(newSelectedIndex === 'delete'){
setSelectedIndex(null)
}else {
setSelectedIndex(newSelectedIndex)
}
}
Now add the value and placeholder prop in your select.
<Select placeholder="Add to Cart" value={selectedIndex} onChange={onChange} bordered={false}>
<Option value="delete"> <DeleteFilled /> </Option>
{loopStock(5, selectedIndex)}
</Select>

What should I use to get my updated count based on filter selection?

I am using CSV upload and display the data in the table along with using custom table filter pipe function.
How can I get the count of filtered based on filter section?
For e.g. Once you upload data considering your csv has 7 rows; it would be Showing 7 of 7 Records.
When you are filtering based on Gender male/female I should get Showing 4 of 7 Records or Showing 3 of 7 Records
I tried below I didn't get the results.
Showing {{filteredUsers.length}} of {{dataList.length}} Records
If I am doing like:
<strong class="ml-3" *ngFor="let record of dataList | tableFilter: form.value as filteredUsers">
Showing {{filteredUsers.length}} of {{dataList.length}} Records
</strong>
I am getting the count n updating as well based on the filter selection but it's repeating based on the number of rows so, coming 7 times.
app.component.html
<form #form="ngForm">
<select class="form-control form-control-sm" name="gender" ngModel>
<option value="" selected>All</option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</form>
<tr *ngFor="let record of dataList | tableFilter: form.value as filteredUsers">
<td> {{record.name}} </td>
<td> {{record.gender}} </td>
</tr>
Live: https://stackblitz.com/edit/angular-csv-parser-aazvnq?file=app%2Fapp.component.html
CSV Sample
I suggest a slightly different approach than Nicholas's solution - instead of updating two items every time a change is made, you can have the pipe function update the variable which represents the count - this way, a single place is responsible of handling both the count and the filtering, this requires adding an object in the component:
countObj = { count: 0 };
in the template:
Showing {{countObj.count}} Records
...
<tr *ngFor="let record of dataList | tableFilter: form.value:countObj">
and in the transform function:
transform(list: any[], filters: Object, countObj: any) {
const keys = Object.keys(filters).filter(key => filters[key]);
const filterUser = user => keys.every(key => user[key] === filters[key]);
if (keys.length) {
const res = list.filter(filterUser);
countObj.count = res.length;
return res;
} else {
countObj.count = list.length;
return list;
}
}
working stackblitz
Make the following changes:
The basic change added here is introducing another variable selectedCount that will hold the count of selected items based on the dropdown values.
app.component.html
Showing {{selectedCount}} of {{dataList.length}} Records
<div>
<form #form="ngForm">
<select class="form-control form-control-sm" name="gender"
[(ngModel)]="dropDown" (change)="dropDownChange()">
<option value="All">All</option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</form>
</div>
app.component.ts
dropDown = "All";
public selectedCount = this.dataList.length;
dropDownChange() {
if (this.dropDown !== "All") {
this.selectedCount = this.dataList.filter(
e => e.gender === this.dropDown
).length;
} else {
this.selectedCount = this.dataList.length;
}
}
table.filter.pipe.ts
export class TableFilterPipe implements PipeTransform {
transform(list: any[], filters: any) {
if (filters.gender !== "All") {
const keys = Object.keys(filters).filter(key => filters[key]);
const filterUser = user => keys.every(key => user[key] === filters[key]);
return keys.length ? list.filter(filterUser) : list;
} else {
return list;
}
}
}
Working stackblitz found here.

Categories