I have two buttons in my expense manager: 'Add Income' and 'Add Expense' which allow the user to add their income and expenses but they don't work and I'm not sure why. I have onclick listeners for both of the 'add expense' and 'add income buttons. What do I need to change in my JS code to solve this?
const uuidv4 = require('uuid/v4')
// Read existing expenses from localStorage
const getSavedExpenses = () => {
const expensesJSON = localStorage.getItem('expenses')
try {
return expensesJSON ? JSON.parse(expensesJSON) : []
} catch (e) {
return []
}
}
const expenses = getSavedExpenses()
// Save expenses to localStorage
const saveExpenses = (expenses) => {
localStorage.setItem('expenses', JSON.stringify(expenses))
}
const displayExpenses = (title, expensesJSON) => {
let html = `<div class="text-expense">`
for (let i = 0; i < expensesJSON.length; i++) {
html += `<textarea type="text" id="description" name="description"></textarea>`
}
html += `</div>`
title.innerHTML = html;
}
// Create account
const account = {
name: 'aaa',
expenses: [],
income: [],
addExpense: function (description, amount) {
this.expenses.push({
description: description,
amount: amount
})
},
addIncome: function (description, amount) {
this.income.push({
description: description,
amount: amount
})
},
getAccountSummary: function () {
let totalExpenses = 0
let totalIncome = 0
let accountBalance = 0
this.expenses.forEach(function (expense) {
totalExpenses = totalExpenses + expense.amount
})
this.income.forEach(function (income) {
totalIncome = totalIncome + income.amount
})
accountBalance = totalIncome - totalExpenses
}
}
account.addExpense('Rent', 850)
account.addExpense('Food Shopping', 60)
console.log(account.getAccountSummary())
// Listen for new expense to be created
$('#add-expense').on('click', function() {
location.href = '/expense.html'
})
// Listen for expense to be submitted
$('#submit-expense').on('click', function() {
saveExpenses(expenses)
location.href = '/index.html'
})
$('#income').on('click', function() {
location.href = '/income.html'
})
$('#submit-income').on('click', function() {
location.href = '/index.html'
})
$('#add-expense').on('click', function () {
const id = uuidv4()
expenses.push({
id: id,
title: '',
body: ''
})
saveExpenses(expenses)
location.href = `/expense.html#${id}`
displayExpenses(targetElem, getSavedExpenses())
})
Make sure your onclick event handlers are wrapped inside a onload handler (or jQuery's $(document).ready), so that you are sure your buttons are loaded before $(some_selector) is called, something like:
onload = function() {
// Listen for new expense to be created
$('#add-expense').on('click', function() {
location.href = '/expense.html'
})
// Listen for expense to be submitted
$('#submit-expense').on('click', function() {
saveExpenses(expenses)
location.href = '/index.html'
})
$('#income').on('click', function() {
location.href = '/income.html'
})
$('#submit-income').on('click', function() {
location.href = '/index.html'
})
$('#add-expense').on('click', function () {
const id = uuidv4()
expenses.push({
id: id,
title: '',
body: ''
})
saveExpenses(expenses)
location.href = `/expense.html#${id}`
displayExpenses(targetElem, getSavedExpenses())
})
}
Related
I have a small project with user cards.
I have a JSON file with data on users whose cards are generated.
My task is in the card to show the icons of those social networks that the user has. If the user does not have any of the social networks - do not show the icon of this social network in his profile.
Frankly, I do not understand how to do it.
The data in JSON come in this form (on the example of one user):
{
"id": 7,
"firstName": "Daniel",
"lastName": "Day-Lewis",
"profilePicture": "https://i2.wp.com/comicbookdebate.com/wp-content/uploads/2019/07/There20will20be20blood202-750x460.jpg?resize=750%2C460&ssl=1",
"contacts": [
"https://www.facebook.com/JasonStatham/",
"https://twitter.com/realjstatham",
"https://www.instagram.com/jasonstatham/?hl=ru"
]
}
JS index file (add so that it is clear how I work with a layout in this project):
'use strict';
const cardsContainer = document.querySelector('#root');
async function loadCards(url) {
try {
const response = await fetch(url);
const data = await response.json();
const cards = data.map((data) => generateUserCard(data));
cardsContainer.append(...cards);
} catch(e) {
throw new Error(e);
}
}
loadCards('./assets/js/data.json');
function generateUserCard(userObj) {
const fullName =
`${userObj.firstName} ${userObj.lastName}`.trim() ||
CARD_CONSTANTS.userName;
const imgWrapper = createUserCardImageWrapper(userObj, fullName);
const cardName = createElement('h2', {classNames: ['cardName']}, fullName);
const cardDescription = createElement('p', {classNames: ['cardDescription']}, userObj.description || CARD_CONSTANTS.cardDescription);
const cardArticle = createElement('article', {classNames: ['cardContainer']}, imgWrapper, cardName, cardDescription);
const card = createElement('li', {classNames: ['userCardWrapper']}, cardArticle);
return card;
}
function createUserCardImageWrapper(userObj, fullName) {
const userImgElem = createElement('img', {
classNames: ['cardImg'],
attributes: {
src: userObj.profilePicture,
alt: fullName,
'data-id': userObj.id,
},
listeners: {
error: errorHandler,
load: loadHandler,
},
});
const initialsElem = createElement(
'div',
{ classNames: ['initials'] },
getInitials(fullName)
);
const imgWrapperElem = createElement(
'div',
{
classNames: ['cardImgWrapper'],
attributes: { id: `imgWrapper${userObj.id}` },
},
initialsElem
);
return imgWrapperElem;
}
function errorHandler({ target }) {
target.remove();
}
function loadHandler({
target,
target: {
dataset: { id },
},
}) {
document.getElementById(`imgWrapper${id}`).append(target);
}
I associate social networks with icons in this way (on the example of Twitter):
const SUPPORTED_SOCIAL_NETWORKS = new Map([
[
"twitter.com",
{
src: "https://w7.pngwing.com/pngs/872/50/png-transparent-computer-icons-social-media-logo-twitter-social-media-blue-logo-social-media-thumbnail.png",
alt: "twitter link for",
},
],
["www.facebook.com"],
]);
This is actually quite straightforward, see the comments in the code
// Get the network hosts for of a user and optionally filter them
let getNetworkHosts = (userId, filter) => {
if (match = users.filter( user => userId === user.id )) {
contacts = match[0].contacts.map( url => (new URL(url)).hostname)
if (filter) contacts = contacts.filter(filter)
return contacts
}
}
// A filter that can be applied to a list
let supportedNetworkFilter = elem => SUPPORTED_SOCIAL_NETWORKS[elem]
// Create an image tag
let createImage = (url) => {
let img = document.createElement("img")
img.src = url
return img
}
users =
[ { "id": 7
, "firstName": "Daniel"
, "lastName": "Day-Lewis"
, "profilePicture": "https://i2.wp.com/comicbookdebate.com/wp-content/uploads/2019/07/There20will20be20blood202-750x460.jpg?resize=750%2C460&ssl=1"
, "contacts":
[ "https://www.facebook.com/JasonStatham/"
, "https://twitter.com/realjstatham"
, "https://www.instagram.com/jasonstatham/?hl=ru"
]
}
]
const SUPPORTED_SOCIAL_NETWORKS =
{ "twitter.com":
{ src: "https://w7.pngwing.com/pngs/872/50/png-transparent-computer-icons-social-media-logo-twitter-social-media-blue-logo-social-media-thumbnail.png"
, alt: "twitter link for"
}
, "www.facebook.com": {} // Oops, facebook has no face
}
// Add some images to a div
let div = document.getElementById('images')
for (network of getNetworkHosts(7, supportedNetworkFilter)) {
div.appendChild(createImage(SUPPORTED_SOCIAL_NETWORKS[network].src))
}
<div id="images"></div>
Currently, I enter data, it stores in the local storage and displays it accordingly. Here is the code for that flow:
Creating/Setting Data (in the create.js file)
let orders = [];
document.onreadystatechange = function () {
if (document.readyState === 'interactive') renderApp();
function renderApp() {
var onInit = app.initialized();
onInit.then(getClient).catch(handleErr);
function getClient(_client) {
window.client = _client;
//client.events.on('app.activated', onAppActivate1);
onAppActivate1();
}
}
};
function onAppActivate1() {
//intialize an array that will have all the keys user puts in
console.log('got here');
$("#btnCreateOrder").click(function () {
//orders = localStorage.getItem('orderlist');
let order = {
id: Date.now(),
order: document.getElementById('order').value,
name: document.getElementById('inputName').value,
date: document.getElementById('inputDate').value,
status: document.getElementById('inputStatus').value
}
orders.push(order);
if (!localStorage.getItem('orderlist') || JSON.parse(localStorage.getItem('orderlist')).length === 0) {
$window.localStorage.setItem('orderlist', JSON.stringify($scope.initData));
}
//localStorage.setItem('orderlist', JSON.stringify(orders));
client.instance.send({
message: {
orders: orders
}
});
client.instance.close();
});
}
function handleErr(err) {
console.log('Error Occuered', err);
}
Receiving/Displaying data (app.js)
function onAppActivate() {
//var displayID = new Array();
console.log("Hi!! We are in the app!");
client.instance.resize({ height: "350px" });
client.instance.receive(function (event) {
var data = event.helper.getData();
console.log("data is", data);
for (let i = 0; i < data.message.orders.length; ++i) {
console.log(data.message.orders.length);
const orderList = data.message.orders[i];
console.log("orderlist is ", orderList);
var order = document.createElement("div");
order.innerHTML = `<br/> Order#: ${orderList.order}<br/> Name: ${orderList.name}<br/>
Date: ${orderList.date} <br/> Status: ${orderList.status}`;
order.style.borderBottom = "1px solid black"
document.getElementById('orderhistory').appendChild(order);
}
})
when i refresh the app, my data stays but when i reload the browser, the data gets reset but I want the data to stay even if i reload the browser and keep appending to it
I've created a store with a list of products that are generated from js and i attached an event listener to every product.
For sorting purposes, i've decided to recreate the dom to put the products in the order that i want but the problem is that the click event doesnt work and i dont know why. My line of thinking is that if something is declared globally, it should be accesable from all corners of the aplication. Am i right?
const grid = document.querySelector('.grid');
//arr of products
const productsArr = [{
name: 'Aname1',
price: 200
},
{
name: 'Cname2',
price: 2000
},
{
name: 'Zname3',
price: 28
},
{
name: 'Pname4',
price: 5
}
];
const paintProducts = function() {
productsArr.forEach(product, () => {
let price = product.price;
let name = product.name;
//create html
productsHtml = `<div product data-price="${price}" data-name = "${name}">
<h6 class="price">${price} coco</h6>
<p class="product-descr">${description}</p>
</div>`
});
//insert html
grid.insertAdjacentHTML('beforeend', productsHtml);
};
paintProducts();
// filter
const aZBtn = document.querySelector('.a-z');
const filterAlphabetically = () => {
allInstruments.sort(function(i, j) {
if (i.name < j.name) {
return -1;
}
if (i.name > j.name) {
return 1;
}
return 0;
});
};
//clean the dom
const cleanGrid = function() {
grid.innerHTML = '';
};
aZBtn.addEventListener('click', function() {
filterAlphabetically();
cleanGrid();
paintProducts();
clickOnProduct();
});
const products = document.querySelectorAll(".product")
const clickOnProduct = function() {
products.forEach(function(product) {
product.addEventListener('click', () => {
console.log("something here")
})
})
}
<div class="grid-container">
<div class="a-z">Alphabetically</div>
<div class="grid">
</div>
</div>
First I found a logical flaw in paintProducts
The following line was outside the loop, so only 1 item could ever be rendered, the last one.
grid.insertAdjacentHTML('beforeend', productsHtml);
Second was in filterAlphabetically. There a mistery variable pops up allInstruments, replaced it with productsArr.
Third problem was const products = document.querySelectorAll(".product"). This was outside the clickOnProduct function.
As every repaint generates new DOM elements, after sorting, the events need to be bound to the new elements.
Fourth problem is the product div itself, it countained <div product ... but you are using a queryselector referring a class so this should be <div class="product" ...
When fixing all the above, we result in this:
<html>
<body>
<div class="grid-container">
<div class="a-z">Alphabetically</div>
<div class="grid">
</div>
</div>
</body>
<script>
const grid = document.querySelector('.grid');
//arr of products
const productsArr = [{
name: 'Aname1',
price: 200
},
{
name: 'Cname2',
price: 2000
},
{
name: 'Zname3',
price: 28
},
{
name: 'Pname4',
price: 5
}
];
const paintProducts = function() {
productsArr.forEach(product => {
let price = product.price;
let name = product.name;
//create html
productsHtml = `<div class="product" data-price="${price}" data-name = "${name}">
<h6 class="price">${price} coco</h6>
<p class="product-descr">${name}</p>
</div>`
//insert html
grid.insertAdjacentHTML('beforeend', productsHtml);
});
};
paintProducts();
// filter
const aZBtn = document.querySelector('.a-z');
const filterAlphabetically = () => {
productsArr.sort(function(i, j) {
if (i.name < j.name) {
return -1;
}
if (i.name > j.name) {
return 1;
}
return 0;
});
};
//clean the dom
const cleanGrid = function() {
grid.innerHTML = '';
};
aZBtn.addEventListener('click', function() {
filterAlphabetically();
cleanGrid();
paintProducts();
clickOnProduct();
});
const clickOnProduct = function() {
var products = document.querySelectorAll(".product")
products.forEach(function(product) {
product.addEventListener('click', () => {
console.log("something here")
});
});
}
clickOnProduct()
</script>
</html>
I am trying to generate a form based on these settings...
let formItems = new Form("form-items", {
items: [
{ id: "companyName" },
{ id: "documentKey" },
{ id: "documentDate" }
],
});
Inside, I generate each input, and try to add an eventListener, but it donesn't work. What am I doing wrong?
module.exports = function Form(formElementId, options) {
this.state = {}
options.items.map(item => {
renderInput(item.id);
this.state[item.id] = ""
})
function renderInput(id) {
let html = `<input id="${id}" />`
document.getElementById(formElementId).innerHTML += html;
document.getElementById(id).addEventListener("input", (e) => {
console.log(e); // <--------- Doesn't work
this.state[id] = e.target.value; // <--------- Doesn't work
console.log(this.state); // <--------- Doesn't work
})
}
}
Instead of having your variable as template literal, you can just dynamically create an HTML input element and attach to it the event, also instead of add the HTML to using += just have appended to the container
I would use this snippet instead:
module.exports = function Form(formElementId, options) {
this.state = {};
self = this;
options.items.map(item => {
renderInput(item.id);
this.state[item.id] = "";
});
function renderInput(id) {
let input = document.createElement("input");
input.setAttribute("id", id);
document.getElementById(formElementId).append(input);
input.addEventListener("input", e => {
self.state[id] = e.target.value;
});
}
};
I m new in suitescripts. I have made a Suitelet Form script with 3 fields which will act as filters on the scheduled script. The scheduled script in sending the PDF file to a certain email after filtering the 3 values of suit let script from my saved search.
When I hit the button on suitelet form, after entering the fields, the scheduled script goes on processing for 1 hour and then gets failed.
I think I have placed the wrong filters in the loading of My saved search in my scheduled script.
The saved search (customsearch_mx_itemsearch) is without any filters or criteria.
Please help if anyone knows. Thank you
My Suitelet form Script:
define(['N/ui/serverWidget', 'N/search', 'N/render', 'N/runtime', 'N/file', 'N/task'],
function (ui, search, render, runtime, file, task) {
/**
* main function for suitelet
* #param {object} ctx
*/
function onRequest(ctx) {
var req = ctx.request;
var res = ctx.response;
var param = req.parameters;
/**
* create form is creating the UI for report generation
*/
if (req.method === 'GET') {
// createForm(req, res, param);
createForm(req, res, param);
} else {
generateReport(req, res, param);
}
}
// R E Q U E S T
function createForm(req, res, param) {
if (req.method === 'GET') {
var form = ui.createForm({
title: 'Generate Item Report'
});
var item = form.addField({
id: 'custpage_selectitem',
type: ui.FieldType.SELECT,
label: 'Select Item',
source: 'item'
});
item.isMandatory = true;
var gender = form.addField({
id: 'custpage_selectgender',
type: ui.FieldType.SELECT,
label: 'Select Gender',
source: 'customrecord6'
});
gender.isMandatory = true;
var fromDate = form.addField({
id: 'custpage_selectdate',
// type: ui.FieldType.DATETIME,
type: ui.FieldType.DATE,
label: 'Select Date/Time',
});
form.addSubmitButton({
label: 'Generate Report'
});
res.writePage(form);
}
}
// R E S P O N C E
function generateReport(req, res, param) {
var param = req.parameters;
log.debug('parameters', param);
var script = runtime.getCurrentScript();
var filters = {
'isgender': param.custpage_selectgender,
'isItem': param.custpage_selectitem,
'fromDate': param.custpage_selectdate
};
log.debug('filters', filters);
var scriptTask = task.create({ taskType: task.TaskType.SCHEDULED_SCRIPT });
// scriptTask.scriptId = 3920;
scriptTask.scriptId = 'customscript_mx_itemreport_ss';
scriptTask.deploymentId = 'customdeploy_mx_itemreport_ss';
scriptTask.params = {
custscript_searchfilter_report: JSON.stringify(filters)
};
log.debug('workingtillhere');
var scriptTaskId = scriptTask.submit();
res.write("Your report is being generated. It will be emailed to you shortly.")
}
return {
onRequest: onRequest
};
});
My Scheduled script:
define(['N/ui/serverWidget', 'N/search', 'N/render', 'N/runtime', 'N/file', 'N/email'],
function (ui, search, render, runtime, file, email) {
function execute() {
try {
generateReport();
}
catch (e) {
log.error('generateReport ERROR', e);
}
}
function generateReport(req, res, param) {
var slfilters = runtime.getCurrentScript().getParameter({ name: 'custscript_searchfilter_report' });
log.debug('slfilters', slfilters);
if (!!slfilters) {
slfilters = JSON.parse(slfilters);
}
log.debug('slfilters2', slfilters);
var getUser = runtime.getCurrentUser();
var gender = slfilters.isgender
log.debug('gender', gender)
var item = slfilters.isItem
log.debug('item', item)
var item = getItems(item, gender);
log.debug('items table', item)
var xmlTemplateFile = file.load(3918);
var template = script.getParameter({ name: 'custscript_template' });
var renderer = render.create();
renderer.templateContent = xmlTemplateFile.getContents();
var customSources = {
alias: 'searchdata',
format: render.DataSource.JSON,
data: JSON.stringify({
value: item,
})
};
renderer.addCustomDataSource(customSources);
var xml = renderer.renderAsString();
var pdf = render.xmlToPdf({
"xmlString": xml
});
email.send({
author: 317,
recipients: 'aniswtf#gmail.com',
subject: 'Item Report',
body: 'Report Generated: ',
attachments: [pdf]
});
}
//
// ─── GET RESULTS ───────────────────────────────────────────────────
//
const getResults = function (set) {
var results = [];
var i = 0;
while (true) {
var result = set.getRange({
"start": i,
"end": i + 1000
});
if (!result) break;
results = results.concat(result);
if (result.length < 1000) break;
i += 1000;
}
return results;
};
//
// ─── GET ITEMS ───────────────────────────────────────────────────
//
function getItems(item, gender) {
try {
var itemSearch = search.load({
id: 'customsearch_mx_itemsearch'
});
var defaultFilters = itemSearch.filters;
var arrFilters = [];
arrFilters.push(search.createFilter({
name: 'custitem5',//gender
operator: 'anyof',
values: [gender]
}));
arrFilters.push(search.createFilter({
name: 'internalid',
operator: 'anyof',
values: [item]
}));
//defaultFilters.push(arrFilters)
defaultFilters = defaultFilters.concat(arrFilters);
var results = getResults(itemSearch.run()).map(function (x) {
return {
'category': x.getText({
name: "custitem10",
join: "parent"
}),
'season': x.getValue({
name: "custitem11",
join: "parent"
}),
'riselabel': x.getValue({
name: "custitem_itemriselabel",
join: "parent"
}),
'fit': x.getValue({
name: "custitem9",
join: "parent"
}),
'name': x.getValue({ //sku
name: "itemid",
join: "parent"
}),
'style': x.getValue({
name: "custitem8",
join: "parent"
}),
'inseam': x.getValue({
name: "custitem7",
join: "parent"
}),
'wash': x.getValue({
name: "custitem_washname",
join: "parent"
}),
};
});
return results;
} catch (e) {
log.error('error in getItems', e)
}
}
return {
execute: execute
};
});
You have the req, res, and param arguments defined for generateReport(), but you aren't actually populating them when you call generateReport() within execute(). You will need to pass values for those parameters.