Apply CSS style within .js var - javascript

I'm currently building a game which relies on a random number to show a particular string from an array within a .js file. What I want to do is effectively apply a span element to certain text within the array - eg. make positive outcomes green & negative red, however I can't figure out how to do this outside of html. Eg:
var data = [
{"label": "Question 1", "value": 1, "question": "Awareness: High, Consistency: Low."},
{"label": "Question 2", "value": 1, "question": "Consistency: High, Awareness: Low."},
]
A separate set of code, after selecting a random number, returns data[i].label. How would I set a span within js so that the "High" strings return in green, and "Low" return in red?
Cheers.

You have not specified much, but I went and did what I could.
First, your span
<span id="result">here we go</span>
The CSS file, minimalist, I did not include Bootstrap for this.
span {color:green}
.red{color:red}
The JS, the is no event, or event handler.
var data = [
{"label": "Question 1", "value": 1, "question": "Awareness: High, Consistency: Low."},
{"label": "Question 2", "value": 1, "question": "Consistency: High, Awareness: Low."}
]
The array, I have removed the trailing comma after the second object.
let outputs = data[0].question; This will hold the result from the array, data[0] targets the first object, you can also get map over the properties, but you have not entirely specified the scope and desired functionality.
var output = document.getElementById('result'); // the span element
output.innerText = outputs; // i prefer innerText to innerHTML
if (outputs.includes("Consistency: Low")) { // new string method includes, you can pass the whole ("Consistency: Low")in with no worries.
output.classList.toggle("red"); // if condidtion is true, toggle the red class
}
Codepen
https://codepen.io/damPop/pen/ZwvvGV?editors=0010

Uses classes
var data = [
{label: "Question 1", value: 1, awareness: 'High', consistency: 'Low' },
{label: "Question 2", value: 1, awareness: 'Low', consistency: 'High' },
{label: "Question 3", value: 1, awareness: 'Low', consistency: 'Low' },
{label: "Question 4", value: 1, awareness: 'High', consistency: 'High' }
];
const questionElement = document.getElementById('question');
const awarenessElement = document.getElementById('awareness');
const consistencyElement = document.getElementById('consistency');
document.getElementById('select').addEventListener('click', select);
select();
function select() {
const question = data[Math.floor(Math.random() * 4)];
questionElement.innerText = question.label;
awarenessElement.innerText = `Awareness: ${question.awareness}`;
awarenessElement.className = question.awareness;
consistencyElement.innerText = `Consistency: ${question.consistency}`;
consistencyElement.className = question.consistency;
}
.High {
background-color: green;
}
.Low {
background-color: red;
}
<span id="question"></span>
<div id="awareness"></div>
<div id="consistency"></div>
<button id="select">Select</button>

Related

How to bind a conditional panelCount to a question type of 'paneldynamic' in SurveyJs

In my survey in SurveyJs, I'm attempting to implement a question with the type 'paneldynamic', which has the following structure:
{
type: "paneldynamic",
name: "9.2",
visible: false,
visibleIf: "{9.a.n} > 0",
title: "When did you give birth?",
enableIf: "{9.a.n} > 0",
requiredIf: "{9.a.n} > 0",
templateTitle: "Date of birth:",
templateElements: [
{
type: "text",
name: "9.2.1",
inputType: "date",
maxValueExpression: "today()",
titleLocation: 'hidden'
},
],
panelCount: "{9.a.n}"
}
where Question 9a is a number entry. I want the number of panels on this question to vary depending on the answer given to Q9a, but with this question structure the survey does not appear to bind the value of 9a to the actual panel count.
I've been unable to find anything in the documentation, and have tried variants such as "bindings: { "panelCount": "9.a.n" } which also don't seem to work.
How can I correctly set the variable panelCount property?
You may wish to create a custom function and calculate the number of panels based on another question answer. For example: https://plnkr.co/edit/iYNMt7JRPOh2dTOy.
Survey.Serializer.addProperty("paneldynamic", {
name: "panelCountExpression:expression",
onExecuteExpression: (obj, res) => {
if(res !== undefined) {
obj.panelCount = res;
}
}
});
var json = {
"pages": [
{
"name": "page1",
"elements": [
{
"type": "text",
"name": "question1",
"inputType":"number",
"min": 0,
"max": 5,
"defaultValue":"2"
},
{
"type": "paneldynamic",
"name": "question2",
"panelCountExpression":"{question1}",
"templateElements": [
{
"type": "text",
"name": "question3"
}]
}
]
}
],
};
I also recommend that you review the following:
Blogpost (the example was taken from it): SurveyJS Library — Calculate Properties and Hide Elements With Expressions and Functions.
Documentation: Conditional Logic and Dynamic Texts.
Thanks

