Javascript calling a function in a function not defined classes - javascript

I am calling a method and want to have that method get the value of the returned value of the second method, to be able to use the variable in an element.
I always was able to call a function by putting the function in the other function. It seems when using classes I am unable to achieve this.
Do I have to use a callback method of some sort. I am new to classes.
class Bills{
constructor(amount,payment,duedate,apr){
this.amount = amount;
this.payment = payment;
this.duedate = duedate;
this.total = total;
this.apr = apr;
}
amountByMonth(billings){
//This is the function I am calling inside the function to get the value of.
let dueDays = daysLeft(billings);
const items = document.getElementById('bills');
const newitem = document.createElement('ul');
newitem.innerHTML = `
<li>Monthly Amount Due :${billings.amount}</li>
<li>Monthly Amount Due :${dueDays}</li>
<li>Total On Card: ${billings.total}</li>`;
items.appendChild(newitem);
}
daysLeft(billings){
let date1 = new Date();
let dueDate = new Date(billings.duedate);
let timeDiff = Math.abs(dueDate.getTime() - date1.getTime());
let diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
console.log(diffDays);
return diffDays;
}
}// end
document.getElementById('subBtn').addEventListener('click',valueinput);
function valueinput(e){
let amount = document.getElementById('payment').value;
let total = document.getElementById('total').value;
let duedate = document.getElementById('dues').value;
let billings = new Bills();
billings.amount = amount;
billings.duedate = duedate;
billings.total = total;
billings.daysLeft(billings);
billings.amountByMonth(billings);
e.preventDefault();
}

You need to make it clear that you are calling another method of the same class and not a different function by using this:
When you declare a class method without the function keyword, you're basically declaring it as a functional property of the class object. To refer back to the property, you need to put it in the context of the object it's defined in, i.e.
let dueDays = this.daysLeft(billings);

You must use this, for example if you want to call function inside the class function you must use this to your class knows the reference. So your code should looks like:
amountByMonth(billings){
let dueDays = this.daysLeft(billings);
// Rest of your code
}

Related

Getting undefined for object created in callback function

I am trying to use an object I am creating from a button click event. The object is available later in the code. The last console statement prints undefined. How can I return the object for use later in the code? Thank you.
const btn = document.getElementById('btn');
// Use IIFE to get human data from form
var getUserData = (
function () {
function getInput() {
let formContainer = document.getElementsByClassName('form-container')[0];
// Remove form from screen
formContainer.classList.add('hide');
//formContainer.style.display = 'none';
let main = document.getElementById('grid');
main.classList.remove('hide');
main.classList.add('show');
//main.style.display = 'flex';
let name = document.getElementById('name').value;
let feet = document.getElementById('feet').value;
let inches = document.getElementById('inches').value;
let height = (feet * 12) + inches;
let weight = document.getElementById('weight').value;
let diet = document.getElementById('diet').value;
return { name: name, height: height, weight: weight, diet: diet};
}
return(getInput);
})();
let human;
document.getElementById("btn").addEventListener("click", function (event) {
event.preventDefault();
var user = getUserData();
human = new Human(user.name, user.height, user.weight, user.diet);
getHumanInfo(); // console logs the human
return human; // returns udefined when I try to console log later in code
});
function getHumanInfo(){
console.log(human);
}
// this returns udefined
console.log('Human: ' + human);
You are adding an event listener, so the callback won't be called before a click is performed on the element with the id btn. And you are logging the variable human immediatly: it won't be defined because it hasn't been set yet.

How to store previous result into a var

Pretty new to Javascript..
I have managed to script this simple code here:
var currentstock = 50;
function stockminus(x){
newcurrent = currentstock - x;
return newcurrent;
};
console.log(stockminus(10));
the output of this must be : 40
How can I store that new output into currentstock as the new value or in other words overwrite that var?
so next time I call that function again, output should be : 30
Becasue the new currentstock is 40 from the previous call
Simple, currentstock = stockminus(10)
The let statement declares a block scope local variable, optionally initializing it to a value.
So, every time you are working in the same scope you can do:
let currentstock = 50;
function stockminus(x) {
return currentstock -= x;
};
console.log(stockminus(10));
console.log(stockminus(10));
var stockDetails = (function(initialValue){
var currentStock = initialValue;
return {
getCurrentStock: function() {
return currentStock;
},
stockminus: function(x) {
//add additional logic to check negative cases
currentStock-= x;
return currentStock;
}
}
})(50);
you can use this type of modular patterns to access data using methods and later extend it as per needs.
access it like
stockDetails.getCurrentStock();
stockDetails.stockminus(10);

Iterate over an HTMLcollection to dynamically define values in a constructor function?

