Pipe filter based on two or more attributes value in Angular2 - filter

If I have an array of objects like
[
{name: 'aaa', type: 'A'},
{name: 'aaa', type: 'B'},
....
]
How can I create a filter using pipe inside ngFor expression, something like
*ngFor='let obj of array | filter:name[nameValue]:type[typeValue]
Here, name[nameValue] name is a property, nameValue is its value.
only show the objects which match the nameValue in name property and typeValue in type property simultaneously.
I Want a more generalized filter which takes any property[propertyValue] and can filter output accordingly.

This is a way you could do it. Just give the filter pipe an array of the fields with the values you want to filter it with.
The Pipe:
#Pipe({
name: 'filter',
pure: false
})
export class FilterPipe implements PipeTransform {
transform(values: Array<any>, args:any[]):any {
return values.filter((value) => {
for (let i = 0; i < args.length; i++) {
if (value[args[i][0]] != args[i][1]) {
return false;
}
}
return true;
});
}
}
The code in the template:
<h3>Only Type A:</h3>
<div *ngFor="let elm of arr | filter:[['type', 'A']]">
<span>Name: {{elm.name}}</span> | <span>Type: {{elm.type}}</span>
</div>
<h3>Name bbb and Type B:</h3>
<div *ngFor="let elm of arr | filter:[['type', 'B'], ['name', 'bbb']]">
<span>Name: {{elm.name}}</span> | <span>Type: {{elm.type}}</span>
</div>
Plunker for working example

Related

Laravel - Assert json array ids using wildcard

In my application I have a response like this:
{
"items": [
{
"id": 10,
"field": "foo"
},
{
"id": 20,
"field": "bar"
}
]
}
I need to test the content of items and validate each id.
I've tried many solutions but no one works, for example (this is just a kind of pseudo-code):
assertJson(fn (AssertableJson $json) =>
$json->where('items.*.id', [10, 20])
)
Is there a way to use a wildcard to pick every ID and validate using an array?
You can use array_filter:
$idArray = [10, 20];
$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object
// Filter the items for items that aren't in the ID list
$invalidItems = array_filter($items, function ($el) {
// If the item has an id which isn't in the array, return true
return !in_array($el["id"], $idArray);
});
// This returns true if we found 0 items with IDs not in the ID list
return $invalidItems == [];
You can similarly use array_map to simplify your array, then compare it to your ID array:
$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object
$outIdArray = array_map(function($el) {
return $el["id"];
}, $items);
// Compare $outIdArray to [10, 20]
Not tested yet but below should work.
We attach an each on each child element under items and add a callback to where on that id key of each child.
<?php
assertJson(fn (AssertableJson $json) =>
$json->each('items', fn (AssertableJson $childJson) =>
$childJson->where('id', fn($idVal) =>
in_array($idVal, [10,20])
)
)
)

Get state from select form in child component with Gatsby

I code a Gatsby app with a Main page and two components. The value from a select form will be used to query a Postgresql database through a graphql query.
What I can already do: in the form component, I get the value from the select menu and pass it from this child component to the parent (the main page). In the data component, I can query the database with graphql and get the results with hardcoded values.
What I can't do yet: get the value from the select component to the data component and use it in my graphql query.
I tried different ways to get the value without success using this.props.value1 or this.state.value1. I also tested a simple component to make sure I could get the value from the parent to a child component and it worked seamlessly. So it's the way I try to import the value in a querying component that is the problem.
**//Data component**
let val = 88 //for hardcoded test. That works.
const DataPage = () => {
const data = useStaticQuery(query)
return (
<div>
<p>From Postgres: {data.postgres.allEstivalsList[val].nbr}</p>
</div>
)
}
const query = graphql`
{
postgres {
allAveragesList {
avg
}
allEstivalsList {
year
nbr
}
}
}
`;
export default DataPage;
**//Main page**
export default class App extends React.Component {
constructor(props) {
super(props);
}
state = {
value1: null,
// other values
}
render() {
return (
<div>
<p>Get state in main page: {this.state.value1}</p>
<DataPage val = {this.state.value1} />
<SelectForm clickHandler={y => { this.setState({ value1: y }); }} />
</div>
)
}
}
**//Form component**
export default class IndexPage extends React.Component {
state = {
value1: null,
}
handleClick = () => {
this.props.clickHandler(this.state.value1);
this.setState(prevState => {
return { value1: prevState.value1 };
});
};
render() {
return (
<Formlayout>
<p>Test -- Value for the selected year: {this.state.value1}</p>
<select onChange = {(e) => this.setState({ value1: e.target.value })}>
<option value="">-- Year --</option>
<option value="1">1901</option>
<option value="2">1902</option>
<option value="3">1903</option>
</select>
<button onClick={this.handleClick}>Go!</button>
</Formlayout>
)
}
}
I'd appreciate to get some directions to get the select value in the data component. As my test variable val is effectively working when used in the query, what I'd like to achieve is to apply to that variable the state from the component. And that's where I'm stuck right now.
The GraphQL query is executed at the build time, not at runtime. Have a look at the docs, it's possible to query data at build and at runtime.
https://www.gatsbyjs.org/docs/client-data-fetching/
// edit
Normally you would pre generate all (static) content pages - e.g. if you have data for 50 years, you let gatsby build those 50 pages and then you could navigate by path to the page.
If you want to pass a variable to the query, check the docs: https://www.gatsbyjs.org/docs/graphql-reference/#query-variables
query GetBlogPosts(
$limit: Int, $filter: MarkdownRemarkFilterInput, $sort: MarkdownRemarkSortInput
) {
allMarkdownRemark(
limit: $limit,
filter: $filter,
sort: $sort
) {
edges {
node {
frontmatter {
title
date(formatString: "dddd DD MMMM YYYY")
}
}
}
}
}