Issues with converting an array of objects into a different format

I'm struggling to convert an array that I've been given into a useful format:
Given
{
"myValues": [{
"0": {
"id": "5ed32599-3c4d-49ad-8a1a-79bbc39a3e02",
"value": "my Value",
"value2": "my Value 2"
},
"1": {
"id": "5ed32599-3c4d-49ad-8a1a-79bbc39a3e02",
"value": "my Value",
"value2": "my Value 2"
},
"id": "5ed32599-3c4d-49ad-8a1a-79bbc39a3e02"
}]
}
I am trying to remove the numbers in front of the objects and just the ID that sits outside of the two inner objects.
Therefore I would then be given:
{
"myValues": [{
"id": "5ed32599-3c4d-49ad-8a1a-79bbc39a3e02",
"value": "my Value",
"value2": "my Value 2"
},
{
"id": "5ed32599-3c4d-49ad-8a1a-79bbc39a3e02",
"value": "my Value",
"value2": "my Value 2"
}
]
}
I have been trying to use different ways of mapping Objects into new formats but I'm really struggling particularly to get rid of the IDs
You can use Object.values().
I added a filter() to remove the single id...not sure if that is a typo in example data or not...or if you also want it included in results
const myValues=[
{
"0":{
"id":"5ed32599-3c4d-49ad-8a1a-79bbc39a3e02",
value : 'my Value',
value2 : 'my Value 2'
},
"1":{
"id":"5ed32599-3c4d-49ad-8a1a-79bbc39a3e02",
value : 'my Value',
value2 : 'my Value 2'
},
"id":"5ed32599-3c4d-49ad-8a1a-79bbc39a3e02"
}
]
const arrValues = Object.values(myValues[0]).filter(el => typeof el === 'object')
console.log(arrValues)

Is there a way to de-dupe a javascript array and combine values of the data?

I'm using an ajax request to grab some XML data which I then need to push into a chart in fusioncharts.
The XML data is formatted as [time taken], [work done], [which team done for], [who did it] (see below).
I'm iterating over the XML and then building the array using the code below:
//Time Recorded
if (columnidchecker == 7781) {
timearray.push($j(this).find('displayData').text());
temp1 = $j(this).find('displayData').text();
}
//Type of Activity
if (columnidchecker == 7782) {
activityarray.push($j(this).find('displayData').text());
temp2 = $j(this).find('displayData').text();
}
//Team Done For
if (columnidchecker == 7783) {
subjectarray.push($j(this).find('displayData').text());
temp3 = $j(this).find('displayData').text();
}
//Name
if (columnidchecker == 7777) {
internalclientarray.push($j(this).find('displayData').text());
temp4 = $j(this).find('userDisplayName').text();
}
});
//PUSH INTO A NEW ARRAY WHICH CAN THEN BE SORTED AND DE-DUPED WITH TIME COMBINED AGAINST ACTIVITY / TEAM.
objectarray.push([temp1, temp2, temp3, temp4]);
This builds an array of entries from the XML which basically outputs to something which looks like this:
0: (4) ["1.50", "Ad-hoc queries or calls", "Team 1", "James"]
1: (4) ["2.50", "Ad-hoc queries or calls", "Team 1", "James"]
2: (4) ["1.00", "Advice", "Team 2", "James"]
3: (4) ["3.50", "Meeting (External 3rd Party)", "Team 1", "James"]
4: (4) ["1.20", "Administration", Team 2", "James"]
5: (4) ["5.50", "Advice", "Team 1", "John"]
I'm trying to build a chart in fusioncharts which needs the format as shown below (ignore foot stuffs - it's taken straight from the fusioncharts help pages!).
{
"chart": {
"theme": "fusion",
"caption": "Revenue split by product category",
"subCaption": "For current year",
"xAxisname": "Quarter",
"yAxisName": "Revenues (In USD)",
"showSum": "1",
"numberPrefix": "$"
},
"categories": [
{
"category": [
{
"label": "Q1"
},
{
"label": "Q2"
},
{
"label": "Q3"
},
{
"label": "Q4"
}
]
}
],
"dataset": [
{
"seriesname": "Food Products",
"data": [
{
"value": "11000"
},
{
"value": "15000"
},
{
"value": "13500"
},
{
"value": "15000"
}
]
},
{
"seriesname": "Non-Food Products",
"data": [
{
"value": "11400"
},
{
"value": "14800"
},
{
"value": "8300"
},
{
"value": "11800"
}
]
}
]
}
The problem i'm having is that I cannot work out how to take the array of data with times, activity, team, name and push them into categories.
I think the first step is to create a new array of names which can be pushed into the "Category" data field in fusioncharts.
I then need a way in which to take the times being recorded against each activity and for each team and make sure it's assigned to the right person within the stacked bar chart and combine the amount of time spent. (i.e. "James" spent a total of 4 hours doing "Ad Hoc Queries and Calls" for Team 1 but this is split across two time entries so I need a way in which to combine them into one.)
Any help on this would be massively appreciated.
I can de-dupe the names to create a new array by using the following code:
namesarray.push(temp4);
uniq = [...new Set(namesarray)];
but after that it starts getting pretty complicated.
Maybe this can help you along the way. It's probably not exactly in the form you want it, but it demonstrates how you could break the problem down into smaller parts.
Pseudo-code:
get the unique names.
get the unique "task" names (for lack of a
better word)
for each unique person name:
3.1. get the data rows for that person
3.2 for each of all unique tasks names:
find the person data rows matching the task name
sum the duration of those data rows
const testData = [
[
"1.50",
"Ad-hoc queries or calls",
"Team 1",
"James"
],
[
"2.50",
"Ad-hoc queries or calls",
"Team 1",
"James"
],
[
"1.00",
"Advice",
"Team 2",
"James"
],
[
"3.50",
"Meeting (External 3rd Party)",
"Team 1",
"James"
],
[
"1.20",
"Administration",
"Team 2",
"James"
],
[
"5.50",
"Advice",
"Team 1",
"John"
]
];
const columnIndexByName = {
TASK_DURATION: 0,
TASK_NAME: 1,
FOR_WHICH_TEAM: 2,
PERSON_DOING_TASK: 3
};
const sum = (acc, next) => acc + next;
const uniqueNames = [...new Set(testData.map(row => row[columnIndexByName.PERSON_DOING_TASK])) ];
const uniqueTaskNames = [...new Set(testData.map(row => row[columnIndexByName.TASK_NAME])) ];
let result = {};
uniqueNames.forEach(personName => {
const personDataRows = testData.filter(row => row[columnIndexByName.PERSON_DOING_TASK] === personName);
let taskDurationsByTaskName = {};
uniqueTaskNames.forEach(taskName => {
const taskRows = personDataRows.filter(row => row[columnIndexByName.TASK_NAME] === taskName);
const taskDurations = taskRows.map(row => Number.parseFloat( row[columnIndexByName.TASK_DURATION] ));
const taskTotalDuration = taskDurations.reduce(sum, 0);
taskDurationsByTaskName[taskName] = taskTotalDuration;
})
result[personName] = taskDurationsByTaskName;
})
const renderData = data => document.querySelector("#output").innerHTML = JSON.stringify(data, null, 2);
renderData(result);
<pre id="output"></pre>

