mergeMap() RXJS 3 observables - javascript

I watched a tutorial on how to use mergeMap() on 2 observables but I'm still very unclear on how to use it with more than 2 observables.
Here is the link to the tutorial:
https://www.youtube.com/watch?v=b59tcUwfpWU&t=1s
Here is the code for 3 observables to concatenate three inputs and display dynamically on html.
var input1 = document.querySelector('#input1');
var input2 = document.querySelector('#input2');
var input3 = document.querySelector('#input3');
var span = document.querySelector('span');
var obs1 = Rx.Observable.fromEvent(input1, 'input');
var obs2 = Rx.Observable.fromEvent(input2, 'input');
var obs3 = Rx.Observable.fromEvent(input3, 'input');
obs1.mergeMap((event1) => {
return obs2.map((event2) => {
return event1.target.value + ' ' + event2.target.value;
});
}).mergeMap((result) => {
return obs3.map((event3) => {
return result + ' ' + event3.target.value;
});
}).subscribe((combinedValue) => {
console.log(combinedValue);
return span.textContent = combinedValue;
})
<script src="https://unpkg.com/#reactivex/rxjs#5.3.0/dist/global/Rx.js"></script>
<input type="text" id="input1">
<input type="text" id="input2">
<input type="text" id="input3">
<p>Combined value: <span></span></p>
My problem here is that when i type in the first and second inputs, the display does not dynamically show the changes until I enter something into the third input.
My current understanding of mergeMap() is that it flattens a sequence of sequence of observables into a sequence of observables.

