html5 mustache get the index of article and compare the value - javascript

I have a mustache file, and I am iterating over the array:
var data = {
sales: [
{
name: "Jim Frost",
region: "USA East",
phone: "212-555-1212",
email: "jfrost#acme-travel.com"
},
{
name: "Jan Smith",
region: "USA West",
phone: "310-555-1212",
},
{
name: "Fred Wesley",
phone: "608-555-1212",
email: "fwesley#acme-travel.com"
},
{
name: "Lisa Moore",
region: "USA South",
phone: "315-555-1212",
email: "lmoore#acme-travel.com"
},
{
name: "Jim Dio",
phone: "+ 44 022-555-1212",
email: "jdio#acme-travel.com"
},
{
name: "Charles Watts",
region: "Spain",
email: "cwatts#acme-travel.com"
},
{
name: "Bert Cooper",
region: "Italy",
email: "bcooper#acme-travel.com"
}
]
};
here is the markup:
<div>
<section>
{{#data.sales}}
<article class="items">
<div class="region">{{{region}}}</div>
</article>
{{/data.sales}}
</section>
</div>
I want to add some special style (like, bold font, color etc) ONLY if the region is USA East.
how can i detect inside this inherent loop in the article element if {{{region}} has a specific value? Given that the comparison will be made against a value that i get from backend, say {{myValue}}, which is manually set to USA East in the backend.

You can add a function in the data which will return the correct class depending on the region value. Something like
data['regionClass'] = function(){
if ( this['region'] == 'USA East' ) {
return "strong green";
}else{
return "";
}
}
And then in the Mustache you can do: <div class="region {{regionClass}}">{{{region}}}</div>

Related

Trying to map elements from object of nested subarrays to a cell in an HTML table, unsure how to reach deepest sub arrays

I have a React/TypeScript component I'm building that features an HTML table for contact details.
I'm trying to map the API response to cells in the table and dynamically populate rows for each contact and their details. There are two arrays for phone numbers and addresses that are nested deep within the object and I can't figure out how to iterate over them along with the rest of the data all in one go.
I initially tried nested for loops but I hit a wall when I got to those two elements because of their position in the data object.
I then tried to use .map() in the middle of the for loops, but I hit a TypeScript error stating the element I'm trying to map over could possibly be null.
I thought about iterating over phone number and address arrays separately and then inserting them into the appropriate cells per contact but I can't figure out how to do when I'm using separate for loops to populate the other cells.
Expected Output:
Name | Member | Telephone | Email | Addresses
Ben B| Friend | 610-535-1234 | ben#gmail.com | 123 Fiction Drive,Denver
215-674-6789 234 Dreary Ln,Seattle
Alice | Family| 267-333-1234 | ally#aim.com | 437 Chance St, Pitts.
I made a CodeSandbox and dropped the current component and example data structure below. For the CodeSandbox it currently loads but as soon as you uncomment these lines you'll see the error
<td>{contacts.contactGroups[i].contacts[j].phoneNumbers}</td>
<td>{contacts.contactGroups[i].contacts[j].addresses}</td>
Current Component
import React from "react";
import { Contacts } from "./contact-types";
type Props = {
contacts: Contacts;
};
export const ContactsGrid = (props: Props) => {
const { contacts } = props;
const rows = [];
for (let i = 0; i < contacts.contactGroups.length; i++) {
rows.push(
<tr>
<td>{contacts.contactGroups[i].contactGroup}</td>
</tr>
);
for (let j = 0; j < contacts.contactGroups[i].contacts.length; j++) {
rows.push(
<tr>
<td>{contacts.contactGroups[i].contacts[j].fullName}</td>
<td>{contacts.contactGroups[i].contacts[j].member}</td>
{/* <td>{contacts.contactGroups[i].contacts[j].phoneNumbers}</td> */}
<td>{contacts.contactGroups[i].contacts[j].email}</td>
{/* <td>{contacts.contactGroups[i].contacts[j].addresses}</td> */}
</tr>
);
}
}
return (
<table>
<thead>
<tr>
<td>Name</td>
<td>Member Type</td>
<td>Telephone</td>
<td>Email</td>
<td>Address</td>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
};
Current Data Structure
export default {
count: 1,
contactGroups: [
{
contactGroup: "Family",
count: 1,
contacts: [
{
member: "Uncle",
fullName: "BENJAMIN BILLIARDS",
lastName: "BILLIARDS",
firstName: "BENJAMIN",
email: "shark#billiards.com",
phoneNumbers: [
{
telephoneNumber: "123-456-7899",
type: "mobile"
},
{
telephoneNumber: "610-555-7625",
type: "work"
}
],
addresses: [
{
addressLine1: "123 FAMILY ST",
addressLine2: "APT 1208",
city: "ATLANTA",
state: "GEORGIA",
zipCode: "12345"
},
{
addressLine1: "456 WORKING BLVD",
addressLine2: "",
city: "ATLANTA",
state: "GEORGIA",
zipCode: "12345"
}
]
}
]
},
{
contactGroup: "Friends",
count: 1,
contacts: [
{
member: "School Friend",
fullName: "HANS ZIMMER",
lastName: "ZIMMER",
firstName: "HANS",
email: "hans#pirates.com",
phoneNumbers: [
{
telephoneNumber: "267-455-1234",
type: "mobile"
}
],
addresses: [
{
addressLine1: "789 FRIEND ST",
addressLine2: "",
city: "SAN DIEGO",
state: "CALIFORNIA",
zipCode: "67890"
},
{
addressLine1: "234 CANARY ST",
addressLine2: "",
city: "SEATTLE",
state: "WASHINGTON",
zipCode: "67890"
}
]
}
]
}
]
};
Use a nested map:
const rows = contacts.contactGroups.map(group => <tr>
<td>{group.contactGroup}</td>
<td>
<table>
{group.contacts.map(contact => <tr>
<td>{contact.fullName}
</tr>}
</table>
</td>
</tr>;

Fetch specific informations from complex json object

I want to fetch year, category, id and share of a particular nobel prize winner from the json data, by giving firstname and surname as raw input. I am able to get id and share easily from the below code but how can I get year and category.
Here is my code:
var json = {"prizes":
[{"year":"2018",
"category":"physics",
"overallMotivation":"\u201cfor groundbreaking inventions in the field of laser physics\u201d",
"laureates":[
{"id":"960",
"firstname":"Arthur",
"surname":"Ashkin",
"motivation":"\"for the optical tweezers and their application to biological systems\"",
"share":"2"},
{"id":"961",
"firstname":"G\u00e9rard",
"surname":"Mourou",
"motivation":"\"for their method of generating high-intensity, ultra-short optical pulses\"",
"share":"4"},
{"id":"962",
"firstname":"Donna",
"surname":"Strickland",
"motivation":"\"for their method of generating high-intensity, ultra-short optical pulses\"",
"share":"4"}
]}
]};
var winner = json.prizes.reduce((acc, winners) =>
(acc.push(...winners.laureates), acc), []).find(i => i.id === '960')
console.log(winner);
For the sake of completeness, I have added miultiple prizes in my test input. I hope that is allright. Try this code.
const input = {
prizes: [
{
year: "2018",
category: "physics",
overallMotivation:
"\u201cfor groundbreaking inventions in the field of laser physics\u201d",
laureates: [
{
id: "960",
firstname: "Arthur",
surname: "Ashkin",
motivation:
'"for the optical tweezers and their application to biological systems"',
share: "2"
},
{
id: "961",
firstname: "G\u00e9rard",
surname: "Mourou",
motivation:
'"for their method of generating high-intensity, ultra-short optical pulses"',
share: "4"
},
{
id: "962",
firstname: "Donna",
surname: "Strickland",
motivation:
'"for their method of generating high-intensity, ultra-short optical pulses"',
share: "4"
}
]
},
{
year: "2019",
category: "chemistry",
overallMotivation:
"\u201cfor groundbreaking inventions in the field of laser physics\u201d",
laureates: [
{
id: "960",
firstname: "Arthur",
surname: "Ashkin",
motivation:
'"for the optical tweezers and their application to biological systems"',
share: "2"
},
{
id: "961",
firstname: "G\u00e9rard",
surname: "Mourou",
motivation:
'"for their method of generating high-intensity, ultra-short optical pulses"',
share: "4"
},
{
id: "123",
firstname: "Donna",
surname: "Strickland",
motivation:
'"for their method of generating high-intensity, ultra-short optical pulses"',
share: "6"
}
]
}
]
};
function find(firstName, surname) {
return input.prizes.map(prize => {
const filteredLaureate = prize.laureates.filter(
laureate =>
laureate.firstname === firstName && laureate.surname === surname
);
return filteredLaureate.map(i => {
return {
id: i.id,
share: i.share,
category: prize.category,
year: prize.year
};
});
}).flat();
}
const response = find("Donna", "Strickland");
console.log(response);
Try this code.
let winners = [];
json.prizes.forEach(e => {
let data = { year: e.year, category: e.category };
const laureates = e.laureates.find( item => {
return item.firstname === 'Arthur' && item.surname === 'Ashkin';
});
data = { ...data, ...laureates };
winners.push(data);
});
console.log(winners);
The code above will give output like this:
[{
category: "physics",
firstname: "Arthur",
id: "960",
motivation: "\"for the optical tweezers and their application to biological systems\"",
share: "2",
surname: "Ashkin",
year: "2018"
}]
Remember, the code above will not perform very good when the dataset is large.

Loop through a dynamic array in Vue.js and then add each object component to divs in a separate component based on a corresponding value?

I am trying to make a Vue app that lists company offices based on regions. I have a main home view, an offices components, and an office item component. I am using v-for in the offices component to loop through the office items and display them. That works to list them all out. However, I need to sort the office items into separate divs based on the value of "Region". There are 5 regions. I cannot figure out how to loop through them based on that single value.
I know how to import components to one another, but I am trying to loop through all of the office items within the offices component. My guess is to do a loop within a loop, but do I need another component that I'm missing?
office item component:
<div class="office" :class="office.Region">
<p>{{office.Name}}</p>
<p>{{office.Address}}</p>
<p>{{office.Country}}</p>
<p>{{office.Region}}</p>
<p>{{office.Email}}</p>
<p>{{office.Phone}}</p>
</div>
offices component:
<div>
<div v-for="office in offices" :key="office.name">
<div class="office-container global" v-if="office.Region === 'Global'">
<ul>
<li><OfficeItem v-bind:office="office"/></li>
</ul>
</div>
<div class="office-container north" v-if="office.Region === 'North America'">
<ul>
<li><OfficeItem v-bind:office="office"/></li>
</ul>
</div>
<div class="office-container europe" v-if="office.Region === 'Europe, Middle East and Africa'">
<ul>
<li><OfficeItem v-bind:office="office"/></li>
</ul>
</div>
<div class="office-container asia" v-if="office.Region === 'Asia Pacific'">
<ul>
<li><OfficeItem v-bind:office="office"/></li>
</ul>
</div>
<div class="office-container latin" v-if="office.Region === 'Latin America'">
<ul>
<li><OfficeItem v-bind:office="office"/></li>
</ul>
</div>
</div>
</div>
a hardcoded array of objects:
offices: [
{
Name: "Corporate Headquarters",
Address: "Suite 500, 698 West 10000 South, South Jordan, Utah 84095",
Country: "USA",
Region: "Global",
Email: "contact#ivanti.com",
Phone: "+1-888-253-6201"
},
{
Name: "EMEA Headquarters",
Address: "First Floor Europa House, Harcourt Street Dublin 2, D02 WR20",
Country: "Ireland",
Region: "Europe, Middle East and Africa",
Email: "contact#ivanti.me",
Phone: "+ 353 1 411 7100"
},
{
Name: "India",
Address: "Bagmane Tech Park, Unit No. 4A, Level 2 , Bangalore",
Country: "India",
Region: "Asia Pacific",
Email: "contact#ivanti.com",
Phone: ""
},
{
Name: "Brazil",
Address: "Borges de Figueiredo, 303 - 4th floor, Bairro Mooca, São Paulo, SP 03110-010",
Country: "Brazil",
Region: "Latin America",
Email: "contact-brazil#ivanti.com",
Phone: "+55 11 9 8136 0343"
},
{
Name: "United States (Seattle)",
Address: "1011 Western Ave SW #700, Seattle, WA 98104",
Country: "United States",
Region: "North America",
Email: "contact#ivanti.com",
Phone: "+1-206-274-4280"
}
]
I want there to be only 5 office-container divs with the list of corresponding offices in each one. however, I get multiple office-container (i.e. two north America divs) and multiple empty divs inside of those
[...new Set(this.offices.map(o => o.Region))] gives you the list of all your regions.
You can loop through this list and and display offices having that region, using a filtering method:
officesOfRegion(region) {
return this.offices.filter(o => o.Region === region)
},
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#hook',
template: '#appTemplate',
data: ({
offices: [{
Name: "Corporate Headquarters",
Address: "Suite 500, 698 West 10000 South, South Jordan, Utah 84095",
Country: "USA",
Region: "North America",
Email: "contact#ivanti.com",
Phone: "+1-888-253-6201"
},
{
Name: "EMEA Headquarters",
Address: "First Floor Europa House, Harcourt Street Dublin 2, D02 WR20",
Country: "Ireland",
Region: "Europe, Middle East and Africa",
Email: "contact#ivanti.me",
Phone: "+ 353 1 411 7100"
},
{
Name: "India",
Address: "Bagmane Tech Park, Unit No. 4A, Level 2 , Bangalore",
Country: "India",
Region: "Asia Pacific",
Email: "contact#ivanti.com",
Phone: ""
},
{
Name: "Brazil",
Address: "Borges de Figueiredo, 303 - 4th floor, Bairro Mooca, São Paulo, SP 03110-010",
Country: "Brazil",
Region: "Latin America",
Email: "contact-brazil#ivanti.com",
Phone: "+55 11 9 8136 0343"
},
{
Name: "United States (Seattle)",
Address: "1011 Western Ave SW #700, Seattle, WA 98104",
Country: "United States",
Region: "North America",
Email: "contact#ivanti.com",
Phone: "+1-206-274-4280"
}
]
}),
computed: {
regions() {
return [...new Set(this.offices.map(o => o.Region))]
}
},
methods: {
officesOfRegion(region) {
return this.offices.filter(o => o.Region === region)
},
displayJson(o) {
return JSON.stringify(o, null, 2);
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/template" id="appTemplate">
<div id="app">
<div class="region" v-for="region in regions" :key="region">
<hr>
<h3 v-text="region"></h3>
<ul>
<li v-for="(office, i) in officesOfRegion(region)" :key="i">
<pre v-html="displayJson(office)"></pre>
</li>
</ul>
</div>
</div>
</script>
<div id="hook"></div>
I didn't look at your markup, as it's irrelevant. You can use any markup you want once the data is properly sorted.
Here it is with your markup:
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#hook',
template: '#appTemplate',
data: ({
offices: [{
Name: "Corporate Headquarters",
Address: "Suite 500, 698 West 10000 South, South Jordan, Utah 84095",
Country: "USA",
Region: "North America",
Email: "contact#ivanti.com",
Phone: "+1-888-253-6201"
},
{
Name: "EMEA Headquarters",
Address: "First Floor Europa House, Harcourt Street Dublin 2, D02 WR20",
Country: "Ireland",
Region: "Europe, Middle East and Africa",
Email: "contact#ivanti.me",
Phone: "+ 353 1 411 7100"
},
{
Name: "India",
Address: "Bagmane Tech Park, Unit No. 4A, Level 2 , Bangalore",
Country: "India",
Region: "Asia Pacific",
Email: "contact#ivanti.com",
Phone: ""
},
{
Name: "Brazil",
Address: "Borges de Figueiredo, 303 - 4th floor, Bairro Mooca, São Paulo, SP 03110-010",
Country: "Brazil",
Region: "Latin America",
Email: "contact-brazil#ivanti.com",
Phone: "+55 11 9 8136 0343"
},
{
Name: "United States (Seattle)",
Address: "1011 Western Ave SW #700, Seattle, WA 98104",
Country: "United States",
Region: "North America",
Email: "contact#ivanti.com",
Phone: "+1-206-274-4280"
}
]
}),
computed: {
regions() {
return [...new Set(this.offices.map(o => o.Region))]
}
},
methods: {
officesOfRegion(region) {
return this.offices.filter(o => o.Region === region)
},
propsOf(o) {
return Object.keys(o);
}
},
})
.office p {
display: flex;
}
.office p strong {
width: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/template" id="appTemplate">
<div id="app">
<div class="region" v-for="region in regions" :key="region">
<hr>
<hr>
<h3>{{region}}</h3>
<div v-for="(office, i) in officesOfRegion(region)" :key="i" class="office">
<hr>
<p v-for="prop in propsOf(office)"><strong>{{prop}}:</strong> {{office[prop]}}</p>
</div>
</div>
</div>
</script>
<div id="hook"></div>
It looks like the only thing changing in your template is the classes surrounding to the OfficeItem Component
To keep your code less DRY try applying that conditional logig within the OfficeItem Component like below.
// OfficeItem.vue
<template>
<li :class="['office-container', getRegionClass(office.Region)]">{{office.Region}}</li>
</template>
<script>
const regional_classes = {
A: 'class_a and-another-A-class',
B: 'class_b and-another-B-class',
C: 'class_c and-another-C-class',
D: 'class_d and-another-D-class',
Z: 'class_z and-another-Z-class'
}
export default {
name: "OfficeItem",
props: {
office: Object
},
methods: {
getRegionClass(region) {
return regional_classes[region] || ''
}
}
};
</script>
Alternatively, have a switch statement that takes the Region and returns a String of whatever case is met within the switch.
in this Scenario though i feel a regional_class Object is more readable/maintainable.
And in your Offices component, just pass the office object to your Officeitem like below
// Offices.vue
<template>
<div>
<ul :key="`${regionName}_${index}`" v-for="(region, regionName, index) in officesByRegion">
<h1>Region {{regionName}}</h1>
<OfficeItem v-for="office in region" :key="office.Region" :office="office"/>
</ul>
</div>
</template>
<script>
export default {
name: "Offices",
data() {
return {
offices: [
{ Region: "A" },
{ Region: "B" },
{ Region: "C" },
{ Region: "D" },
{ Region: "Z" },
{ Region: "A" },
{ Region: "B" },
{ Region: "A" },
{ Region: "B" },
{ Region: "A" },
{ Region: "Z" },
{ Region: "C" },
{ Region: "D" },
{ Region: "E" }
]
};
},
computed: {
officesByRegion() {
const obj = {};
this.offices.forEach(o => {
if (o.Region in obj) obj[o.Region].push(o);
else obj[o.Region] = [o];
});
return obj;
}
}
};
</script>
I Hope this helps. Or at least shine's some light on dynamic css class application. :-)

Javascript error trying to perform a search filter using Angular JS

In my app Im trying perform a search filter on names by partial match from the beginning. Heres an example of what Im trying to do:
Lets say I have a list of names:
Ben
James
Adam
Judy
Andy
and enter the text "a" in my search field, it would return
Adam
Andy
if I further enter "an" in my search field, it would return
Andy
In my app.js, I have the code:
var myApp = angular.module("myApp", []);
myApp.controller("myController", function ($scope) {
var employees = [
{ name: "Ben", gender: "Male", salary: 55000, city: "London" },
{ name: "Jane", gender: "Female", salary: 62000, city: "Albany" },
{ name: "Rick", gender: "Male", salary: 65000, city: "Los Angeles" },
{ name: "Pam", gender: "Female", salary: 60000, city: "Paris" },
{ name: "Josh", gender: "Male", salary: 68000, city: "Brussels" },
];
$scope.employees = employees;
$scope.filtered = function (item) {
if ($scope.searchName == undefined) {
return true;
} else {
if (item.name.toLowerCase().startsWith($scope.searchName.toLowerCase()) != -1) {
return true;
}
}
return false;
}
});
And in my html page, I have the following line which displays the list of employees:
<tr ng-repeat="employee in employees | filter: filtered">
And the following line which the user inputs the search text:
<input type="text" placeholder="Search Name" ng-model="searchName.name"> <br><br>
However, when I attempt to test this, I get the error:
Error: $scope.searchName.toLowerCase is not a function. (In '$scope.searchName.toLowerCase()', '$scope.searchName.toLowerCase' is undefined)
As your ng-model is set to searchName.name, $scope.searchNameis an object, therefore it has no .toLowerCase() function.
You need to adjust your if-case like this:
if (item.name.toLowerCase().startsWith($scope.searchName.name.toLowerCase()) !== -1) {}
Furthermore, it is advisable to use identity operators instead of equality operators, unless strict identity is explicitly not needed.
The ng-model is set to searchName.name, so you may need to call on $scope.searchName.name.toLowerCase() instead of $scope.searchName.toLowerCase()

Search by name and family but display account number in jquery autocomplete

I'm working on a piece of code which has used jquery ui autocomplete component in order filter searched items. I have to configure it in order to be available to search based on multi ple factors, here is my sample search array:
var availableTags = [{
name: "Smith",
family: "Johnson",
account_number: "10032",
nick_name: "Friend account",
}, {
name: "Susan",
family: "Rice",
account_number: "343243",
nick_name: "Mom Account",
}, {
name: "Joe",
family: "Austin",
account_number: "3434",
nick_name: "Partner Account",
}, {
}];
the auto complete should display name, family, account number and nick_name when displaying the suggestion box. but when each item is selected only the account_number must be inserted into the text box. user must also be able to search through name, family, account number and nick name all of them. How can i achieve this target?
You need to:
Revise the data array to contain the value parameter (this allows autocomplete to fill the input upon focus/select)
Write a custom source function that filters the data based on what user has typed
Write a custom _renderItem function that displays the data formatted to your liking
So you have:
var userData = [{
name: "Smith",
family: "Johnson",
value: "10032",
nick_name: "Friend account"
}, {
name: "Susan",
family: "Rice",
value: "343243",
nick_name: "Mom Account"
}, {
name: "Joe",
family: "Austin",
value: "3434",
nick_name: "Partner Account"
}];
$("#autocomplete").autocomplete({
source: function (request, response) {
var search = $.trim(request.term.toLowerCase());
var array = $.grep(userData, function (val) {
return
val.name.toLowerCase().indexOf(search) >= 0 ||
val.family.toLowerCase().indexOf(search) >= 0 ||
val.value.toLowerCase().indexOf(search) >= 0 ||
val.nick_name.toLowerCase().indexOf(search) >= 0;
});
response(array);
}
})
.data("ui-autocomplete")._renderItem = function (ul, item) {
var $a = $("<a></a>").text(item.name + " " + item.family);
$("<br />").appendTo($a);
$("<small></small>").text(item.nick_name).appendTo($a);
$("<br />").appendTo($a);
$("<small></small>").text(item.value).appendTo($a);
return $("<li></li>").append($a).appendTo(ul);
};
Demo here

Categories