Constructor function didn't save querySelector(x).value - javascript

I am working on a small APP for calculating Budgets and i am stuck on a problem i don't understand. I used a constructor for getting the user inputs in a few input fields.
I tried to setup this part in a constructor to learn more about prototyping and constructor functions and to challenge myself. I don't get why the constructor GetInput not holding my input.values
<div class="add">
<div class="add__container">
<select class="add__type">
<option value="inc" selected>+</option>
<option value="exp">-</option>
</select>
<input type="text" class="add__description" placeholder="Add description">
<input type="number" class="add__value" placeholder="Value">
<button class="add__btn"><i class="ion-ios-checkmark-outline"></i></button>
</div>
</div>
const addType = document.querySelector('.add__type').value;
const description = document.querySelector('.add__description').value
const addValue = document.querySelector('.add__value').value;
// EVENTLISTENER Constructor:
function EventListner(selector, listner, fnt) {
this.selector = selector;
this.listner = listner;
this.fnt = fnt;
document.querySelector(selector).addEventListener(listner, fnt);
};
function GetInput(operator, description, value) {
this.operator = operator;
this.description = description;
this.value = value;
}
const inputData = new GetInput(addType, description, addValue);
console.log(inputData);
const clickListener = new EventListner('.add__btn', 'click', () => {
if (description.value == '' || addValue.value == '') {
// MAKE SURE DESCRIPTION AND VALUE IS NOT EMPTY
alert('description and value can\'t be empty');
return;
}
const inputData = new GetInput(addType, description, addValue);
console.log(inputData);
});
const enterKeyListener = new EventListner('.add__value', 'keypress', (e) => {
if (e.keyCode == 13) {
if (description.value == '' || addValue.value == '') {
// MAKE SURE DESCRIPTION AND VALUE IS NOT EMPTY
alert('description and value can\'t be empty');
return;
}
// ON ENTER SAVE VALUES IN AN ARRAY
// IF PLUS INTO incomeArr, ON MINUS INTO expenseArr
console.log('enter pressed');
const inputData = new GetInput(addType, description, addValue);
console.log(inputData);
}
});
Output is:
GetInput {operator: "inc", description: "", value: ""}
Only works when i:
document.querySelector('.add__value').value;
directly into the console.

Your initial values are empty for the input fields. And your code is executing right away. That's why you are getting empty value for those fields.You can try adding some predefined values for those input field.
Code with predefined value for the input fields :
const addType = document.querySelector('.add__type').value;
const description = document.querySelector('.add__description').value
const addValue = document.querySelector('.add__value').value;
function GetInput(operator, description, value) {
this.operator = operator;
this.description = description;
this.value = value;
}
const inputData = new GetInput(addType, description, addValue);
console.log(inputData);
<div class="add">
<div class="add__container">
<select class="add__type">
<option value="inc" selected>+</option>
<option value="exp">-</option>
</select>
<input type="text" value="mydesc" class="add__description" placeholder="Add description">
<input type="number" value="2" class="add__value" placeholder="Value">
<button class="add__btn"><i class="ion-ios-checkmark-outline"></i></button>
</div>
</div>
Note : You need to add event listener to listen to any change in your input field values. If user type anything then you can proceed to instantiate your constructor function

You are getting this issue because you need to trigger an event to for calculate, and one more thing you have to use something else other then const, because you cannot reinitialize value in constant variables, Current code is execute on loading page, So the values is initialized to that will not change
const addType = document.querySelector('.add__type').value;
const description = document.querySelector('.add__description').value
const addValue = document.querySelector('.add__value').value;
So please change const with var or let, Or you can set default value to description and value

Related

(JS) Remove an option from select box JUST ONCE after calling function