i'll get right to the description and example code as i bet many of you are as confused about the title as i am (tried my best).
Situation: I have a form that submits input data to a new instance of an object, using a constructor function.
CURRENT CODE:
// Selectors
var submit = document.getElementById('submit-btn');
var formValues = document.getElementsByClassName('input');
// ** Task Object
function Task(arrayOfValues) {
this.title = arrayOfValues[0].value;
this.deadline = arrayOfValues[1].value;
this.status = arrayOfValues[2].value;
}
submit.addEventListener('click', function(e) {
e.preventDefault();
var newTask = new Task(formValues);
}, false);
Problem: Passing the array as an argument and assigning each index manually feels incredibly brittle to me. If i were to extend the data collection of the form i would have to manually assign each new value as a separate variable. What is the syntax or pattern if you will for iterating over an array and dynamically assigning values to variables in a constructor function ? Thank you in advance. Any and all help is much appreciated.
EXAMPLE of desired CODE
var formValues = document.getElementsByClassName('input');
// ** Task Object
function Task(arrayOfValues) {
this.values = arrayOfValues;
for (var i=0;i<this.values.length;i++) {
var key = this.values[i].getAttribute('name');
// This should be used with "this" to reference key ie. "this.title, this.deadline, this.status ect...
var value = this.values[i].value;
// This will be the value associated with each key reference ie. "this"
return this[key] = value;
// is this even possible ?
}
}
submit.addEventListener('click', function(e) {
e.preventDefault();
var newTask = new Task(formValues);
}, false);
Just use the names of the form elements as the property names in the object.
function Task(arrayOfValues) {
for (var i=0;i<arrayOfValues.length;i++) {
var key = arrayOfValues[i].name;
var value = arrayOfValues[i].value;
this[key] = value;
}
}
Don't use return inside the loop, that will end the constructor after processing the first element in the array, and ignore the rest.
I would just take the three parameters separately, let the caller figure out which one is the title, deadline or status
function Task(title, deadline, status) {
this.title = title;
this.deadline = deadline;
this.status = status;
}
Now your callers can figure out which one is which by using IDs, data-attributes, or anything they want.
<input id="title-x"> <input id="deadline"> <input id="status">
submit.addEventListener('click', function(e) {
e.preventDefault();
var getVal = function(id) { return document.getElementById(id).value; }
var newTask = new Task(getVal('title-x'), getVal('deadline'), getVal('status'));
}, false);
Now your constructor doesn't rely on the order within the array, or even that they are DOM elements.
You could always have a contract where a specific input maps to a task field through data-attributes
<input data-task-field="title">
<input data-task-field="deadline">
<input data-task-field="status">
<input data-task-field="newField">
/**
* #param {object} taskDescription
* #param {string} taskDescription.title
* #param {string} taskDescription.deadline
* #param {string} taskDescription.status
* #param {string} taskDescription.newField
*/
function Task(taskDescription) {
this.task = taskDescription;
}
submit.addEventListener('click', function(e) {
e.preventDefault();
var tasks = document.querySelectorAll('input[data-task-field]');
var taskSpec = {};
for (var i=0; i < tasks.length; i++) {
taskSpec[tasks.getAttribute('data-task-field')] = tasks.value;
}
var newTask = new Task(taskSpec);
}, false);
I would use an associative array -
http://www.w3schools.com/js/js_arrays.asp
var assArray = [];
assArray["title"] = document.getElementbyID(......)
That makes your array semantically meaningful and less brittle. It doesn't matter if you expand the form, so long as you don't change the names it will continue to work.
It also means you don't have to change the call signature of your constructor each time you expand the form, you only have to alter the body to deal with the new elements in the array (or not, as you so choose).

Javascript simple dice object with roll & display functions