I think you want the operator combineLatest().
To make things simpler, I would map the event.target.value after each fromEvent, and perhaps throw in the startWith('') since nothing is happening until the user types.
console.clear()
var input1 = document.querySelector('#input1')
var input2 = document.querySelector('#input2');
var input3 = document.querySelector('#input3');
var span = document.querySelector('span');
var obs1 = Rx.Observable.fromEvent(input1, 'input')
.map(e=>e.target.value)
.startWith('')
var obs2 = Rx.Observable.fromEvent(input2, 'input')
.map(e=>e.target.value)
.startWith('')
var obs3 = Rx.Observable.fromEvent(input3, 'input')
.map(e=>e.target.value)
.startWith('')
Rx.Observable.combineLatest(obs1,obs2,obs3)
.map(([val1,val2,val3]) => val1 + ' ' + val2 + ' ' + val3)
.subscribe((combinedValue) => {
console.log(combinedValue);
return span.textContent = combinedValue;
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.js"></script>
<input type="text" id="input1">
<input type="text" id="input2">
<input type="text" id="input3">
<p>Combined value: <span></span></p>

Related

how to run JavaScript code when checkbox is checked

There is a button that passes value to another input field. but now I want to pass value if checkbox is checked.
const copy = (id) => {
var value = document.getElementById("col" + id + "-input").value
var list = document.getElementsByClassName("col" + id + "-input")
for (i = 0; i < list.length; i++)
list[i].value = value
}
document.getElementById("col1-button").addEventListener("click", () => copy(1))
<a type="button" id="col1-button">OK</a>
a does not have a type.
Pass the number in a data-attribute then you can reuse the script for more than one button
I guessed the HTML. Next time post a complete example please
const copy = e => {
const tgt = e.target;
if (tgt.type !=="checkbox" || !tgt.checked) return;
const id = e.target.dataset.id;
var value = document.getElementById("col" + id + "-input").value
document.querySelectorAll(".col" + id + "-input").forEach(inp => inp.value = value);
};
document.getElementById("col1-button").addEventListener("click",copy)
<input type="text" id="col1-input" />
<input type="text" class="col1-input" />
<input type="text" class="col1-input" />
<input type="text" class="col1-input" />
<label><input type="checkbox" id="col1-button" data-id="1" />OK</label>

Javascript loop array for form validation

I have a table form with some rows, that are controlled by user. Meaning they can add as more as they want. Let's pretend user requested 5 rows and i need to check if they all have values.
function validateForm() {
var lastRowInserted = $("#packageAdd tr:last input").attr("name"); // gives me "packageItemName5"
var lastCharRow = lastRowInserted.substr(lastRowInserted.length - 1); // gives me 5
var i;
for (i = 1; i <= lastCharRow; i++) {
var nameValidate[] = document.forms["packageForm"]["packageItemName"].value;
if(nameValidate[i].length<1){
alert('Please fill: '+nameValidate[i]);
return false;
}
}
}
How can i receive packageItemName1 to 5 values in a loop so then I can use to validate them. Want the loop to process this code
var nameValidate[] = document.forms["packageForm"]["packageItemName1"].value;
var nameValidate[] = document.forms["packageForm"]["packageItemName2"].value;
var nameValidate[] = document.forms["packageForm"]["packageItemName3"].value;
var nameValidate[] = document.forms["packageForm"]["packageItemName4"].value;
var nameValidate[] = document.forms["packageForm"]["packageItemName5"].value;
Like this
const validatePackageItems = () => {
const nameValidate = $("form[name=packageForm] input[name^=packageItemName]"); // all fields with name starting with packageItemName
const vals = nameValidate.map(function() { return this.value }).get(); // all values
const filled = vals.filter(val => val.trim() !== ""); // all values not empty
console.log("Filled", filled, "= ", filled.length, "filled of", vals.length)
return filled.length === vals.length
};
$("[name=packageForm]").on("submit",(e) => {
if (!validatePackageItems()) {
alert("not valid");
e.preventDefault();
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form name="packageForm">
<input type="text" name="packageItemName1" value="one" /><br/>
<input type="text" name="packageItemName2" value="two" /><br/>
<input type="text" name="packageItemName3" value="" /><br/>
<input type="text" name="packageItemName4" value="four" /><br/>
<input type="submit">
</form>
You can use string interpolation to get the key dynamically:
for (let i = 1; i < 6; i++) {
const currentValue = document.forms.packageForm[`packageItemName${i}`]
console.log('current value:', currentValue)
}

adding the sum of two separate functions

(ETA: I'm working on this for a class and the teacher wants everything to be "oninput"...yes, it's annoying :p )
I'm working on a form where each function miltiplies a number and gives me a "subtotal" on input. I'd like to take the two "subtotal" answers from the two functions and add them togething into a "total" amount. I feel like this should be simple but nothing I've tried works.
Here's what I've got in the javascript that works to give me the two subtotals:
function myCalculator() {
var qty1 = document.getElementById('qty1').value;
document.getElementById('subTotalOne').innerHTML = '$ ' + qty1 * 19.99;
}
function myCalculatorTwo() {
var qty2 = document.getElementById('qty2').value;
document.getElementById('subTotalTwo').innerHTML = '$ ' + qty2 * 37.99;
}
Here's the important parts of the html:
<div class="qty">
<label for="qty">Qty</label><br>
<input type="number" id="qty1" placeholder="0" oninput="myCalculator()"/><br>
<input type="number" id="qty2" placeholder="0" oninput="myCalculatorTwo()"/><br>
</div>
<div class="price">
<label for="price">Price</label>
<p>$19.99</p>
<p>$37.99</p>
</div>
<div class="subtotal">
<label for="subTotal">Total</label><br>
<span class="subTotalOne" id="subTotalOne">$</span><br>
<span class="subTotalTwo" id="subTotalTwo">$</span><br>
</div>
<div class="total">
<label for="total">Order Total</label><br>
<span class="orderTotal" id="orderTotal" oninput="orderTotal()">$</span><br>
</div>
I'm trying to add the subTotalOne and subTotalTwo and have them output at orderTotal, essentially. :)
Thanks!
//Global variables (concidering ID is unique)
let subTotalOne, subTotalTwo, qty1, qty2, orderTotal;
const setup = () => {
subTotalOne = document.getElementById('subTotalOne');
subTotalTwo = document.getElementById('subTotalTwo');
qty1 = document.getElementById('qty1');
qty2 = document.getElementById('qty2');
orderTotal = document.getElementById('orderTotal');
myCalculator();
myCalculatorTwo();
};
const updateTotal = (target, value) => {
if(target == null || value == null || Number.isNaN(value)) return;
target.textContent = `$ ${value.toFixed(2)}`;
target.setAttribute('data-value', value.toFixed(2));
}
const getTotal = () => {
if(subTotalOne == null || subTotalTwo == null) return 0;
const [value1, value2] = [
Number.parseFloat((subTotalOne.dataset?.value ?? 0), 10),
Number.parseFloat((subTotalTwo.dataset?.value ?? 0), 10)
];
if(Number.isNaN(value1) || Number.isNaN(value2)) return;
else return value1 + value2;
};
const updateOrderTotal = () => updateTotal(orderTotal, getTotal());
const myCalculator = () => {
const value = Number.parseFloat(qty1.value || 0, 10) * 19.99;
updateTotal(subTotalOne, value);
updateOrderTotal();
}
const myCalculatorTwo = () => {
const value = Number.parseFloat(qty2.value || 0, 10) * 37.99;
updateTotal(subTotalTwo, value);
updateOrderTotal();
}
window.addEventListener('load', setup);
<div class="qty">
<label for="qty">Qty</label><br>
<input type="number" id="qty1" placeholder="0" oninput="myCalculator()" min="0"><br>
<input type="number" id="qty2" placeholder="0" oninput="myCalculatorTwo()" min="0"><br>
</div>
<div class="price">
<label for="price">Price</label>
<p data-value="19.99">$19.99</p>
<p data-value="37.99">$37.99</p>
</div>
<div class="subtotal">
<label for="subTotal">Total</label><br>
<span class="subTotalOne" id="subTotalOne">$</span><br>
<span class="subTotalTwo" id="subTotalTwo">$</span><br>
</div>
<div class="total">
<label for="total">Order Total</label><br>
<span class="orderTotal" id="orderTotal" oninput="orderTotal()">$</span><br>
</div>
Here's how you do it:
function orderTotal() {
const qty1 = document.getElementById('qty1').value;
const qty2 = document.getElementById('qty2').value;
const total = parseInt(qty1) + parseInt(qty2);
document.getElementById('orderTotal').innerHTML = '$ ' + total;
}
Remove the oninput="orderTotal()" in your span element and trigger the above function using a button click e.g. <button onClick="orderTotal()">Calculate Total</button> or maybe when either of your two inputs' value changes. Also consider using const and let instead of var.
https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/
Instead of querying the DOM in Ray's answer--as DOM queries should generally be avoided since they are slow W3 Wiki, you could also consider using a shared variable between the two functions.
Also, consider using something else in place of innerHTML, mostly because of efficiency why-is-element-innerhtml-bad-code.
var total1;
var total2;
function myCalculator() {
var qty1 = document.getElementById('qty1').value;
total1 = qty1 * 19.99
document.getElementById('subTotalOne').textContent = '$ ' + total1;
}
function myCalculatorTwo() {
var qty2 = document.getElementById('qty2').value;
total2 = qty2 * 37.99;
document.getElementById('subTotalTwo').textContent = '$ ' + total2;
}
function orderTotal() {
document.getElementById('orderTotal').innerHTML = '$ ' + (total1 + total2);
//parentheses because '$' isn't a number so the numbers total1 and total2 will be treated like strings and joined together
}

Javascript passing variable and elementId to an function

var ingaveA = {
stuks: ""
};
var ingaveB = {
prijs: ""
};
var uitgave = {
totaal: ""
};
update(ingaveA, "testa", ingaveB, "testb", uitgave, "testc");
function update(refA, argsA, refB, argsB, refC, argsC) {
refA.stuks = document.getElementById(argsA).value;
refB.prijs = document.getElementById(argsB).value;
refC.totaal = refA.stuks * refB.prijs;
document.getElementById(argsC).value = refC.totaal;
}
ingaveA = ingaveA.stuks;
ingaveB = ingaveB.prijs;
uitgave = uitgave.totaal;
alert("Stuks=" + ingaveA + " Prijs=" + ingaveB + " Totaal=" + uitgave);
<input id="testa" value="10">
<input id="testb" value="6">
<input id="testc" value="">
Is there an better way to do this? always 3 variable names and 3 elements.
Maybe using an array ?
thanks in advance
Instead of using document.getElementById() for every input, you can use document.querySelectorAll() and [id^=test] to select all 3 and put them in a NodeList (think of it as an array).
[id^=test] means every id that starts with test
You can also make one general object, and update it as needed like below.
var ingave = {
ingaveA: {
stuks: ""
},
ingaveB: {
prijs: ""
},
uitgave: {
totaal: ""
}
};
var inputs = document.querySelectorAll("[id^=test]");
update(ingave, inputs);
function update(ref, inputs) {
ref.ingaveA.stuks = inputs[0].value;
ref.ingaveB.prijs = inputs[1].value;
ref.uitgave.totaal = ref.ingaveA.stuks * ref.ingaveB.prijs;
inputs[2].value = ref.uitgave.totaal;
}
var ingaveA = ingave.ingaveA.stuks;
var ingaveB = ingave.ingaveB.prijs;
var uitgave = ingave.uitgave.totaal;
alert("Stuks=" + ingaveA + " Prijs=" + ingaveB + " Totaal=" + uitgave);
<input id="testa" value="10">
<input id="testb" value="6">
<input id="testc" value="">
I'm not really sure what the question is here, but you don't need to create 3 separate objects.
let ingaveA = ""
let ingaveB = ""
let uitgave = ""
then just pass these 3 to your function:
function update(refA, refB, refC) {
refA = document.getElementById("testa").value;
refB = document.getElementById("testb").value;
refC = refA * refB;
document.getElementById("testc").value = refC.totaal;
}
Again, it's hard to optimize it as we don't know your specifics of the code.

java script functions for different textbox my code here

This is my code in html and java script. I coded same things thrice, I want to do it once... what to do...............
<input type="text" name="option1" id="option1" onblur="calc_amt(1);">
<input type="text" name="price1" id="price1" onblur="calc_amt(1);">
<input type="text" name="amount1" id="amount1" readonly>
<input type="text" name="option2" id="option2" onblur="calc_amt(2);">
<input type="text" name="price2" id="price2" onblur="calc_amt(2);">
<input type="text" name="amount2" id="amount2" readonly>
<input type="text" name="option3" id="option3" onblur="calc_amt(3);">
<input type="text" name="price3" id="price3" onblur="calc_amt(3);">
<input type="text" name="amount3" id="amount3" readonly>
<script>
function calc_amt(val){
if(val==1){
var option1 = document.getElementById("option1").value;
var pri1 = document.getElementById("price1").value;
....
document.getElementById("amount1").value=amoun1 ;
}
if(val==2){
var option2 = document.getElementById("option2").value;
var pri2 = document.getElementById("price2").value;
...
document.getElementById("amount2").value=amoun2;
}
if(val==3){
var option3 = document.getElementById("option3").value;
var pri3 = document.getElementById("price3").value;
....
document.getElementById("amount3").value=amoun3;
}
var amoun1=document.getElementById("amount1").value;
var amoun2=document.getElementById("amount2").value;
var amoun3=document.getElementById("amount3").value;
var tot = Number(amt1)+Number(amt2)+Number(amt3);
document.getElementById("amount").value=tot;
}
</script>
how do solve it by coding only once... I am beginner please help me.... any other ideas to solve this.. i need a solution like inheritance.
You can further reduce above script like this. Your amoun is unclear for though. However you can reduce the code like this. This is just an idea and make sure you match the variables with correct statement.
<script>
function calc_amt(val){
var option1 = document.getElementById("option"+val).value;
var pri1 = document.getElementById("price"+val).value;
....
document.getElementById("amount"+val).value=""+amount+val ;
var amoun1=document.getElementById("amount1").value;
var amoun2=document.getElementById("amount2").value;
var amoun3=document.getElementById("amount3").value;
var tot = Number(amt1)+Number(amt2)+Number(amt3);
document.getElementById("amount").value=tot;
}
</script>
Replace:
if(val==1){
var option1 = document.getElementById("option1").value;
var pri1 = document.getElementById("price1").value;
document.getElementById("amount1").value=amoun1 ;
}
with:
var amoun = document.getElementById("amount" + val).value;
var option = document.getElementById("option" + val).value;
var pri = document.getElementById("price" + val).value;
document.getElementById("amount" + val).value=amoun;
TRY...
Remove all inline handler and use blur handler like in demo
$("input[type=text]").on("blur", function () {
var id = this.id;
var last = id.charAt(id.length - 1); // get last id string value
var optionValue = $("#option" + last).val();
var priceValue = $("#price" + last).val();
var option = isNaN(optionValue) ? 0 : +optionValue; // check is nan
var price = isNaN(priceValue) ? 0 : +priceValue;
$("#amount" + last).val(option * price); // display multiply value
$("#amount").text($("input[type=text][id^=amount]").map(function () { // collect all amount1,2,3 values
var value = $(this).val();
return isNaN(value) ? 0 : +value;
}).get().reduce(function (a, b) { // add total value
return a + b;
}));
});
DEMO
OPTIMIZED CODE
$("input[type=text]:not([readonly])").on("blur", function () {
var obj = $();
obj = obj.add($(this)).add($(this).prevUntil('[readonly]')).add($(this).nextUntil('[readonly]'));
$(this).nextAll('[readonly]').first().val($.map(obj, function (val, i) {
return parseInt(val.value, 10) || 0;
}).reduce(function (a, b) {
return a * b
}, 1));
$("#amount").text($("input[type=text][id^=amount]").map(function () {
var value = $(this).val();
return isNaN(value) ? 0 : +value;
}).get().reduce(function (a, b) {
return a + b;
}));
});
DEMO

Categories

Resources