Beginner array issue - Javascript - javascript

I'm having an issue with an app in JavaScript.
I seem to randomly have an issue with my array not showing up in JavaScript - I've tried remaking the program several times and sometimes it works, sometimes it doesn't. This is a sample of my latest failed attempt. Could anyone tell me exactly why the array is not appearing in the browser? I've tried to set up a filter and form. I'm trying to create a list with a filter objects in the array.
<!DOCTYPE html>
<html>
<head>
<title>Work</title>
</head>
<body>
<h1>Todos</h1>
<todo class="tddiv"></todo>
<input type="text" class="todo" placeholder="type here">
<form class="todo-form">
<input type="text" placeholder="input-todo-text" name="addTodo">
<button>Submit Text</button>
</form>
<script src="script.js"></script>
</body>
</html>
JavaScript
let todos = [{
text: 'Order cat food',
completed: false
}, {
text: 'Clean kitchen',
completed: true
}, {
text: 'Buy food',
completed: true
}, {
text: 'Do work',
completed: false
}, {
text: 'Exercise',
completed: true
}]
const filters = {
searchText: ''
}
const renderTodos = function(todos, filters) {
const filter = todos.filter(function(todo) {
return
todo.text.toLowerCase().includes(filters.searchText.toLowerCase())
})
document.querySelector('.tddiv').innerHTML = ''
filter.forEach(function(a) {
const add = document.createElement('p')
add.textContent = a.text
document.querySelector('.tddiv').appendChild(add)
})
}
renderTodos(todos, filters)
document.querySelector('.text').addEventListener('input', function(e) {
filters.searchText = e.target.value
renderTodos(todos, filters)
})

There is a new line after your return statement in the filter method that prevents the includes method call(the js interpreter replaces the new line with a ;)
Change your document.querySelector('.text') with querySelector('input[type=text]'), and replace e.target.value with this.value in this querySelector(this refers here to the input element).
let todos = [{
text: 'Order cat food',
completed: false
}, {
text: 'Clean kitchen',
completed: true
}, {
text: 'Buy food',
completed: true
}, {
text: 'Do work',
completed: false
}, {
text: 'Exercise',
completed: true
}]
const filters = {
searchText: ''
}
const renderTodos = function(todos, filters) {
const filter = todos.filter(function(todo) {
return todo.text.toLowerCase().includes(filters.searchText.toLowerCase())
})
document.querySelector('.tddiv').innerHTML = ''
filter.forEach(function(a) {
const add = document.createElement('p')
add.textContent = a.text
document.querySelector('.tddiv').appendChild(add)
})
}
renderTodos(todos, filters)
document.querySelector('input[type=text]').addEventListener('input', function(e) {
filters.searchText = this.value
renderTodos(todos, filters)
})
<h1>Todos</h1>
<todo class="tddiv"></todo>
<input type="text" class="todo" placeholder="type here">
<form class="todo-form">
<input type="text" placeholder="input-todo-text" name="addTodo">
<button>Submit Text</button>
</form>
<script src="script.js"></script>

This can work with e.target.value as well, you just have add class "text" on the input type. The main issue was new line after return statement. It will always return undefined and filter array will be empty. For more information please refer following link. MDN JavaScript Grammar
<!DOCTYPE html>
<html>
<head>
<title>Work</title>
</head>
<body>
<h1>Todos</h1>
<todo class="tddiv"></todo>
<input type="text" class="todo" placeholder="type here">
<form class="todo-form">
<input class="text" type="text" placeholder="input-todo-text" name="addTodo">
<button >Submit Text</button>
</form>
<script src="script.js"></script>
</body>
</html>
let todos = [{
text: 'Order cat food',
completed: false
}, {
text: 'Clean kitchen',
completed: true
}, {
text: 'Buy food',
completed: true
}, {
text: 'Do work',
completed: false
}, {
text: 'Exercise',
completed: true
}]
const filters = {
searchText: ''
}
const renderTodos = function (todos, filters) {
const filter = todos.filter(function (todo) {
return todo.text.toLowerCase().includes(filters.searchText.toLowerCase())
})
document.querySelector('.tddiv').innerHTML = ''
filter.forEach(function (a) {
const add = document.createElement('p')
add.textContent = a.text
document.querySelector('.tddiv').appendChild(add)
})
}
renderTodos(todos, filters)
document.querySelector('.text').addEventListener('input', function (e) {
filters.searchText = e.target.value
renderTodos(todos, filters)
})

