I am trying to apply a Ternary operator to some JSON Data which is held in a separate file locally. Below is the JSON:
[
{
"id": 1,
"company": "Photosnap",
"logo": "./images/photosnap.svg",
"new": true,
"featured": true,
"position": "Senior Frontend Developer",
"role": "Frontend",
"level": "Senior",
"postedAt": "1d ago",
"contract": "Full Time",
"location": "USA Only",
"languages": ["HTML", "CSS", "JavaScript"]
},
{
"id": 2,
"company": "Manage",
"logo": "./images/manage.svg",
"new": true,
"featured": true,
"position": "Fullstack Developer",
"role": "Fullstack",
"level": "Midweight",
"postedAt": "1d ago",
"contract": "Part Time",
"location": "Remote",
"languages": ["Python"],
"tools": ["React"]
},
{
"id": 3,
"company": "Account",
"logo": "./images/account.svg",
"new": true,
"featured": false,
"position": "Junior Frontend Developer",
"role": "Frontend",
"level": "Junior",
"postedAt": "2d ago",
"contract": "Part Time",
"location": "USA Only",
"languages": ["JavaScript"],
"tools": ["React"
Now the issue I have is I conditionally want to show a button dependent on whether "new" is true. The same is said to be with the Featured button.
So I have written a Ternary Operator in my Component.
import React from 'react';
import './job-card.styles.css';
const JobCard = ({company, position, postedAt, contract, location, logo, featured, newJob }) => (
<div className="container">
<div className='card'>
<div className='companyName'>
<img src={logo} alt="logo" width="100" height="100"></img>
</div>
<div className='content'>
{{newJob} ? <button className='myButton'>New!</button> : null }
{{featured} ? <button className='myDarkButton'>Featured</button> : null }
<h2>{company}</h2>
<h1>{position}</h1>
<div className='details'>
<h3>{postedAt} ·</h3>
<h3>{contract} ·</h3>
<h3>{location}</h3>
</div>
</div>
</div>
</div>
)
export default JobCard;
This is just a card component and feeds into another component which displays all the cards.
import React from 'react';
import './job-listing.styles.css';
import JobCard from '../job-card/job-card.component.jsx/job-card.component';
import { Component } from 'react';
class JobListing extends Component {
constructor() {
super();
this.state = {
jobs: []
}
};
componentDidMount() {
fetch('/data.json')
.then(response => response.json())
.then(data => this.setState({jobs: data}))
}
render() {
return (
<div>
{this.state.jobs.map(({id, ...otherJobProps}) =>(
<JobCard key={id} {...otherJobProps} />
))}
</div>
)
}
}
export default JobListing;
The output I am getting is that they are all rendering as true when some of the new or featured are false in the JSON Data. Not sure what I have missed. Any help would be appreciated.
The problem is the inner {}.
{{newJob} ? <button className='myButton'>New!</button> : null }
// ^ here
Within JSX, {} denotes a javascript expression. But once you are within an expression, {} goes back to being normal object syntax. This is throwing off your ternary because you're checking whether an object with key newJob is truthy. Simply removing the brackets would fix it:
{newJob ? <button className='myButton'>New!</button> : null }
Regarding the new issue
I prefer not to destructure props like this, but to get it working most like you already have, destructure the new reserved word into an alias. Here is a simple proof of concept:
let test = [{ new: true }, { new: false }];
test.map(({new: isNew}) => console.log(isNew))
I would prefer to keep the data structured as is. But thats just a preference. It would also avoid the reserved word issue.
let test = [{ new: true }, { new: false }];
test.map((value) => console.log(value.new))
In your case, you can simply do:
{newJob
&& (<button className='myButton'>New!</button>)
}
{featured
&& <button className='myDarkButton'>Featured</button>
}
It works because in JavaScript, true && expression always evaluates to expression,
and false && expression always evaluates to false.
Therefore, if the condition is true, the element right after && will appear in the output. If it is false, React will ignore and skip it.
And null, undefined, 0, "" are falsy values in JS.
Ternary operator is also an option if you really need two options (e.g):
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
First of all, in the json objects I see the property new: true but the JobCard component receives newJob as part of it's props, not new.
To answer your question you see the buttons rendered because the condition {newJob} is an object which evaluates to true in:
{{newJob} ? <button className='myButton'>New!</button> : null }
Why? Because {newJob} is the same as this: { newJob: newJob } which is creating an object with a property called newJob and assigning it the value of newJob that you get from the component props.
What you want to do is one of the following:
{newJob ? <button className='myButton'>New!</button> : null }
{newJob && <button className='myButton'>New!</button> }
Related
I implemented i18n with useTranslation and tried to make it easier for json path to be written.
JSON
{
"userInfo":{
"name": "Name",
"lastname": "Last Name"
},
"sideMenu:{
"home":"Home"
}
}
So now when I try to access translated text I go
t("userInfo.name", "Name")
I tried to make a function that will recursively call it self and create the object like this
object = {
userInfo: {
name: {
key:"userInfo.name",
value:"Name"
},
lastname: {
key:"userInfo.lastname",
value:"Last name"
},
},
sideMenu: {
home: {
key:"sideMenu.home",
value:"Home"
}
}
}
so now I could access like this
t(object.userInfo.name.key, object.userInfo.name.value)
I tried using entries and fromEntries function but I simply cant get hold of the logic behind it.
I made it work with a recursive function so now, no matter the number of nested objects function will return a new object with key and value items.
You can see working snippet with json file i used for the project.
let en = {
"userInfo":{
"name": "Name",
"lastname": "Last Name",
"gender": "Gender",
"age": "Age",
"location": "Location",
"address": "Address",
"city": "City",
"login": "Login",
"about": "About"
},
"comboBox": {
"loading": "Loading...",
"loadmore": "Load More"
},
"error": {
"errormsg": "Ooops, it seems you are not authenticated to see this...",
"errormsgsub": "Get back and try again",
"errorbtn": "Back to Safety",
"alertmsg": "You should login first!"
},
"sideMenu": {
"home": "Home",
"logout": "Logout",
"croatian": "Croatian",
"english": "English",
"hello": "Hello"
},
"loadingPage": {
"load": "Loading...",
"loadsub": "Please wait"
}
}
function languageRecursion(data, key){
if (typeof data === 'object') {
return Object.fromEntries(
Object.entries(data).map(([ikey, value]) => {
return [ikey, languageRecursion(value, key ? `${key}.${ikey}` : ikey)];
})
);
}
return { key: key, value: data };
}
let locale = languageRecursion(en)
console.log("path",locale.userInfo.about.key)
console.log("value", locale.userInfo.about.value)
With this working now you can call languages with i18next useTranslation hook faster and easier.
t(locale.userInfo.name.key)
I have imported the following json file:
[
{
"case_id": "1234",
"thread": [
{
"t_id": "1111",
"text": "test"
},
{
"t_id": "2222",
"text": "test"
}
]
},
{
"case_id": "5678",
"thread": [
{
"t_id": "9999",
"text": "test"
},
{
"t_id": "8888",
"text": "test"
},
{
"t_id": "777",
"text": "test"
}
]
}
]
using the following:
import cases from '../cases.json'
The whole json dataset is available in cases variable and can be used in the template with the support of v-if and v-for.
How can I create a separate dataset (thecase) that contains only threads for a given case_id? In the template I would only like to use v-for to display all threads for a given case_id.
Below is my export default section:
export default {
name: "details",
props: {
case_id: {
required: true,
type: String
}
},
data () {
return {
cases,
thecase: ### THIS IS THE PART I CANNOT FIGURE OUT ###
}
}
};
You can remove thecase from data options and use a computed property instead for thecase. Inside the computed property, we will need to use array .find() method to find the case where case_id is same as the case_id passed in the prop:
data: {
cases,
},
computed: {
thecase: function() {
return this.cases.find(c => c.case_id === (this.case_id || ''))
}
}
and then you can use v-for on thecase.thread just like you would do for a data option like:
<li v-for="item in thecase.thread" :key="item.t_id">
{{ item.text }}
</li>
You can further modify it and use v-if & v-else to show a text like No cases were found with give case id in case there is no match found.
I have a UI where initially a User has to check some checkboxes. The checkboxes have sequential IDs. The JSON Structure for it is as follows:
{
"categories": [{
"name": "Product",
"labels": [{
"id": 1,
"name": "I work on an asset (capital good).",
"checked": false
}, {
"id": 2,
"name": "I work on a consumer product.",
"checked": false
}, {
"id": 3,
"name": "I am not sure what type of product I work on.",
"checked": false
}
]
}, {
"name": "Goal",
"labels": [{
"id": 4,
"name": "I want to improve the product's reliability.",
"checked": false
}, {
"id": 5,
"name": "I need information to identify root causes.",
"checked": false
}, {
"id": 6,
"name": "I need information about the product's environment.",
"checked": false
}, {
"id": 7,
"name": "I need information about customer requirements.",
"checked": false
}, {
"id": 8,
"name": "I need quantified information.",
"checked": false
}, {
"id": 9,
"name": "I am not sure what I need.",
"checked": false
}
]
}
]
}
I render it Angular using the following Code:
component.html
<div class="row mt-lg-auto" *ngFor="let filter of filters['categories']">
<div class="col-lg-12">
<h4>
{{filter['name']}}
</h4>
<div *ngFor="let label of filter['labels']">
<div class="form-check">
<input class="form-check-input"
type="checkbox"
value="{{label['id']}}"
id="{{label['id']}}"
[(ngModel)]="label['checked']"
(change)="changeCheck(label['id'], $event)"
>
<label class="form-check-label" for="{{label['id']}}">
{{label['name']}}
</label>
</div>
</div>
</div>
</div>
component.ts
I directly import the JSON file from src/assets/ folder and save the id to a Set in order to avoid duplicate values when the user selects a checkbox.
import { Component, OnInit } from '#angular/core';
import * as FilterFunc from 'src/assets/FilterFunction.json';
const Filters: any = FilterFunc;
#Component({
selector: 'explore-step1',
templateUrl: './explore-step1.component.html',
styleUrls: ['./explore-step1.component.css']
})
export class ExploreStep1Component implements OnInit {
filters = Filters.default;
selections = new Set();
constructor() {
}
ngOnInit(): void {
}
changeCheck(id: number, event: any) {
(event.target.checked) ?
this.selections.add(id):
this.selections.delete(id);
console.log(this.selections);
}
}
I am using ngx-treeview to render a tree view for a fixed JSON file that has the following structure:
GitHub Gist of the Complete Recursive JSON
Here on the children in the most depth have the following key-value pair:
"value": {
"label_ids": [relevant ids from the first json],
"description": "some text to render"
}
else the "value" is null.
I wish to compare the Set values to the above mentioned recursive JSON's label_ids and if one or more than one values in the label_ids match with the Set then change the checked value to true
How does one accomplish this in Typescript/Angular?
I solved it by creating a Recursion Parsing Function which takes in the JSON Structure.
Within the ngOnInit() I call the service and pass it to the parseTree function
I recursively parse it and compare the values with the Set
I add additional information like a Font-Awesome class text within the value structure to render it
pass the updated JSON to the respective items variable
component.ts:
parseTree(factors: TreeviewItem[]) {
factors.forEach(factor => {
// console.log(factor);
if (!isNil(factor.value)) {
let labels: number[] = factor.value['label_ids'];
labels.forEach(label => {
if(this.selected.findIndex(l => l == label) > -1) {
factor.value['highlighted'] = true;
factor.value['class'] = 'fas fa-flag';
}
});
}
if (!isNil(factor.children)) {
this.parseTree(factor.children);
}
});
this.items = factors;
}
here selections is a Set and within ngOnInit() I set it a fixed value:
ngOnInit() {
this.selections.add(15);
this.items = this.parseTree(this.service.getDataQualityFactors());
}
Based on ngx-treeview example I use the itemTemplate in the code and add the Font-Awesome fonts next to the selections as follows:
component.html
<label class="form-check-label" *ngIf="item.value !== null && item.value['highlighted']">
<i class="{{item.value['class']}}" aria-hidden="true" title="Highlighted" [ngClass]="{'marked': item.checked}"></i>
</label>
and use the CSS classes to manipulate the color change of the font:
.fa-flag {
color: red;
}
.fa-flag.marked {
color: green;
}
StackBlitz Example Code
I am trying to list the server response , but some mistake is their in my code about accessing nested json..Following is the structure of json
Updated:
{
"child": [],
"courses": [{
"data": {
"name": "Student 1",
"date_created": 1514610451,
"total_students": 4,
"seats": "",
"start_date": false,
"categories": [{
"name": "Subject",
"slug": "Subject"
}],
"intro": {
"id": "1",
"name": "Main Admin",
"sub": ""
},
"menu_order": 0
},
"headers": [],
"status": 200
}]
}
And my react part is
render(){
return this.state.course.map(course =>
<Text style={styles.userStyle}>{course.courses.data.map(datas => datas.name)}</Text>
);
}
Please help me to figure out the mistake.I am getting this.state.course.map is not a function.My fetch request is as follows
state= {course:[]};
componentWillMount(){
fetch('https://www.mywebsite.com/' + this.props.navigation.state.params.id)
.then((response) => response.json())
.then((responseData) => this.setState({course: responseData}))
}
So you would need to show us how this.state is set, but if you're doing something like this.setState(jsonObject), the property you are looking for seems to be this.state.courses. This would access the array of courses. However, in the subsequent lines you try to access course.courses, which suggests you're setting the state like this.seState({course: jsonObject}) so it's not clear.
I'd say if you fix the first problem, you'll immediately hit another one because it doesn't look like data is an array but an object, so trying to call map on it is unlikely to do what you want (unless you've been playing with prototypes).
EDIT:
In response to the new info, I recommend the following:
render(){
if(this.state.course && this.state.course.courses) {
return this.state.course.courses.map(course =>
<Text style={styles.userStyle}>{course.data.name}</Text>
);
} else {
return [];
}
}
I have this json file and I want to access the array that is inside this object:
best-sellers": [
{
"title": "Chuteira Nike HyperVenomX Proximo II Society",
"price": 499.90,
"installments": {
"number": 10,
"value": 49.90
},
"high-top": true,
"category": "society",
"image": ""
},
{
"title": "Chuteira Nike HyperVenom Phantom III Cano Alto Campo",
"price": 899.90,
"installments": {
"number": 10,
"value": 89.90
},
"high-top": true,
"category": "campo",
"image": ""
}
}
]
This is the code on my component:
ngOnInit(): void {
this.service
.lista()
.subscribe(chuteiras =>{
this.chuteiras = chuteiras;
})
}
and my template looks like this:
<div *ngFor="let chuteira of chuteiras.best-sellers">
But angular is not reconigzing it the `best-sellers", here's the error that I'm getting:
Cannot read property 'best' of undefined
Just use bracket notation,
<div *ngFor="let chuteira of chuteiras["best-sellers"]">
well,that's one way of doing it but angular 6 came with a simple solution. when i was faced with this problem i myself resolved to this solution but it didn't work for me so after searching and making my own touches i ended up with this solution.
1.create the function to receive the JSON data in my case i used a web API
getTrending() {
return this.http.get(https://api.themoviedb.org/3/trending/all/day?api_key=${this.api_key});
}
2.call the function in my case i used a service, so after import it to my component i simply added this
showPopular(): void {
this.api.getTrending().subscribe((data: Array<object>) => {
this.list = data['results'];
console.log(this.list);
});
}
as you can see the data variable only accessed the information i required.