//Budget controller
var budgetController = (function(){
// Made expense and income functions to calculate Data
var Expense = function(id,description,value){
this.id = id;
this.description = description;
this.value = value;
};
var Income = function(id,description,value){
this.id = id;
this.description = description;
this.value = value;
};
var calculateTotal = function(type){
var sum = 0;
data.allItems[type].forEach(function(cur){
sum += cur.value;
});
data.totals[type] = sum;
};
var data = {
allItems:{
exp:[],
inc:[]
},
totals:{
exp:0,
inc:0
},
budget:0,
percentage: -1
};
return {
addItem: function(type,des,val){
var newItem, ID;
// Create new ID
if (data.allItems[type].length > 0 ){
ID = data.allItems[type][data.allItems[type].length -1].id + 1;
}
else {
ID = 0;
}
// Create new Item based on "inc" or "exp" type
if (type === 'exp') {
newItem = new Expense(ID,des,val);
}
else if(type === "inc"){
newItem = new Income(ID,des,val);
}
// Push it into Data structure
data.allItems[type].push(newItem);
// Return element
return newItem;
},
deleteItem: function (type, id) {
var ids, index;
// data.allItems[type][id];
// id = 6
// index = 3
// ids = [1, 2, 4 , 6, 9];
// index = [0, 1, 2, 3, 4];
ids = data.allItems[type].map(function (current) {
return current.id;
});
index = ids.indexOf(id);
if (index !== -1) {
data.allItems[type].splice(index, 1);
}
},
calculateBudget: function(){
//calculate total income and expense
calculateTotal('exp');
calculateTotal('inc');
//calculate the Budget income - expense
data.budget = data.totals.inc - data.totals.exp;
//calculate the percentage of income
if (data.totals.inc > 0) {
data.percentage = Math.round((data.totals.exp/data.totals.inc) * 100);
}
else {
data.percentage = -1;
}
},
getBudget: function(){
return {
budget:data.budget,
totalInc:data.totals.inc,
totalExp:data.totals.exp,
percentage:data.percentage
};
},
testing: function(){
console.log(data);
}
};
})();
// UI controller
var UIController = (function(){
var DOMstrings = {
inputType:'.add__type',
inputDescription: '.add__description',
inputValue:'.add__value',
inputBtn: '.add__btn',
incomeContainer:'.income__list',
expensesContainer:'.expenses__list',
budgetLabel:'.budget__value',
incomeLabel:'.budget__income--value',
expensesLabel:'.budget__expenses--value',
percentageLabel:'.budget__expenses--percentage',
container:'.container'
}
return {
getInput: function(){
return {
// getting input data from elements
type: document.querySelector(DOMstrings.inputType).value,
description: document.querySelector(DOMstrings.inputDescription).value,
value: parseFloat(document.querySelector(DOMstrings.inputValue).value)
};
},
addListItem: function(obj, type) {
var html, newHtml, element;
//Create html stings with placeholder text
if (type === 'inc'){
element = DOMstrings.incomeContainer;
html = html = html = '<div class="item clearfix" id="income-%id%"><div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>'
}
else if(type === 'exp') {
element = DOMstrings.expensesContainer;
html = '<div class="item clearfix" id="expense-%id%"><div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__percentage">21%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>';
}
//Replace the placeholder text with actual data
newHtml = html.replace('%id%', obj.id);
newHtml = newHtml.replace('%description%', obj.description);
newHtml = newHtml.replace('%value%', obj.value);
// insert html into DOM
document.querySelector(element).insertAdjacentHTML('beforeend',newHtml);
},
clearFields: function(){
var fields, fieldsArr;
fields = document.querySelectorAll(DOMstrings.inputDescription + ' , ' + DOMstrings.inputValue);
fieldsArr = Array.prototype.slice.call(fields);
fieldsArr.forEach(function(current,index,array){
current.value = "";
});
fieldsArr[0].focus();
},
displayBudget: function(obj){
document.querySelector(DOMstrings.budgetLabel).textContent=obj.budget;
document.querySelector(DOMstrings.incomeLabel).textContent=obj.totalInc;
document.querySelector(DOMstrings.expensesLabel).textContent=obj.totalExp;
if (obj.percentage > 0){
document.querySelector(DOMstrings.percentageLabel).textContent=obj.percentage + "%" ;
}
else{
document.querySelector(DOMstrings.percentageLabel).textContent="---";
}
},
// exposing to public so that we can excess DOMstrings
getDOMstrings: function(){
return DOMstrings;
}
};
})();
// Global App controller.
var controller = (function(budgetCtrl,UICtrl){
// privioulsly all the event listeners were scattered But now i have a seprate function SetupEventListeners for all events
var setupEventListeners = function(){
var DOM = UICtrl.getDOMstrings();
document.querySelector(DOM.inputBtn).addEventListener('click',ctrlAddItem);
document.addEventListener('keypress',function(event){
if (event.keyCode === 13 || event.which === 13) {
ctrlAddItem();
}
});
document.querySelector(DOM.container).addEventListener('click',ctrlDeleteItem);
};
var updateBudget = function(){
//calculate budget
budgetCtrl.calculateBudget();
//return budget
var budget = budgetCtrl.getBudget();
//Display budget on UI
UICtrl.displayBudget(budget);
}
var ctrlAddItem = function(){
var input, newItem;
// Get the input data.
input = UICtrl.getInput();
if(input.description !== "" && !isNaN(input.value) && input.value > 0 ) {
//add the item to the budget controller
newItem = budgetCtrl.addItem(input.type,input.description,input.value);
// add the item to the Ui
UICtrl.addListItem(newItem, input.type);
//Clear fields
UICtrl.clearFields();
// calculate and update budget
updateBudget();
}
};
var ctrlDeleteItem = function (event) {
var itemID, splitID, type, ID;
// example: ID = 'inc-0'
itemID = event.target.parentNode.parentNode.parentNode.parentNode.id;
if (itemID) {
//inc-1
splitID = itemID.split('-');
type = splitID[0];
ID = parseInt(splitID[1]);
// 1. Delete the item from the data structure
budgetCtrl.deleteItem(type, ID);
// 2. Delete the item from UI
// 3. Update and show the new budget
}
};
return {
init:function(){
console.log("Your App Has started");
UICtrl.displayBudget({
budget:0,
totalInc:0,
totalExp:0,
percentage:-1
});
setupEventListeners();
}
};
})(budgetController,UIController);
controller.init();
I’ve edited your post for readability. When you enter a code block into the forum, precede it with a line of three backticks and follow it with a line of three backticks to make easier to read. See this post to find the backtick on your keyboard. The “preformatted text” tool in the editor (</>
) will also add backticks around text.
When you post a large chunk of code and only put a title of “Canot read property of Map undefined”, you are not going to get much of a response. You need to explain what you expected your code to do, so we can focus our efforts on helping you with that specific part.
BTW - When I run the code you posted as it shows now, I get the following error message:
TypeError: Cannot set property ‘textContent’ of null (line 192)