Iterating over a complex object using ngRepeat to generate inputs

Using AngularJS, I need to generate settings for a web app using ngRepeat to generate each input. An example of the settings object is as follows:
// in HostController where var host = this
host.settings = {
section: {
property: {name: "Option title", value: 1024},
property: {name: "Option title", value: false},
property: {name: "Option title", value: "#000000"},
},
section: {
dropdownPproperty: {name: "Option title", value: "a", values: ["a", "b", "c", "d"]},
dropdownProperty: {name: "Option title", value: "g", values: ["e", "f", "g", "h"]},
},
section: {
group: {
property: {name: "Option title", value: 0.9},
property: {name: "Option title", value: 1.2},
},
group: {
property: {name: "Option title", value: 1},
group: {
property: {name: "Option title", value: 0.5},
},
},
},
section: {
property: {name: "Option title", value: false},
}
};
Note that in this example the parts of the object have been named what they're supposed to be, eg section/property/group - therefore these names can't be used for identification.
The settings object is divided into different sections. All sections are on the first level of the object. Each section should have its own <div> at the very least.
Each property should have its own <input> - the type of input should depend on the value of the property (eg number/boolean(checkbox)/text).
If the property should be a dropdown (ie has a values array) then it should be a dropdown.
I'd like to ignore groups - the properties of each group should iterated over as if the group wasn't present at all.
I've used ngRepeat a good few times before but only ever to iterate over something simple - a task like this just seems to complex to me, I don't even know if it's possible. How would I go about this?
Thank you very much for any advice and/or help!
you can use a filter to exclude the groups like this
https://docs.angularjs.org/api/ng/filter/filter

Lodash: filter a nested object by multiple properties

