Through an object, I need to create HTML elements using the Javascript loop.
The sample code displays the contract, contract lots and batch invoices.
Well, I just need to display the contracts as a list and, for each contract, add the lots of the contract in a drop-down list (selection option)` and then display the invoices in another list, according to the selected lot.
there was a problem displaying batches, but Barmar's comment helped me
Resume:
1: Create an event to load invoices when changing the batch
2: load the lots and invoices related to the contract by clicking on the contract.
Sample code
UPDATE: Added loading invoices
<div id="div-contracts"></div>
<ul id="div-invoices"></ul>
<style>
#div-contracts {
display: table-cell;
width: 100%;
}
.loren {
float:left;
width: 45px;
}
.ipsulum {
float:left;
width: 35%;
}
</style>
<script>
let object = {
name: 'wagner',
contracts: [
{
id: 1,
contract: '123',
batches: [
{
id: 1,
batch: '1',
invoices: [
{
value: 10,
due: '01/01/2020',
},
{
value: 10,
due: '01/02/2020',
},
],
},
{
id: 2,
batch: '2',
invoices: [
{
value: 10,
due: '01/04/2020',
},
{
value: 10,
due: '01/05/2020',
},
],
},
]
},
{
id: 1,
contract: '456',
batches: [
{
id: 3,
batch: '1',
invoices: [
{
value: 15,
due: '01/01/2020',
},
{
value: 14,
due: '01/02/2020',
},
],
},
]
},
]
};
let getById = (id, parent) => parent ? parent.getElementById(id) : getById(id, document);
const DOM = {
contract: getById('div-contracts'),
};
function htmlForBatchsOptions(contract) {
return contract.batches.map(batch => `<option value="${batch.id}">${batch.batch}</option>`).join('');
};
function htmlForInvoices(invoices) {
let invoiceLi = '';
for (const invoice of invoices) {
invoiceLi += `<li>${invoice.value} - ${invoice.due}</li>`;
}
return document.querySelector('#div-invoices').innerHTML = invoiceLi;
};
for (const contract of object.contracts) {
DOM.contract.innerHTML +=
`<div class="contract">
<div class="loren">
<span>${contract.contract}</span>
</div>
<div class="ipsulum">
<select class="sel-batch">
${htmlForBatchsOptions(contract)}
</select>
</div>
</div>`;
let = contractInAttendance = 1;
let batchFilteredByContractId = contract.batches.filter(batch => batch.id === contractInAttendance);
htmlForInvoices(batchFilteredByContractId[0].invoices);
}
</script>
Related
I have this array of JSON objects:
and I want to add a unique ID (string) to each entry, like this:
let myTree = [
{
text: 'Batteries',
id: '0',
children: [
{
text: 'BatteryCharge',
id: '0-0'
},
{
text: 'LiIonBattery',
id: '0-1'
}
]
},
{
text: 'Supplemental',
id: '1',
children: [
{
text: 'LidarSensor',
id: '1-0',
children: [
{
text: 'Side',
id: '1-0-0'
},
{
text: 'Tower',
id: '1-0-1'
}
]
}
]
}
]
I just can't think of the right logic to achieve this. I have written this recursive function, which obviously does not achieve what I want:
function addUniqueID(tree, id=0) {
if(typeof(tree) == "object"){
// if the object is not an array
if(tree.length == undefined){
tree['id'] = String(id);
}
for(let key in tree) {
addUniqueID(tree[key], id++);
}
}
}
addUniqueID(myTree);
How can I solve this problem?
Instead of using a number/id in the recursive function I build a string.
let myTree = [{
text: 'Batteries',
children: [{
text: 'BatteryCharge'
},
{
text: 'LiIonBattery'
}
]
},
{
text: 'Supplemental',
children: [{
text: 'LidarSensor',
children: [{
text: 'Side'
},
{
text: 'Tower'
}
]
}]
}
];
function addUniqueID(arr, idstr = '') {
arr.forEach((obj, i) => {
obj.id = `${idstr}${i}`;
if (obj.children) {
addUniqueID(obj.children, `${obj.id}-`);
}
});
}
addUniqueID(myTree);
console.log(myTree);
I hope you are well.
why don't you consider using uuid?
In node there is the uuid module which you can use to generate unique identifiers, I share a base example:
install:
npm install uuid
npm i --save-dev #types/uuid
code:
import {v4 as uuid} from 'uuid';
let _id = uuid();
I found an example of how to fill the table with pdfmake dynamically, which has 2 columns. Now I tried add another column 'height' to the table but I don't know how to modify it.
function buildTableBody(data, columns) {
var body = [];
body.push(columns);
data.forEach(function(row) {
var dataRow = [];
columns.forEach(function(column) {
dataRow.push(row[column].toString());
})
body.push(dataRow);
});
return body;
}
function table(data, columns) {
return {
table: {
headerRows: 1,
body: buildTableBody(data, columns)
}
};
}
function Pdftest(){
var externalDataRetrievedFromServer = [
{ name: 'Bartek', age: 34, height: 1.78 },
{ name: 'John', age: 27, height: 1.79 },
{ name: 'Elizabeth', age: 30, height: 1.80 },
];
var dd = {
content: [
{ text: 'Dynamic parts', style: 'header' },
table(externalDataRetrievedFromServer, ['name', 'age', 'height'])
]
}
pdfMake.createPdf(dd).download();
}
Does anybody know what needs to be modified?
Error was anywhere else, the code above works perfectly fine
I am trying to add tasks for each todo list that has a specific title.
Can I get a specific todo list by its id and add some tasks to it?
I am new to javascript, so I searched google about adding lists for a specific list with no results :(
class Model {
constructor() {}
this.todos = [
{
id: 1,
title: 'Outside',
text: 'Running',
complete: false,
tasks: [
{ id: 1, text: 'Run a marathon', complete: false},
{ id: 2, text: 'Run with freinds', complete: false}
]
},
{
id: 2,
title: 'Garden',
text: 'Plant',
complete: false,
tasks: [
{ id: 1, text: 'Plant a garden', complete: false},
{ id: 2, text: 'Water the garden', complete: false}
]
}];
addTodo(todoText) {
const todo = {
id: this.todos.length > 0 ? this.todos[this.todos.length - 1].id + 1 : 1,
text: todoText,
complete: false,
tasks: []
}
this.todos.push(todo)
}
}
Is it true to do like addTodo function for adding a tasks for a specific todo list like this?
addTodoTask(todoTaskText) {
const todoTask = {
id: this.todos.tasks.length > 0 ? this.todos[this.todos.tasks.length - 1].id + 1 : 1,
text: todoText,
complete: false,
}
this.todos.tasks.push(todoTask)
}
and how to add a list of a list in javascript like:
<ul>
<li>Running
<ul>
<li>Run a marathon</li>
<li>Run with freind</li>
</ul>
</li>
</ul>
You could make each class handle rendering its own content and just map the list items consecutively while rendering from the top-down.
Edit: The render() methods make use of ES6 template literals. These are special strings that allow you embed variabes and expressions without the use of string concatenation.
const main = () => {
let todoList = new TodoList({ todos : getData() })
document.body.innerHTML = todoList.render()
}
class TodoTask {
constructor(options) {
this.id = options.id
this.text = options.text
this.complete = options.complete
}
render() {
return `<li>[${this.id}] ${this.text} (${this.complete})</li>`
}
}
class TodoEntry {
constructor(options) {
this.id = options.id
this.title = options.title
this.text = options.text
this.complete = options.complete
this.tasks = []
if (options.tasks) {
options.tasks.forEach(task => this.addTask(task))
}
}
addTask(task) {
this.tasks.push(new TodoTask(Object.assign({
id : (this.tasks.length || 0) + 1
}, task)))
}
render() {
return `<li>
[${this.id}] ${this.title} (${this.complete})
<ul>${this.tasks.map(task => task.render()).join('')}</ul>
</li>`
}
}
class TodoList {
constructor(options) {
this.todos = []
if (options.todos) {
options.todos.forEach(todo => this.addTodo(todo))
}
}
addTodo(todo) {
this.todos.push(new TodoEntry(Object.assign({
id : (this.todos.length || 0) + 1
}, todo)))
}
render() {
return `<ul>${this.todos.map(todo => todo.render()).join('')}</ul>`
}
}
function getData() {
return [{
id: 1,
title: 'Outside',
text: 'Running',
complete: false,
tasks: [{
id: 1,
text: 'Run a marathon',
complete: false
}, {
id: 2,
text: 'Run with freinds',
complete: false
}]
}, {
id: 2,
title: 'Garden',
text: 'Plant',
complete: false,
tasks: [{
id: 1,
text: 'Plant a garden',
complete: false
}, {
id: 2,
text: 'Water the garden',
complete: false
}]
}]
}
main() // entry
To add a task your todo, you should have a way of knowing which todo list you're updating. Like using the todo's id.
For example your addTaskToTodo will looks like so.
addTask(todoId, taskObject) {
// find that todos index
const todoIndex = this.todos.findIndex(todo => todo.id ===todoId);
// using that index update the tasks
this.todos[todoIndex].tasks.push(taskObject)
}
This assumes your taskObject already has all the properties. If you need to manually update its id, you can also do that before pushing by checking the length of the tasks and incrementing by 1.
I made an example of how to use dictionaries instead of arrays, and also a random ID. I think you will find it much cleaner and simpler:
class Model {
constructor() { }
todos = {
1: {
id: 1,
title: 'Outside',
text: 'Running',
complete: false,
tasks: {
1: { id: 1, text: 'Run a marathon', complete: false },
2: { id: 2, text: 'Run with freinds', complete: false }
}
},
2: {
id: 2,
title: 'Garden',
text: 'Plant',
complete: false,
tasks: {
1: { id: 1, text: 'Plant a garden', complete: false },
2: { id: 2, text: 'Water the garden', complete: false }
}
}
}
getRandomId = () => {
return '_' + Math.random().toString(36).substr(2, 9);
}
addTodo(todoText) {
const id = this.getRandomId();
const todo = {
id,
text: todoText,
complete: false,
tasks:{}
}
this.todos[id] = todo;
}
addTodoTask(todoTaskText,todoId) {//Pass also the id of the todo, to know where this task belongs to.
const id = this.getRandomId();
const todoTask = {
id,
text: todoTaskText,
complete: false,
}
this.todos[todoId].tasks[id] = todoTask
}
}
This way you could easily edit/remove both todos and tasks, just by their id, without using any messy Array.filter and such
I have an array of elements and each element is super-complex because its attributes are arrays which contains other arrays as properties. I want to extract just few attributes of this element, I've tried with the forEach function but it doesn't work.
The array comes from a json file, that's why I use axios, and the elements of the array are something like this:
{
"ITEMS":[
{
"id":"0001",
"name":"foo",
"sizes":[
{
"name":"small",
"id":"9999",
"available":"no"
},
{
"name":"medium",
"id":"9998",
"available":"yes"
}
]
},
{
"id":"0002",
"name":"bar",
"sizes":[
{
"name":"small",
"id":"4444",
"available":"yes"
},
{
"name":"medium",
"id":"4443",
"available":"no"
}
]
},
...
]
}
So I want to collect the their attributes creating elements that are PUSHED in an array and that replicate this model:
this.sample = {
colour:'item.name',
size:'item.size.name[i]',
idcode:'item.id',
sizecode:'item.size.id[i]',
available:'item.size.available[i]'
}
this is my attempt (not working)
const axios = require('axios');
class IndianaJones {
constructor(){
this.sample = {
name:'',
colour:'',
size:'',
idcode:'',
sizecode:'',
available:''
},
this.newids = ["10","11","12"...]
this.freshprods = []
}
async FreshProd(){
for(this.i=0;this.i<this.newids.length;this.i++){
this.prod = await axios.get(`https://blablabla/${this.newids[this.i]}.json`)
this.ITEMS.forEach(function(item){
this.sample.idcode=item.id;
this.sample.colour=item.name;
item.sizes.forEach(function(SIZE){
this.sample.size=SIZE.name
this.sample.sizecode=SIZE.id
this.sample.available=SIZE.available
this.freshprods.push(this.sample)
})
}
)
}
return this.freshprods
}
}
(async()=>{
const indiana = new IndianaJones();
await indiana.FreshProd()
})()
Really, this is driving me up to wall, i would be SO GRATEFUL for anyone who can help me, maybe LODASH could be useful?
You are trying to flatten the structure. To so you can use Array.flatMap() (or lodash's _.flatMap() to iterate the ITEMS, map the sizes array, and return a new object for each size:
const prods = {"ITEMS":[{"id":"0001","name":"foo","sizes":[{"name":"small","id":"9999","available":"no"},{"name":"medium","id":"9998","available":"yes"}]},{"id":"0002","name":"bar","sizes":[{"name":"small","id":"4444","available":"yes"},{"name":"medium","id":"4443","available":"no"}]}]};
const freshprods = prods.ITEMS.flatMap(
({ id: idcode, name: colour, sizes }) =>
sizes.map(o => ({
colour,
size: o.name,
idcode,
sizecode: o.id,
available: o.available
}))
);
console.log(freshprods);
let prod = {
"ITEMS":[
{
"id":"0001",
"name":"foo",
"sizes":[
{
"name":"small",
"id":"9999",
"available":"no"
},
{
"name":"medium",
"id":"9998",
"available":"yes"
}
]
},
{
"id":"0002",
"name":"bar",
"sizes":[
{
"name":"small",
"id":"4444",
"available":"yes"
},
{
"name":"medium",
"id":"4443",
"available":"no"
}
]
}
]
}
let freshprods = [];
prod.ITEMS.forEach(function(item){
item.sizes.forEach(function(SIZE){
freshprods.push({
idcode: item.id,
colour: item.name,
size: SIZE.name,
sizecode: SIZE.id,
available: SIZE.available
})
})
})
console.log(freshprods);
Output
[ { idcode: '0001',
colour: 'foo',
size: 'small',
sizecode: '9999',
available: 'no' },
{ idcode: '0001',
colour: 'foo',
size: 'medium',
sizecode: '9998',
available: 'yes' },
{ idcode: '0002',
colour: 'bar',
size: 'small',
sizecode: '4444',
available: 'yes' },
{ idcode: '0002',
colour: 'bar',
size: 'medium',
sizecode: '4443',
available: 'no' } ]
I have the following data that needs to be turned into a form.
var contract = [
{
contract_id: 1,
options: [
{ contract_option_id: 2, contract_id: 1, payment: 'first', price: 100 },
{ contract_option_id: 3, contract_id: 1, payment: 'second', price: 100 },
{ contract_option_id: 4, contract_id: 1, payment: 'full', price: 200 }
]
},
{
contract_id: 2,
options: [
{ contract_option_id: 5, contract_id: 2, payment: 'first', price: 200 },
{ contract_option_id: 6, contract_id: 2, payment: 'second', price: 100 },
{ contract_option_id: 7, contract_id: 2, payment: 'full', price: 300 }
]
},
];
I'm basically creating a "shopping cart" to pay contract installments. Each contract has many contract_options which are payment installments. They can either pay the first, second, or entire contract at once depending on what they choose.
So I need to create a form that only allows adding one contract option per item. Here is what I have but I am running into issues with keeping the items independent of eachother and was wondering if i should make a component of the item or the item option?
<table>
<tr v-for="contract in availableContracts">
<td>
<div v-for="option in contract.options">
<input type="radio" #click="storeInCart(option)" name="item_{{option.contract_id}}" value={{option.price}} >
{{ option.payment }} ({{ option.price| currency }})
</div>
</td>
<td>
<button #click="addItem(item)">
add to cart
</button>
</td>
// remove shows up when item added
<td><a #click="removeItem(item)">remove</a></td>
</tr>
new Vue({
el: "#search-results",
data: {
availableItems: items,
selectedItem: {},
cart: cartStorage.fetch()
},
watch: {
cart: {
handler: function(cart) {
cartStorage.save(cart);
},
deep: true
}
},
computed: {
subTotal: function() {
var total = 0;
var cartSize = this.cart.length;
for(var i = 0; i < cartSize; i++) {
total += this.cart[i].options[0].price;
}
return (total / 100).toFixed(2);
}
},
methods: {
addItem: function() {
this.cart.push(this.selectedItem);
},
removeItem: function(item) {
this.cart.splice(this.cart.indexOf(item), 1);
},
storeInCart: function(option) {
this.selectedContract = {
contract_id: option.contract_id,
options: [option]
};
}
}
});
Components don't by themselves make this kind of data management easier. They are more about isolation and reuse. Here's what I would suggest.
storeInCart: function(option) {
this.selectedContracts[option.contract_id] = {
contract_id: option.contract_id,
options: [option]
};
}
I.e. store the contracts in an object indexed by the contract_id.