I know Angular 2 is old, but I used the best course our library had, and most of it still works...
I have a list of data in a json file, which looks something like this:
{
"name" :"example one",
"source" :"",
"source_type" :"",
"text" :"A bumblebee in the field"
},
{
"name" :"example two",
"source" :"",
"source_type" :"",
"text" :"Two flowers with red petals"
},
I can display the whole list of names and can access the other data too.
Now I want to have a text field so the user can search.
I'd like a search option for name and one for text (even though the text doesn't display directly).
The problem is: I'd like users to be able to search for single words, like 'one' and get all results that contain the word 'one'.
Is this possible? Or would I be better of learning how to set up a database online and implement a search option from there?
This might give you the idea for the search using filter
var searchText = "three";
var data = [
{
name: "example one three",
source: "",
source_type: "",
text: "A bumblebee in the field"
},
{
name: "example two",
source: "",
source_type: "",
text: "Two flowers with red petals"
},
{
name: "example three",
source: "",
source_type: "",
text: "Two flowers with red petals"
}
];
let newObj = data.filter((value)=>{
return value.name.indexOf(searchText) != -1 ? value : null
});
console.log(newObj);
Well, you could do this using a Pipe. But using Filter Pipes is not really recommended by the Angular Team. So you can essentially listen to the keyup event on your input field and then call a function to filter the data.
Now, since this is a strict filter object by a key, you can simply use the filter method on Array and check if the indexOf the searchInput in the name or text is >-1
Here's how:
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
filterText;
data = [{
"name": "example one",
"source": "",
"source_type": "",
"text": "A bumblebee in the field"
},
{
"name": "example two",
"source": "",
"source_type": "",
"text": "Two flowers with red petals"
}];
filteredData = [];
ngOnInit() {
this.filteredData = [...this.data];
}
onChange() {
console.log('ran');
this.filteredData = this.data.filter(
datum => (datum.name.indexOf(this.filterText) > -1 || datum.text.indexOf(this.filterText) > -1));
console.log(this.filteredData);
}
}
And in the template:
<input type="text" [(ngModel)]="filterText" (keyup)="onChange()">
<ul>
<li *ngFor="let datum of filteredData">
{{ datum.name }}
</li>
</ul>
Here's a Sample StackBlitz for your ref.
Related
Helloo !
i would use a json files in my angular-app.
I have a local json file 'client.json'
{
"T001": {
"name": "admin",
"type": "adm",
"id": "1",
"class": "viewer",
},
"T002": {
"name": "Customer",
"type": "usr",
"id": "2",
"class": "viewer",
},
"T003": {
"name": "DB450",
"type": "air",
"id": "3",
"class": "gateway",
},
"T004": {
"name": "DB620",
"type": "air",
"id": "4",
"class": "gateway",
}
}
Actually, i could read this file and i got access to the name of object (object of object) -- T003.name for example and using console.log to display the result but this is not what i'm looking for because i can't use this variable into html template.
import { Component, OnInit } from '#angular/core';
import * as tunnelsData from '../data/client.json'
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit{
objects: any;
ngOnInit(): void {
this.objects = (tunnelsData as any).default;
console.log(this.object);
}
}
I'm looking for loads this file into a variable, then using this variable on a html template with ngfor like this :
<my-app *ngFor="let T of objects"
[tunnel]="T"
[name]="T.name"><my-app>
tunnel and name are two variables of my second component
To display at the end an example like this :
Tunnel : T001 (which the first object of my file)
Name : admin (which an object of the first object)
.
.
.
I hope it's clear for you.
any idea please ?
Thank you.
The only problem is that you don't have an array in your json data. you have this data as an object
{
"T001": {
"name": "admin",
"type": "adm",
"id": "1",
"class": "viewer",
},
"T002": {
"name": "Customer",
"type": "usr",
"id": "2",
"class": "viewer",
},
"T003": {
"name": "DB450",
"type": "air",
"id": "3",
"class": "gateway",
},
"T004": {
"name": "DB620",
"type": "air",
"id": "4",
"class": "gateway",
}
}
And in your html you are trying to loop through your elements with ngFor. So ngFor directive always expects an array to loop in html.
So the simple solution you can have is that in your component you can do like this
app.component.ts
this.objects = (tunnelsData as any).default;
this.objectKeys = Object.keys(his.objects); // ['T001', 'T002', 'T003', 'T004']
And inside of your html you can do like this
<my-app *ngFor="let keyName of objectKeys" [tunnel]="objects[keyName]" [name]="objects[keyName]?.name"><my-app>
What i am doing here is that first i am getting all of the keys of your object as an array and then i am iterating over the array of keys. And in html i am accessing the individual object by it's key name which is coming from ngFor loop.
Generaly, angular does not support iterating over objects in ngFor.
Check your browser's dev tools console. There should be an error.
You can use pipe in ngFor to make it work KeyValuePipe.
For more details check this thread.
And to display the key (eg."T001"), you would need to pass it too.
*ngFor="let item of objects | keyvalue"
[tunnel]="item.value"
[name]="item.value.name"
[key]="item.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 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> }
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 have a HTML list build with ngFor from AngularFire2 ObjectObservable.
Once a object(not last) is deleted from the Database, null appears in his position(index) and the HTML list tries to update but an error is thrown from the ngFor loop.
JSON from AngularFire2 DB:
let tasksList = {
"name": "Task list",
"items": [
{
"name": "Task 1"
},
{
"name": "Test 2"
},
{
"name": "Test 3"
}
]
}
HTML:
<ul *ngFor="let theTask of tasksList.items">
<li>{{theTask.name}}</li>
</ul>
After task 1 has been removed, the JSON looks like this:
let tasksList = {
"name": "Task list",
"items": [
null,
{
"name": "Test 2"
},
{
"name": "Test 3"
}
]
}
I think that the simplest solution would be to stop the null value from replacing the deleted object.
But is that possible?
Thank you
You can use angular 2's pipe (angular 1's filter) in order to filter all empty items:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'removeNull', pure: false})
export class RemoveNullPipe implements PipeTransform {
transform(items: any[]): any {
return items.filter(item => item);
}
}
then use it in HTML:
<ul>
<li *ngFor="let theTask of tasksList.items | removeNull">{{ theTask.name }}</li>
</ul>
I moved *ngFor from <ul> to <li> element, it seems better to repeat <li>s within <ul> and not repeat uls
plunker: http://plnkr.co/edit/IQq45kaJqqWRZOqhZdoe?p=preview