This should work.
<input type="text" placeholder="input-todo-text" name="addTodo" class="text"/>
const renderTodos = function(todos, filters) {
const filter = todos.filter(function(todo) {
if(todo.text.toLowerCase().includes(filters.searchText.toLowerCase()) && filters.searchText!="")
{
console.log(todo);
return todo;
}
})

Related

I need to save single objects in LocalStorage, but i have the whole array saved

I don't get how i am supposed to save only a single object a not the whole array. I am trying to create a movie watchlist. If I click "add to watchlist", the single object should be saved in LocalStorage. If I hit remove from watchlist, the object should get removed. I tried to write down methods to regulate all of that, but i guess somethings wrong. The data comes from an API request. Here's the code:
<template>
<div>
<div class="card" v-for="movie in movies"
:key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button type="submit" #click="storeMovie" >
Aggiungi
</button>
<button type="submit" #click="removeMovie">
Rimuovi
</button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
//Cambiare il nome con quello del componente creato
name: 'HomeComp',
data () {
return {
movies: [],
movie: "",
}
},
mounted () {
axios
.get('https://api.themoviedb.org/3/movie/popular?api_key=###&language=it-IT&page=1&include_adult=false&region=IT')
.then(response => {
this.movies = response.data.results
// console.log(response.data.results)
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
if (localStorage.movies) {
this.movies = JSON.parse(localStorage.movies);
}
},
watch: {
movies: {
handler(newMovies) {
localStorage.movies = JSON.stringify(newMovies);
},
deep:true
}
},
methods: {
getMovie() {
this.movies = JSON.parse(localStorage.getItem("movie"));
},
storeMovie() {
if (this.movie.length) {
// push the new movie to list
this.movies.push(this.movie);
// store the data in localStorage
localStorage.setItem("movies", JSON.stringify(this.movies));
// clear the input
this.movie = "";
}
},
removeMovie() {
localStorage.removeItem('movie');
}
},
}
</script>
<style scoped lang="scss">
/*Inserire style componente*/
</style>
tried to parse ad stringify, but i think i'm doing it wrong in some way. Also written some methods, not working
Few observations as per the code you posted :
As you want to store the new movie through input, Aggiungi button should come outside of v-for loop.
For removeStore event, You need to pass the store id from a template so that we can filter out the movies array.
Live Demo :
new Vue({
el: '#app',
data: {
movies: [],
movie: ''
},
mounted() {
// This data will come from API, Just for a demo purpose I am using mock data.
this.movies = [{
id: 1,
title: 'Movie A',
release_date: '06/12/2022'
}, {
id: 2,
title: 'Movie B',
release_date: '07/12/2022'
}, {
id: 3,
title: 'Movie C',
release_date: '08/12/2022'
}, {
id: 4,
title: 'Movie D',
release_date: '09/12/2022'
}, {
id: 5,
title: 'Movie E',
release_date: '10/12/2022'
}]
},
methods: {
storeMovie() {
const newMovieID = this.movies.at(-1).id + 1;
this.movies.push({
id: newMovieID,
title: this.movie,
release_date: '06/12/2022'
})
},
removeMovie(movieID) {
this.movies = this.movies.filter(({ id }) => id !== movieID)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
Add new movie : <input type="text" v-model="movie"/>
<button type="submit" #click="storeMovie()">
Aggiungi
</button>
</div><br>
<div class="card" v-for="movie in movies"
:key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button type="submit" #click="removeMovie(movie.id)">
Rimuovi
</button>
</div>
</div>

Vue.js Loop Input [v-model] updating all form elements not just one

I have form data of:
const form = {
title: null,
content: null,
language: {
en: { title: null, content: null },
es: { title: null, content: null }
}
}
My Form inputs
<v-form ref="formEl" #submit.prevent="validate">
<div v-if="Object.keys(form.language).length > 0">
<div v-for="(language, langCode) in form.language" :key="langCode">
<v-text-field
v-model="form.language[langCode].title"
:label="$t('form.title')"
/>
<v-textarea
v-model="form.language[langCode].content"
:label="$t('form.content')"
/>
</div>
</div>
</form>
Now when I change the input of 1 form input, it updates both to be the same.
I tried placed a KEY on each input field, with the same result. Does anyone have any thoughts or direction on this? Much appreciated.
const form = ref(lodash.cloneDeep(state.pages.one))
// GET CONTENT
useFetch(async () => {
loading.value = true
try {
await dispatch('pages/getOne', route.value.params.id).then((res) => {
if (res !== false) {
form.value = lodash.cloneDeep(res)
}
})
} catch(e) {
$system.log({
comp: 'AdminPagesEdit', msg: 'useFetch', val: e
})
} finally {
loading.value = false
}
})

How to add new elements to an objects-array using event listener and show it on the html page

I was trying to make a todo application, then I wanted to add new todo using a form and an event listener but, when I finished the code the app only adds the written text to the object-array and doesn't actually add it to the todos list in the html page.
I'll provide a small code down below showing the array, the setup code and the html as well in order to make this question short, but if you want to see the whole application code feel free to check this github's repository: https://github.com/salahmak/Todo-application
You can also check out the live version of the app from this ngrok link (i'll keep it live until I fix the problem) : http://7eb95c9a.ngrok.io
The code:
// The array
const todos = [{
text: 'wake up',
completed: true
}, {
text: 'get some food',
completed: true
}, {
text: 'play csgo',
completed: false
}, {
text: 'play minecraft',
completed: true
}, {
text: 'learn javascript',
completed: false
}];
//creating p elements and assigning their text content to each "text" property of the "todos" array
todos.forEach(function(todo) {
let p = document.createElement('p');
p.textContent = todo.text;
document.querySelector('#todo').appendChild(p);
})
<h1>Todos</h1>
<form id="form">
Add a new todo
<input type="text" placeholder="Type your first name" name="firstName">
<button>Submit</button>
</form>
document.querySelector('#todo').appendChild(p); You're appending to a non-existent #todo element. Throw in a <div id="todo"></div> and it'll work.
Working:
// The array
const todos = [{
text: 'wake up',
completed: true
}, {
text: 'get some food',
completed: true
}, {
text: 'play csgo',
completed: false
}, {
text: 'play minecraft',
completed: true
}, {
text: 'learn javascript',
completed: false
}];
//creating p elements and assigning their text content to each "text" property of the "todos" array
todos.forEach(function(todo){
let p = document.createElement('p');
p.textContent = todo.text;
document.querySelector('#todo').appendChild(p);
})
<body>
<h1>Todos</h1>
<form id="form">
Add a new todo
<input type="text" placeholder="Type your first name" name="firstName">
<button>Submit</button>
</form>
<div id="todo"></div>
<script src="todo-app.js"></script>
</body>
The main issue here is that the #todos element is not present in your HTML, which means there is no "destination" for todos data to be displayed in your page.
As an improvment to your app, consider defining a reusuable function like updateView() that updates the contents of the #todo element to display the current data in the todos array. In you app, that could be called:
after submission of your form, to display newly added todos
on load, to display inital data
One way to implement this is as follows:
// The array
const todos = [{
text: 'wake up',
completed: true
}, {
text: 'get some food',
completed: true
}, {
text: 'play csgo',
completed: false
}, {
text: 'play minecraft',
completed: true
}, {
text: 'learn javascript',
completed: false
}];
/* Reusable function that updates the view container with current
data in todos array */
function updateView() {
const container = document.getElementById("todo");
/* Clear list */
container.innerHTML = "";
/* Populate list with current todos data */
todos.forEach(function(todo) {
const p = document.createElement('p');
p.textContent = todo.text;
container.appendChild(p);
});
}
const form = document.getElementById("form");
/* Add form submit event handler to add actual todo to list(s) */
form.addEventListener("submit", function(event) {
/* Prevent default "page reload" form submit behavior */
event.preventDefault();
/* Extract text from input field */
const text = form.querySelector('input[name="firstName"]').value;
/* Add new todo item to todos array (model) */
todos.push({
text: text,
completed: false
});
/* Update container with added todo data */
updateView();
});
/* Populate container with inital data */
updateView();
<h1>Todos</h1>
<form id="form">
Add a new todo
<input type="text" placeholder="Type your first name" name="firstName">
<button>Submit</button>
</form>
<!-- Add a DOM element that contains the actual display list -->
<div id="todo">
</div>

Uncaught TypeError: renderIncomes.overIncome is not a function at HTMLInputElement.<anonymous>

I'm currently learning Javascript at school, so my codes may look like a beginner coding style.
I wanted my list(arrays) on the browser to change when I check the checkbox input. But, when I do check the checkbox, it will say this, "Uncaught TypeError: renderIncomes.overIncome is not a function at HTMLInputElement."
In my html file, I set up the input as a checkbox type. And in my javascript file, I added an eventlistener to change when I check the checkbox. The list on the browser should only change when the income is greater than 300. Here are the codes to both html and javascript files.
let user = {
firstName: 'Zoraida',
lastName: 'Rodriguez',
accountType: 'Personal'
}
let renderUser = {
renderName: function() {
const h1 = document.createElement('h1')
h1.textContent = `Welcome ${user.firstName}!`
document.querySelector('#user').appendChild(h1)
}
}
renderUser.renderName()
let incomes = [{
type: 'monthly wages',
date: '09/01/2018',
income: 900,
}, {
type: 'yardwork',
date: '09/07/2018',
income: 100,
}, {
type: 'eBay',
date: '09/14/2018',
income: 250,
}]
let renderIncomes = {
renderList: function() {
document.querySelector('#incomes').innerHTML = ''
const h3 = document.createElement('h3')
h3.textContent = `You have a list of ${incomes.length} incomes.`
document.querySelector('#incomes').appendChild(h3)
incomes.forEach(function(each) {
const p = document.createElement('p')
p.textContent = `On ${each.date}, you received $${each.income} from ${each.type}.`
document.querySelector('#incomes').appendChild(p)
})
},
totalIncome: function() {
document.querySelector('#totalIncome').innerHTML = ''
let totalIncome = 0
incomes.forEach(function(income) {
totalIncome += income.income
})
const h2 = document.createElement('h2')
h2.textContent = `Total income: $${totalIncome}`
document.querySelector('#totalIncome').appendChild(h2)
},
overIncome: function() {
incomes.filter(function(incomeResults) {
return incomeResults.income > 300
})
}
}
renderIncomes.renderList()
renderIncomes.totalIncome()
renderIncomes.overIncome()
document.querySelector('#new-incomes').addEventListener('submit', function(e) {
e.preventDefault()
incomes.push({
type: e.target.elements.typeOfIncome.value,
date: e.target.elements.date.value,
income: parseInt(e.target.elements.income.value)
})
renderIncomes.renderList()
renderIncomes.totalIncome()
e.target.elements.typeOfIncome.value = ''
e.target.elements.date.value = ''
e.target.elements.income.value = ''
})
document.querySelector('#filterincomes').addEventListener('change', function(e) {
renderIncomes.overIncome = e.target.checked
renderIncomes.overIncome()
})
<body>
<div id="user" class="center"></div>
<hr>
<br>
<div id="totalIncome" class="center"></div>
<div id="incomes" class="center"></div>
<form id="new-incomes" class="center">
<label>
Date: <input type="text" placeholder="MM/DD/YYYY" name="date">
</label>
<label>
Type: <input type="text" placeholder="From Where" name="typeOfIncome">
</label>
<label>
Income: <input type="text" placeholder="Type New Income" name="income">
</label>
<button>Submit</button>
</form>
<label>
<input id="filterincomes" type="checkbox">Check here for incomes over $300
</label>
<script src="test.js"></script>
</body>
you are assigning property overIncome of object renderIncomes to boolean value
so there is no function overIncome() after line
renderIncomes.overIncome = e.target.checked
remove the line, your code will work fine
In your addEventListener you are assigning a value to a object
Change the below code as commend and please note below solution will fix your Uncaught TypeError error
document.querySelector('#filterincomes').addEventListener('change', function(e) {
//renderIncomes.overIncome = e.target.checked
renderIncomes.overIncome()
})

I'm trying to implement VeeValidate to check if at least one checkbox is checked

I found a jsfiddle example that I forked and then edited. I don't understand what's going on or how to fix it. In my example I'm using checkboxes with values but when I click a checkbox the value is changed to true or false depending on if the checkbox is clicked.
const Checkboxes = {
template: '#checkboxTmpl',
data() {
return {
text: '',
options: [
{
name: 'Web',
slug: 'web'
},
{
name: 'iOS',
slug: 'ios'
},
{
name: 'Android',
slug: 'android'
}
]
};
},
created() {
this.$validator.extend('oneChecked', {
getMessage: field => 'At least one ' + field + ' needs to be checked.',
validate: (value, [testProp]) => {
const options = this.options;
// console.log('questions', value, testProp, options.some((option) => option[testProp]));
return value || options.some((option) => option[testProp]);
}
});
},
methods: {
validateBeforeSubmit(e) {
this.$validator.validateAll(); // why is oneChecked not validated here? --> manually trigger validate below
this.options.forEach((option) => {
this.$validator.validate('platforms', option.slug, ['checked'])
});
console.log('validator', this.errors);
if (!this.errors.any()) {
alert('succesfully submitted!');
}
}
}
};
Vue.use(VeeValidate);
const app = new Vue({
el: '#app',
render: (h) => h(Checkboxes)
})
<script src="https://cdn.jsdelivr.net/vee-validate/2.0.0-beta.18/vee-validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>
<div id="app">
</div>
<script id="checkboxTmpl" type="text/template">
<form #submit.prevent="validateBeforeSubmit">
<label v-for="(option, index) in options">
<input type="checkbox"
v-model="option.slug"
name="platform"
v-validate.initial="option.slug"
data-vv-rules="oneChecked:checked"
data-vv-as="platform"/> {{option.name}}
</label>
<p v-show="errors.has('platform')">{{ errors.first('platform') }}</p>
<pre>{{options}}</pre>
<button type="submit">Submit</button>
</form>
</script>
I don't understand why all of the checkboxes are checked and unchecking one of them returns a validation error even though two are still checked. I like that errors are shown before the form is submitted but unchecking all and then submitting doesn't trigger the validation error.
I'm using VeeValidate because that is what the example uses but any other solution would be fine. I don't want to use jQuery in my vue.js application.
I would really like to understand what is going on.
There was two main problems going on :
Using v-model on the wrong key. In fact, each time the checkbox was checked or unchecked, it will emit an input event that will modify the original slug of the option (in your data). Instead, you need to add a checked field in your option. Then in your template add the :checked attribute and modify your v-model to be :option.checked.
As the docs of VeeValidate say, you can just use the required rule to make sure a checkbox has to be checked to submit your form. Here is the link towards the docs. Therefore, you don't need your created block.
Additionally, the validateAll function returns a promise containing the result of the validation. So no need to use this.errors.any() too.
Also, I upgraded the VeeValidate library to the latest as you used a beta.
Here is the working code :
const Checkboxes = {
template: '#checkboxTmpl',
data() {
return {
text: '',
options: [{
name: 'Web',
slug: 'web',
checked: false
},
{
name: 'iOS',
slug: 'ios',
checked: true
},
{
name: 'Android',
slug: 'android',
checked: true
}
]
};
},
methods: {
validateBeforeSubmit(e) {
this.$validator.validateAll().then(value => {
if (value) {
alert('successfully submitted')
}
})
}
}
};
Vue.use(VeeValidate);
const app = new Vue({
el: '#app',
render: (h) => h(Checkboxes)
})
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>
<script src="https://unpkg.com/vee-validate#latest"></script>
<script id="checkboxTmpl" type="text/template">
<form #submit.prevent="validateBeforeSubmit">
<label v-for="(option, index) in options">
<input type="checkbox"
:checked="option.checked"
v-model="option.checked"
name="platform"
v-validate="'required'"/> {{option.name}}
</label>
<p v-show="errors.has('platform')">{{ errors.first('platform') }}</p>
<pre>{{options}}</pre>
<button type="submit">Submit</button>
</form>
</script>
Hope that helps!

Categories

Resources