I have this code where I want to add text to the select box when calling a function via clicking an input button.
I want the select box to have a default text when the page is loaded and no value is added to the array. And I want this text to vanish but I could still add many values from the input box and make them show on the select box.
So I made the input and select box with the following:
let num = document.querySelector('input#numtxt')
let lista = document.querySelector('select#seltxt')
let res = document.querySelector('div#res')
let valores = []
function adicionar() {
if (isNumero(num.value) && !inLista(num.value, valores)) {
lista.options[0] = null //
valores.push(Number(num.value))
let item = document.createElement('option')
item.text = `Valor ${num.value} adicionado.`
lista.appendChild(item)
} else {
window.alert('Valor inválido ou já existe!')
}
}
<div>
<p>TYpe a number between 1 and 100: <input type="number" name="num1" id="numtxt">
<input type="button" value="Adicionar" onclick="adicionar()"></p>
</div>
<div>
<p>
<select name="sel1" id="seltxt" size="10">
<option>Type a number above!</option>
</select>
</p>
<p><input type="button" value="End" onclick="finalizar()"></p>
</div>
I've tried a lot of commands with boxvar.options[0] = null and boxvar.remove(0)but they all kept removing the first value which I need for the program.
Any sugestions?
let num = document.querySelector('input#numtxt')
let lista = document.querySelector('select#seltxt')
let res = document.querySelector('div#res')
let valores = []
function adicionar() {
if (isNumero(num.value) && !inLista(num.value, valores)) {
if(!valores.length) {
// If there are no values on list, delete whatever is inside of select
lista.innerHTML = ''
}
valores.push(Number(num.value))
let item = document.createElement('option')
item.text = `Valor ${num.value} adicionado.`
lista.appendChild(item)
} else {
window.alert('Valor inválido ou já existe!')
}
}
This is slightly verbose for clarity - if we add a data attribute we can filter on that and remove it if it exists. We can also filter by values and not add if the new one exists (it could be a data attribute if you do not want to set the value.
let lista = document.querySelector('#seltxt');
let res = document.querySelector('#res');
let valores = [];
function adicionar() {
let num = document.querySelector('#numtxt');
let opts = [...lista.options].filter((element, index) => {
return element.dataset.default == "default";
});
console.log(opts);
if (opts.length) {
opts[0].remove();
}
let newValue = Number(num.value);
// now if it already exists, don't add it
let matchOpt = [...lista.options].filter((element, index) => {
return element.value == newValue;
});
// we already have it so jump back out
if (matchOpt.length) {
return;
}
valores.push(newValue);
let item = document.createElement('option');
item.text = `Valor ${num.value} adicionado.`;
item.value = newValue;
lista.appendChild(item);
}
<div>
<p>Type a number between 1 and 100: <input type="number" name="num1" id="numtxt">
<input type="button" value="Adicionar" onclick="adicionar()"></p>
</div>
<div>
<p>
<select name="sel1" id="seltxt" size="10">
<option data-default="default">Type a number above!</option>
</select>
</p>
<p><input type="button" value="End" onclick="finalizar()"></p>
</div>

Converting Typed Properties in JavaScript Classes

Just starting to use JavaScript Classes and have a quick question. In the example below, I want to ensure/convert certain properties to be numeric when I create the class. For instance, if the user enters "$10.50" for the unit price, I want the class to only have 10.50 so the total function works. I'm sure this can be done with a getter/setter but I can't quite figure out how to implement it.
<form name="orderform">
order date: <input name="order_date" value="" type=text>
<br>item_name: <input name="item_name" value="" type=text>
<br>unit_price: <input name="unit_price" value="" type=text>
<br>quantity: <input name="quantity" value="0" type=text>
</form>
class OrderItem {
constructor(
order_date,
item_name,
unit_price,
quantity,
) {
this.order_date = order_date;
this.item_name = item_name;
this.unit_price = unit_price;
this.quantity = quantity;
}
get total() {
return this.unit_price * this.quantity;
}
}
const orderitem1 = new OrderItem();
function GetNumber(val)
{
if (typeof val !== 'undefined')
{
return Number(val.replace(/[^0-9.-]+/g, ""));
}
else
{
return 0;
}
}
function getOrder()
{
$("#orderform").serializeArray().map(function(x){orderitem1[x.name] = x.value;});
var total = orderitem.total; //doesn't work if they enter '$10.50'
//...do stuff here with the orderitem1 class....
}
Instead of creating the OrderItem and then updating its values in getOrder, you could create the OrderItem on the spot.
The snippet below instantiates an OrderItem from the form data on submit, and parses the numeric values in the constructor using your existing GetNumber function.
You could also consider changing the input type from 'text' to 'number' for those inputs, which would (mostly) prevent users from entering $ in the first place.
// get a reference to the form
const form = document.querySelector('form[name=orderform]');
// add an onsubmit handler
form.addEventListener('submit', (e) => {
// stop the form from submitting
e.preventDefault();
// convert the form's inputs into a key-value mapping, e.g. { quantity: "12", unit_price: "$10.50", ... }
const data = new FormData(e.target);
const config = [...data.entries()].reduce((acc, [key, value]) => ({...acc, [key]: value}), {})
// instantiate an OrderItem from the config
const orderItem = new OrderItem(config);
// do whatever you need to do with it
console.log(orderItem.total);
})
class OrderItem {
constructor({
order_date,
item_name,
unit_price,
quantity,
} = {}) {
this.order_date = order_date;
this.item_name = item_name;
this.unit_price = GetNumber(unit_price);
this.quantity = GetNumber(quantity);
}
get total() {
return this.unit_price * this.quantity;
}
}
function GetNumber(val) {
if (typeof val !== 'undefined') {
return Number(val.replace(/[^0-9.-]+/g, ""));
} else {
return 0;
}
}
<form name="orderform">
order date: <input name="order_date" value="" type=text>
<br>item_name: <input name="item_name" value="" type=text>
<br>unit_price: <input name="unit_price" value="$10.50" type=text>
<br>quantity: <input name="quantity" value="1" type=text>
<button>Get Order</button>
</form>
alternative
If for some reason you need to be able to change the OrderItem's values after instantiating it, you could do it in a getter or setter. (But generally speaking immutability is A Good Thing™.)
class OrderItem {
set unit_price(p) {
// needs a different internal name to avoid recursion
this._unitPrice = toNumber(p);
}
get unit_price() {
return this._unitPrice;
}
}
const toNumber = v => Number(`${v}`.replace(/[^0-9.]/g, ''));
const item = new OrderItem();
item.unit_price = '$10.50';
console.log(item.unit_price);

How the user can change the city name in this request

i'm starting learning JS and i don't understand how to change ( as a user ) the city name on this request .
It's working when in my request i put the city name in askWeather.open("GET", "url.../london)
but how can i give the possibilty to the user to change this value ?
Thanks a lot
askWeather.onreadystatechange = function () {
if (this.readyState = XMLHttpRequest.DONE && this.status == 200) {
var response = JSON.parse(this.responseText);
let resp = document.getElementById('result');
let ask = document.getElementById('btn');
ask.addEventListener('click', function () {
resp.innerHTML = response.current_condition.condition;
});
}
};
askWeather.open("GET", "https://www.prevision-meteo.ch/services/json/lille");
askWeather.send();
You need an event listener for the value that has been typed and also an event Listener when the request should be sent.
This is one way how you could let the user to change the city:
let city = 'london'; // The default value, if the user didn't try to set a value
document.getElementById('city').addEventListener('keyup', (e) => {
city = e.target.value;
});
document.getElementById('send').addEventListener('click', () => {
askWeather.open("GET", `url.../${city}`)
askWeather.send();
});
<input id="city" type="text">
<button id="send">Send the request</button>
But you could use a simple form as well with only 1 event listener, like:
let city = 'london';
document.getElementById('my-form').addEventListener('submit', () => {
city = document.getElementById('city').value;
askWeather.open("GET", `url.../${city}`)
askWeather.send();
});
<form id="my-form" action="">
<input id="city" type="text">
<input type="submit" id="send">
</form>
Declare a variable for the city name, you can set this value from wherever you wish, like from user input.
const city = 'lille';
askWeather.open("GET", `https://www.prevision-meteo.ch/services/json/${city}`);

Getting data from HTML forms in Javascript always produces Strings?

I use Javascript to intercept an HTML form submission:
var form_api = $("#apiForm");
$(form_api).submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
/* Get input values from form */
var formData = prepFormData("#apiForm");
}
However, when I convert the data into an object (I wish to use jQuery to pass this to an endpoint), all object properties are strings.
function prepFormData(formSelector){
var form_api = $(formSelector);
// Serialize the form data as a PlainObject.
var formData = $(form_api).serializeArray().reduce(function (obj, item) {
obj[item.name] = item.value;
return obj;
}, {});
}
Why does it always produce strings? I would like the following behavior instead:
<input type="text"> should produce NULL when nothing has been entered.
<input type="number"> should produce an Int when a value has been entered.
You need to parse the input to suite your needs. Every form value in HTML in inherently a string.
The type attribute lets the browser know what kind of field to display, not what is the data type of the value. Take for example:
<input type="hidden" value="1">
HTML and javascript can infer no information about the data type from hidden it could be a string it could be an int.
number is equally problematic, why default to int, what about doubles and other number types?
In my example above, note that the value is surrounded by quotes, denoting a string. (Quotes are optional, but recommended, but do nothing to the data type.)
To actually solve your problem I would consider adding a data attribute to your fields, say data-type to hold the data type you want to cast your value to.
Here's a quick example:
var form_api = $("#apiForm");
$(form_api).submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
/* Get input values from form */
var formData = prepFormData("#apiForm");
console.log(formData);
});
function prepFormData(formSelector){
var form_api = $(formSelector);
// Serialize the form data as a PlainObject.
var formData = $(form_api).serializeArray().reduce(function (obj, item) {
var tempValue = null;
if(item.value !== "") {
//Get data type of current field
var dataType = $(form_api).find("[name=" + item.name + "]").data("type");
if(dataType === undefined) {
dataType = "text";
}
//Extend this based on the other data types you need
switch(dataType) {
case "text" :
tempValue = item.value;
break;
case "int" :
tempValue = parseInt(item.value, 10);
break;
case "float" :
tempValue = parseFloat(item.value);
break;
//Fall back for no data type defined, eg the select in this example
default :
tempValue = item.value;
break;
}
}
obj[item.name] = tempValue;
return obj;
}, {});
return formData;
}
label {display:block; margin-bottom:5px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<form id="apiForm" method="get" action="">
<label>Name <input type="text" data-type="text" name="Name"></label>
<label>Integer <input type="number" data-type="int" name="Integer"></label>
<label>Float <input type="number" step="0.1" data-type="float" name="Float"></label>
<fieldset>
<legend>Age Range</legend>
<label><18 <input type="radio" data-type="text" name="AgeRange" value="<18"></label>
<label>>18 <input type="radio" data-type="text" name="AgeRange" value=">18"></label>
</fieldset>
<label>Country
<select name="country">
<option value="usa">USA</option>
<option value="aus">Australia</option>
<option value="other">Other</option>
</select>
</label>
<label>Product
<select name="ProductId" data-type="int">
<option value="1">Apple</option>
<option value="2">Orange</option>
<option value="11">Pear</option>
<option value="110">Pineapple</option>
</select>
</label>
<input type="hidden" data-type="text" name="HiddenText" value="">
<input type="submit">
</form>
This in normal JS behaviour. type number and text are for validations inside the input for browsers. They don't define the data-type of the value inside of them. By default they are strings. You can perform conversions for your use. The text field returns an empty string because it's by default an empty string and not null.

Adding dynamic <option> to select doesn't show unless I switch the field

I want to be able to add Group Members dynamically and I simply used + button for that. The problem is - it adds the input value of the prompt, but I can't see it unless I switch fields and type something else.
Here is the YouTube link for better understanding:
https://youtu.be/sA0WB2Le3Fg
<button id="add" type="button" onClick={(e)=>this.addMember(e)}>+</button>
The state :
members: [
{member:"Berin"},
{member:"Cristian"},
{member:"Raddy"},
{member:"Ventsislav"},
]
The function :
addMember = (e) => {
let input = prompt("Enter the name");
this.state.members.push({member:input});
e.preventDefault();
}
The mapping function :
let members = this.state.members.map((member,index)=>{
return <option value={member.member} key={index}>{member.member}</option>
})
The select field :
<select id="groupMember" onChange={this.changeMember} name="member" required>
<option defaultValue="" selected disabled>Select group member</option>
{members}
</select>
Try this:
addMember = (e) => {
let input = prompt("Enter the name");
let newMemberList = this.state.members;
newMemberList.push(input);
this.setState({ members: newMemberList });
e.preventDefault();
}
Recommended approach in the later React versions looks like this:
addMember = (e) => {
let input = prompt("Enter the name");
this.setState(prevState => ({
members: [...prevState.members, input]
}))
e.preventDefault();
}
Thank you ! Already found the solution :
addMember = (e) => {
let input = prompt("Enter the name");
let members = this.state.members;
members.push({member:input})
e.preventDefault();
this.setState({
...this.state,
members
})

Categories

Resources