Filter an Array in an Observable

Here is an edited sample from learnrxjs. I want to filter the values in the type array. But thats not how it works: 'This condition will always return 'true' since the types 'string[]' and 'string' have no overlap.'
I am new to rxjs and cant figure out how to filter the array. Any advices? Is it possible?
const source = from([
{ name: 'Joe', age: 31, type: ['a', 'b'] },
{ name: 'Bob', age: 25, type: ['a'] }
]);
//filter out people with type b
const example = source.pipe(filter(person => person.type != 'a'));
//output: "People with type b: Bob"
const subscribe = example.subscribe(val => console.log(`Type a: ${val.name}`));
the filter() you are applying takes a function with signature T => boolean meaning that you will have to return a boolean true/false so it can filter out elements from the stream.
Your elements T are of type Object {name:string, age:number, type:array} so to filter on values in the type Array you will need to use the Array.indexOf prototype function:
source.pipe(filter(person => person.type.indexOf('b') == -1) // filter out people who have type b

Descending orderByChild() on firebase and Ionic 2

I need get items sorted by date, but obviously I need descending sorted to show the posts in correct order...
import {
AngularFireDatabase
} from 'angularfire2/database';
import 'rxjs/add/operator/map';
/*
Generated class for the FirebaseProvider provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class FirebaseProvider {
constructor(public afd: AngularFireDatabase) {}
getPostsItems() {
return this.afd.list('/Posts/', {
query: {
orderByChild: "date",
}
});
}
This query returns a ascendent order and I need a descendent order that it's not explained in Firebase web.
Which are queries I need?
One approach could be reversing the order in your component's template. First, you get a list of posts directly in your component:
posts.component.ts
export class PostsComponent {
posts: FirebaseListObservable<any>;
constructor(db: AngularFireDatabase) {
this.posts = db.list('/posts', {
query: {
orderByChild: 'date'
}
});
}
}
Then, you can use the reverse method to reverse your posts' order in your template:
posts.component.html
<div *ngFor="let post of (posts | async)?.slice().reverse()">
<h1>{{ post.title }}</h1>
</div>
Leaving this for Ionic 3 + AngularFire 4
posts.component.ts
import { AngularFireDatabase } from 'angularfire2/database';
export class PostsComponent {
posts;
constructor(db: AngularFireDatabase) {
this.posts = db.list('/posts', ref => ref.orderByChild('date')).valueChanges();
}
}
posts.component.html
<div *ngFor="let post of (posts | async)?.slice().reverse()">
<h1>{{ post.title }}</h1>
</div>
You should also use limitTolast() instead of limitToFirst() to ensure that your array will start with the last value that match with your filter in your DB (by default the filter is increasing order) and then use .reverse on your array to reverse it order:
1 - let array = db.list('/posts')ref => ref.orderByChild('date').limitToLast(5);
array = array.reverse();
Working without async in the ngFor
posts.component.ts
export class PostsComponent {
posts: FirebaseListObservable<any>;
constructor(db: AngularFireDatabase) {
this.posts = db.list('/posts', {
query: {
orderByChild: 'date'
}
});
}
}
Then, you can use the reverse method to reverse your posts' order in your template:
posts.component.html
<div *ngFor="let post of posts?.slice().reverse()">
<h1>{{ post.title }}</h1>
</div>

KendoUI Grid: Array as a field

I have a data source, which gets built from a JSON data string, containing a field called Fruit:
[{
... /other entries
fruit: [{
name: 1
}, {
name: 2
}, {
name: 3
}]
}]
I'm using this field in a KGrid, and would like to do a comma seperated list of links, from the names:
1, 2, 3
Currently, I'm hooking into the dataBound function, and build this up individually for the fruit field, is there an easier way to do this with, let's say, a template? I tried to look up information about something similar in the docs, but couldn't find anything pertaining to splitting arrays?
I wouldn't transform the data at the data source. That job is the responsibility of the UI component. Instead move your logic to the column template function of your grid. [ API reference ]
$('#grid').kendoGrid({
columns: [ {
field: 'fruit',
template: function(dataItem) {
var html = [];
for (var i = 0; i < dataItem.length; i++) {
html.push('' + dataItem[i].name + '');
}
return html.join(', ');
}
}],
dataSource: data
});

Resources