I NEED TO CREATE A DICE OBJECT, so how can I include a dice object into this code?
I am creating a very simple dice roll in javascript. I have to have a dice object with an attribute named value, a roll method, and a printDie method which shows the two die numbers on the page. When I try this code I get "undefined." Any help is appreciated, thank you!
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
<script>
// dice object attribute named value, method printDie, method roll
function dice(value){
this.value = value;
this.roll = function(roll){
}
this.printDie = function(printDie){
}
}
function roll(){
var die = Math.floor(Math.random()*6)+1;
return die;
}
function printDie(){
this.roll();
var die1 = document.getElementById('die1');
var die2 = document.getElementById('die2');
die1.innerHTML = die1.value;
die2.innerHTML = die2.value;
}
</script>
</head>
<body>
<button onClick="printDie()">Roll Dice</button>
<h1 id="die1"></h1> <h1 id="die2"></h1>
</body>
</html>
There are several problems with your code, the first of which is the use of the this keyword, which will only work if you are making an instance of dice. Also, you are not capturing the result of the roll method.
Look at the comments below for more issues:
// dice object attribute named value, method printDie, method roll
function dice(value){
// The only way the use of the word this will help you here
// is if the function is called as a constructor (var d = new dice())
// Because that's not the case, this refers to the global window
// object, which does not have a value property
this.value = value;
// Here, you are adding a roll property to the global window object
// and assigning a function to it
this.roll = function(roll){
}
// Here, you are creating a printDie property on the global window object
this.printDie = function(printDie){
}
}
// if this function is a function of the dice, it should be added to the
// dice.prototype as in: dice.prototype.roll = function () { var die = . . . }
function roll(){
var die = Math.floor(Math.random()*6)+1;
return die;
}
function printDie(){
// You are calling roll(), which returns a value, but you are not
// capturing that value? Also, no need for this.roll(), use just roll()
this.roll();
var die1 = document.getElementById('die1');
var die2 = document.getElementById('die2');
die1.innerHTML = die1.value;
die2.innerHTML = die2.value;
}
Also, as an FYI, you really should avoid inline HTML event handlers (onclick="") as they create spaghetti code and cause the code you supplied as a value for onclick to be wrapped in a global function, which causes problems if/when the keyword this is used within the callback code. Instead, wire event handlers using the DOM Event Model standard (via JavaScript)
element.addEventListener("eventName", callbackFunction);
Here is working code
I now invoke the object and chain it in the die.roll().printDie()
I also changed the type of button to button to stop submission in some browsers. All the methods are now local to the object you need to create and the click is moved out of the button.
function Dice() {
this.die = [];
this.roll = function(roll) {
this.die = [Math.floor(Math.random() * 6) + 1, Math.floor(Math.random() * 6) + 1]
return this; // for chaining
}
this.printDie = function() {
document.getElementById('die1').innerHTML = this.die[0];
document.getElementById('die2').innerHTML = this.die[1];
}
return this;
}
window.onload=function() {
document.getElementById("rollBut").onclick=function() {
var die = new Dice()
die.roll().printDie();
}
}
<button type="button" id="rollBut">Roll Dice</button>
<h1 id="die1"></h1>
<h1 id="die2"></h1>

How to use javascript class from within document ready

I have this countdown script wrapped as an object located in a separate file
Then when I want to setup a counter, the timeout function in the countdown class can not find the object again that I have setup within the document ready.
I sort of get that everything that is setup in the document ready is convined to that scope,
however it is possible to call functions within other document ready´s.
Does anyone has a solution on how I could setup multiple counters slash objects.
Or do those basic javascript classes have to become plugins
This is the class
function countdown(obj)
{
this.obj = obj;
this.Div = "clock";
this.BackColor = "white";
this.ForeColor = "black";
this.TargetDate = "12/31/2020 5:00 AM";
this.DisplayFormat = "%%D%% Days, %%H%% Hours, %%M%% Minutes, %%S%% Seconds.";
this.CountActive = true;
this.DisplayStr;
this.Calcage = cd_Calcage;
this.CountBack = cd_CountBack;
this.Setup = cd_Setup;
}
function cd_Calcage(secs, num1, num2)
{
s = ((Math.floor(secs/num1))%num2).toString();
if (s.length < 2) s = "0" + s;
return (s);
}
function cd_CountBack(secs)
{
this.DisplayStr = this.DisplayFormat.replace(/%%D%%/g, this.Calcage(secs,86400,100000));
this.DisplayStr = this.DisplayStr.replace(/%%H%%/g, this.Calcage(secs,3600,24));
this.DisplayStr = this.DisplayStr.replace(/%%M%%/g, this.Calcage(secs,60,60));
this.DisplayStr = this.DisplayStr.replace(/%%S%%/g, this.Calcage(secs,1,60));
//document.getElementById(this.Div).innerHTML = this.DisplayStr;
$('#'+this.Div).text(this.DisplayStr);
$('#tel').text(parseInt( $('#tel').text() )+1);
if (this.CountActive) setTimeout(this.obj +".CountBack(" + (secs-1) + ")", 990);
}
function cd_Setup()
{
var dthen = new Date(this.TargetDate);
var dnow = new Date();
ddiff = new Date(dthen-dnow);
gsecs = Math.floor(ddiff.valueOf()/1000);
this.CountBack(gsecs);
}
and setting it up
$(document).ready(function() {
var cd1 = new countdown('cd1');
cd1.Div = "clk";
cd1.TargetDate = "08/15/2010 8:00 PM";
cd1.DisplayFormat = "%%D%% days, %%H%% hours, %%M%% minutes, %%S%% seconds until event AAA happens";
cd1.Setup();
firebug says it errors out with the timeout function
thanks, Richard
cd1 is defined in the local scope. setTimeout will run the function passed as parameter 1 in the window [global] scope, and in your case, window.cd1 is undefined.
The solution for your problem would to make cd1 a global variable. [Remove the "var" in your declaration of cd1]
Off topic: I recommend you look into using anonymous functions, as they can make your code much more pretty/legible at times.

Categories