Consider the following example:
var products = {
"Products": [{
"Title": "A",
"Categories": [{
"Name": "Type",
"Properties": ["Type 1", "Type 2", "Type 3"]
}, {
"Name": "Market",
"Properties": ["Market 1", "Market 2", "Market 3", "Market 4"]
}, {
"Name": "Technology",
"Properties": ["Tech 1", "Tech 2"]
}]
}, {
"Title": "B",
"Categories": [{
"Name": "Type",
"Properties": ["Type 1", "Type 3"]
}, {
"Name": "Market",
"Properties": "Market 1"
}, {
"Name": "Technology",
"Properties": ["Tech 1", "Tech 3"]
}]
}, {
"Title": "C",
"Categories": [{
"Name": "Type",
"Properties": ["Type 1", "Type 2", "Type 3"]
}, {
"Name": "Market",
"Properties": ["Market 2", "Market 3"]
}, {
"Name": "Technology",
"Properties": ["Tech 2", "Tech 3"]
}]
}]
}
I'm trying to filter products by their properties so consider I'm using an array to keep track of my selected filters:
var filters = ['Type 3', 'Tech 1'];
With these filters I would like to return product A and product B.
I currently have this:
var flattenedArray = _.chain(products).map('Categories').flatten().value();
var result= _.some(flattenedArray , ['Properties', 'Tech 1']);
But I'm stuck on how to combine the properties for a combined search.
Use _.filter() to iterate the products. For each product combine the list of properties using _.flatMap(), and use _.intersection() and _.size() to find the amount of filters that exist in the categories. Compare that to the original number of filters, and return comparison's response.
var products = {"Products":[{"Title":"A","Categories":[{"Name":"Type","Properties":["Type 1","Type 2","Type 3"]},{"Name":"Market","Properties":["Market 1","Market 2","Market 3","Market 4"]},{"Name":"Technology","Properties":["Tech 1","Tech 2"]}]},{"Title":"B","Categories":[{"Name":"Type","Properties":["Type 1","Type 3"]},{"Name":"Market","Properties":"Market 1"},{"Name":"Technology","Properties":["Tech 1","Tech 3"]}]},{"Title":"C","Categories":[{"Name":"Type","Properties":["Type 1","Type 2","Type 3"]},{"Name":"Market","Properties":["Market 2","Market 3"]},{"Name":"Technology","Properties":["Tech 2","Tech 3"]}]}]};
var filters = ['Type 3', 'Tech 1'];
var result = _.filter(products.Products, function(product) {
return filters.length === _(product.Categories)
.flatMap('Properties')
.intersection(filters)
.size();
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
If I understand you question correctly, this code may help:
_.filter(
products.Products,
product => _.difference(
filters,
_.chain(product.Categories).map(category => category.Properties).flatten().value()
).length === 0
);
It calculates a union of all properties for each product:
_.chain(product.Categories).map(category => category.Properties).flatten().value()
And then checks that it contains all filters array elements, using _.difference method.
Hope it helps.
another fancy way through _.conforms
var res = _.filter(
products.Products,
_.conforms({'Categories': function(categories) {
return _.chain(categories)
.flatMap('Properties') // flat arrays
.uniq() // remove dublicates
.keyBy() // transform to objects with Properties keys
.at(filters) // get objects values by filters
.compact() // remove undefineds
.size() // get size
.eq(filters.length) // compare to filters size
.value();
}
}))
This will work for a list of items where the givenProperty you want to filter on is either a string like 'doorColour' or an array of strings representing the path to the givenProperty like ['town', 'street', 'doorColour'] for a value nested on an item as town.street.doorColour.
It also can filter on more than one value so you could you just need pass in an array of substrings representing the string values you want to keep and it will retain items that have a string value which contains any substring in the substrings array.
The final parameter 'includes' ensures you retain these values if you set it to false it will exclude these values and retain the ones that do not have any of the values you specified in the substrings array
import { flatMap, path } from 'lodash/fp';
const filteredListForItemsIncludingSubstringsOnAGivenProperty = (items, givenProperty, substrings, including=true) => flatMap((item) =>
substrings.find((substring) => path(givenProperty)(item) && path(givenProperty)(item).includes(substring))
? including
? [item]
: []
: including
? []
: [item])(items);
E.g. fLFIISOAGP(contacts, ['person','name'], ['Joh','Pau',Pet']);
with items of structure {contact, business:null, personal:{name:'John'}}.
For the original question - this will also work - I would use this repeatedly on a list of items to filter with different keys to filter on more than one property.
const firstFilteredResult = filteredListForItemsIncludingSubstringsOnAGivenProperty(
products.Products,
["Categories", "0", "Properties"],
["Type 3"]);
const secondFilteredResult = filteredListForItemsIncludingSubstringsOnAGivenProperty(
firstFilteredResult,
["Categories", "2", "Properties"],
["Tech 1"]);
expect(secondFilteredResult[0]['Title']).to.equal( "A");
expect(secondFilteredResult[1]['Title']).to.equal( "B");
expect(secondFilteredResult.length).to.equal(2);

Categories

Resources