How make multicolumns pipes filter? - angularjs

I have such a paipe and it will work only for the column: name a for the author no longer.
export class FilterBook2 implements PipeTransform {
transform(items: Book[], filter: Book): any {
if (!items || !filter) {
return items;
}
// filter items array, items which match and return true will be kept, false will be filtered out
return items.filter((item) => item.Nazwa.indexOf(filter.Nazwa) || item.Autor.indexOf(filter.Autor) !== -1)
}
}
Input date:
filterargs = { Nazwa: "", Autor: "" }

I think you are trying to filter books by multiple arguments. You can pass multiple values to the pipe transform function like this:
<div *ngFor="let book of books | filterBook2 : firstArg : secondArg">
...
</div>
and in the pipe class
export class FilterBook2 implements PipeTransform {
transform(items: Book[], firstArg: Book, secondArg: any): any {
// your logic here
}
}

Related

JSON to Array binding in select Typescript

JSON
{
"bloodGroup":[
{"Id":1, "Value":"O +ve", "Description":"OPositive"},
{"Id":2, "Value":"O -ve", "Description":"ONegative"},
{"Id":3, "Value":"AB +ve", "Description":"ABPositive"}
],
"Gender":[
{"Id":1, "Value":"Male", "Description":"Male"},
{"Id":2, "Value":"Female", "Description":"Female"}
]
}
Pipe:
transform(aList: Lookup[], lookUpName: string): Lookup[] {
if (!aList || !lookUpName || aList.length === 0) {
return aList;
}
return Array.of(aList[lookUpName])
}
Lookup Class (autogenerated from nswag):
export interface ILookup {
name?: string | undefined;
value?: string | undefined;
description?: string | undefined;
}
HTML:
<option *ngFor="let gender of aList | lookupFilter:'Gender'" [value]="gender.Id">
{{gender.Value}}
</option>
The select doesn't bind the required values. What am i doing wrong?
Change Array.of(aList[lookUpName]) to aList[lookUpName]. Everything will work fine.
transform(aList: Lookup[], lookUpName: string): Lookup[] {
if (!aList || !lookUpName || aList.length === 0) {
return aList;
}
return aList[lookUpName];
}
StackBlitz.

Angular using javascript to make sort Pipe

I have a question when I build sort pipe in Angular2
here is my pipe code:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'sort'
})
export class SortPipe implements PipeTransform {
transform(value: any, propName: string): any {
return value.sort((a,b)=> {
return a[propName]-b[propName];
// if(a[propName]>b[propName]) {
// return 1;
// } else {
// return -1;
// }
});
}
}
When I use the code in comments, the pipe works, but when I use return a [propName]-b[propName]; it is not working;
For sort to work, you must return an integer (see reference). Are you sure, that subtracting your properties will always return these values?
Depends on what you are sorting. If the value is a number it should work. If it's a string then a NaN is returned. Second solution is better.
This code selects the property of the list of items objects passed in by the pipe and takes into consideration null values.
Below you can see what the pipe will look like with the *ngFor:
<tr *ngFor="let item of Clients | sort: sortType: sortDirection">
Below you can see when the column header is clicked, the arrows change direction of the arrow and make the sort output ascending or descending within the sort pipe.
<th>
<div class="d-flex flex-row hoverOver">
<span class="text-nowrap">Quantity</span>
<i class="fa hoverTrans align-self-center mx-1" [ngClass]="{'fa-arrows-v': column != 'created',
'fas fa-long-arrow-up': !sortDirection,
'fas fa-long-arrow-down': sortDirection }" (click)="sortType = 'qty'; sortDirection = !sortDirection">
</i>
Below is the Sort Pipe:
transform(items: any, sortType: any, sortDirection: any): any {
const direction = sortDirection ? -1 : 1;
items.sort((a, b) => {
if (a[sortType] < b[sortType] || (a[sortType] === null && b[sortType] !== null) || (a[sortType] === "" && b[sortType] !== null)) {
return -1 * direction;
} else if (a[sortType] > b[sortType] || (a[sortType] !== null && b[sortType] === null) || (a[sortType] !== null && b[sortType] === "")) {
return 1 * direction;
} else {
return 0;
}
});
return items;
}

How to use a regular for in Angular 2? [duplicate]

...for example...
<div class="month" *ngFor="#item of myCollection; #i = index">
...
</div>
Is possible to do something like...
<div class="month" *ngFor="#item of 10; #i = index">
...
</div>
...without appeal to a non elegant solution like:
<div class="month" *ngFor="#item of ['dummy','dummy','dummy','dummy','dummy',
'dummy','dummy','dummy']; #i = index">
...
</div>
?
Within your component, you can define an array of number (ES6) as described below:
export class SampleComponent {
constructor() {
this.numbers = Array(5).fill().map((x,i)=>i); // [0,1,2,3,4]
this.numbers = Array(5).fill(4); // [4,4,4,4,4]
}
}
See this link for the array creation: Tersest way to create an array of integers from 1..20 in JavaScript.
You can then iterate over this array with ngFor:
#Component({
template: `
<ul>
<li *ngFor="let number of numbers">{{number}}</li>
</ul>
`
})
export class SampleComponent {
(...)
}
Or shortly:
#Component({
template: `
<ul>
<li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
</ul>
`
})
export class SampleComponent {
(...)
}
#OP, you were awfully close with your "non-elegant" solution.
How about:
<div class="month" *ngFor="let item of [].constructor(10); let i = index">
...
</div>
Here I'm getting the Array constructor from an empty array: [].constructor, because Array isn't a recognized symbol in the template syntax, and I'm too lazy to do Array=Array or counter = Array in the component typescript like #pardeep-jain did in his 4th example. And I'm calling it without new because new isn't necessary for getting an array out the Array constructor.
Array(30) and new Array(30) are equivalent.
The array will be empty, but that doesn't matter because you really just want to use i from ;let i = index in your loop.
Edit to respond to comments:
Q. How can I use a variable to set the length of the NgFor loop?
Here is an example on how to render a table with variable columns/rows
<table class="symbolTable">
<tr *ngFor="let f of [].constructor(numRows); let r = index">
<td class="gridCell" *ngFor="let col of [].constructor(numCols); let c = index">
{{gridCards[r][c].name}}
</td>
</tr>
</table>
export class AppComponent implements OnInit {
title = 'simbologia';
numSymbols = 4;
numCols = 5;
numRows = 5;
guessCards: SymbolCard[] = [];
gridCards: SymbolCard[][] = [];
ngOnInit(): void {
for (let c = 0; c < this.numCols; c++) {
this.guessCards.push(new SymbolCard());
}
for (let r = 0; r < this.numRows; r++) {
let row: SymbolCard[] = [];
for (let c = 0; c < this.numCols; c++) {
row.push(
new SymbolCard({
name: '' + r + '_' + c
}))
}
this.gridCards.push(row);
}
}
}
No there is no method yet for NgFor using numbers instead collections,
At the moment, *ngFor only accepts a collection as a parameter, but you could do this by following methods:
Using pipe
demo-number.pipe.ts:
import {Pipe, PipeTransform} from 'angular2/core';
#Pipe({name: 'demoNumber'})
export class DemoNumber implements PipeTransform {
transform(value, args:string[]) : any {
let res = [];
for (let i = 0; i < value; i++) {
res.push(i);
}
return res;
}
}
For newer versions you'll have to change your imports and remove args[] parameter:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'demoNumber'})
export class DemoNumber implements PipeTransform {
transform(value) : any {
let res = [];
for (let i = 0; i < value; i++) {
res.push(i);
}
return res;
}
}
html:
<ul>
<li>Method First Using PIPE</li>
<li *ngFor='let key of 5 | demoNumber'>
{{key}}
</li>
</ul>
Using number array directly in HTML(View)
<ul>
<li>Method Second</li>
<li *ngFor='let key of [1,2]'>
{{key}}
</li>
</ul>
Using Split method
<ul>
<li>Method Third</li>
<li *ngFor='let loop2 of "0123".split("")'>{{loop2}}</li>
</ul>
Using creating New array in component
<ul>
<li>Method Fourth</li>
<li *ngFor='let loop3 of counter(5) ;let i= index'>{{i}}</li>
</ul>
export class AppComponent {
demoNumber = 5 ;
counter = Array;
numberReturn(length){
return new Array(length);
}
}
#Working demo
I couldn't bear the idea of allocating an array for plain repeat of components, so I've written a structural directive. In simplest form, that doesn't make the index available to the template, it looks like this:
import { Directive, Input, TemplateRef, ViewContainerRef } from '#angular/core';
#Directive({ selector: '[biRepeat]' })
export class RepeatDirective {
constructor( private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
#Input('biRepeat') set count(c:number) {
this.viewContainer.clear();
for(var i=0;i<c;i++) {
this.viewContainer.createEmbeddedView(this.templateRef);
}
}
}
http://plnkr.co/edit/bzoNuL7w5Ub0H5MdYyFR?p=preview
you can also use like that
export class SampleComponent {
numbers:Array<any> = [];
constructor() {
this.numbers = Array.from({length:10},(v,k)=>k+1);
}
}
HTML
<p *ngFor="let i of numbers">
{{i}}
</p>
This can also be achieved like this:
HTML:
<div *ngFor="let item of fakeArray(10)">
...
</div>
Typescript:
fakeArray(length: number): Array<any> {
if (length >= 0) {
return new Array(length);
}
}
Working Demo
Use a pipe to transform the number to an array.
#Pipe({
name: 'enumerate',
})
export class EnumeratePipe implements PipeTransform {
transform(n: number): number[] {
return [...Array(n)].map((_,i) => i);
}
}
Then use the pipe in your template.
<p *ngFor="let i of 5 | enumerate">
Index: {{ i }}
</p>
https://stackblitz.com/edit/angular-ivy-pkwvyw?file=src/app/app.component.html
I solved it like this using Angular 5.2.6 and TypeScript 2.6.2:
class Range implements Iterable<number> {
constructor(
public readonly low: number,
public readonly high: number,
public readonly step: number = 1
) {
}
*[Symbol.iterator]() {
for (let x = this.low; x <= this.high; x += this.step) {
yield x;
}
}
}
function range(low: number, high: number) {
return new Range(low, high);
}
It can be used in a Component like this:
#Component({
template: `<div *ngFor="let i of r">{{ i }}</div>`
})
class RangeTestComponent {
public r = range(10, 20);
}
Error checking and assertions omitted on purpose for brevity (e.g. what happens if step is negative).
You can use lodash:
#Component({
selector: 'board',
template: `
<div *ngFor="let i of range">
{{i}}
</div>
`,
styleUrls: ['./board.component.css']
})
export class AppComponent implements OnInit {
range = _.range(8);
}
I didn't test code but it should work.
<div *ngFor="let number of [].constructor(myCollection)">
<div>
Hello World
</div>
</div>
This is a nice and quick way to repeat for the amount of times in myCollection.
So if myCollection was 5, Hello World would be repeated 5 times.
My-component.ts
numbers: number[] = [];
constructor() {
this.numbers = new Array<number>(10)
}
My-component.html
<div *ngFor="let num of numbers; let i = index">{{ i }}</div>
Since the fill() method (mentioned in the accepted answer) without arguments throw an error, I would suggest something like this (works for me, Angular 7.0.4, Typescript 3.1.6)
<div class="month" *ngFor="let item of items">
...
</div>
In component code:
this.items = Array.from({length: 10}, (v, k) => k + 1);
Using custom Structural Directive with index:
According Angular documentation:
createEmbeddedView Instantiates an embedded view and inserts it into this container.
abstract createEmbeddedView(templateRef: TemplateRef, context?: C, index?: number): EmbeddedViewRef.
Param Type Description
templateRef TemplateRef the HTML template that defines the view.
context C optional. Default is undefined.
index number the 0-based index at which to insert the new view into this container. If not specified, appends the new view as the last entry.
When angular creates template by calling createEmbeddedView it can also pass context that will be used inside ng-template.
Using context optional parameter, you may use it in the component,
extracting it within the template just as you would with the *ngFor.
app.component.html:
<p *for="number; let i=index; let c=length; let f=first; let l=last; let e=even; let o=odd">
item : {{i}} / {{c}}
<b>
{{f ? "First,": ""}}
{{l? "Last,": ""}}
{{e? "Even." : ""}}
{{o? "Odd." : ""}}
</b>
</p>
for.directive.ts:
import { Directive, Input, TemplateRef, ViewContainerRef } from '#angular/core';
class Context {
constructor(public index: number, public length: number) { }
get even(): boolean { return this.index % 2 === 0; }
get odd(): boolean { return this.index % 2 === 1; }
get first(): boolean { return this.index === 0; }
get last(): boolean { return this.index === this.length - 1; }
}
#Directive({
selector: '[for]'
})
export class ForDirective {
constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { }
#Input('for') set loop(num: number) {
for (var i = 0; i < num; i++)
this.viewContainer.createEmbeddedView(this.templateRef, new Context(i, num));
}
}
Please find attached my dynamic solution if you want to increase the size of an array dynamically after clicking on a button (This is how I got to this question).
Allocation of necessary variables:
array = [1];
arraySize: number;
Declare the function that adds an element to the array:
increaseArrayElement() {
this.arraySize = this.array[this.array.length - 1 ];
this.arraySize += 1;
this.array.push(this.arraySize);
console.log(this.arraySize);
}
Invoke the function in html
<button md-button (click)="increaseArrayElement()" >
Add element to array
</button>
Iterate through array with ngFor:
<div *ngFor="let i of array" >
iterateThroughArray: {{ i }}
</div>
A simplest way that i have tried
You can also create an array in your component file and you can call it with *ngFor directive by returning as an array .
Something like this ....
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-morning',
templateUrl: './morning.component.html',
styleUrls: ['./morning.component.css']
})
export class MorningComponent implements OnInit {
arr = [];
i: number = 0;
arra() {
for (this.i = 0; this.i < 20; this.i++) {
this.arr[this.i]=this.i;
}
return this.arr;
}
constructor() { }
ngOnInit() {
}
}
And this function can be used in your html template file
<p *ngFor="let a of arra(); let i= index">
value:{{a}} position:{{i}}
</p>
Here is something quite clean and simple for Angular:
In .ts:
max = 10;
In .html:
<div *ngFor="let dummy of ','.repeat(max).split(','); index as ix">
- {{ix + 1}}:
</div>
I think the simple and short solution is here..
Your my.component.ts file
setArrayFromNumber(i: number) {
return new Array(i);
}
Your my.component.html file
<li *ngFor='let in of setArrayFromNumber(5); let i = index'>{{ i }}</li>
That's it!!

How to turn an [object Object] into an array?

I'm displaying some values from an API call into an ngFor directive. I'm trying to convert the data into an array. This is what i get from the server :
Data from the server
I subscribe the data like this :
onVideoClick(video_id) {
this._dashboardService.getVideoData(video_id)
.subscribe(videoData => {
if (videoData.ok) {
console.log(videoData.json());
this.videoData = videoData.json();
} else {
console.log('pas de données vidéos');
}
});
}
I display my data this way :
<tr>
<th *ngFor="let vD of videoData">{{vD.attr_libelle}}</th>
</tr>
How turn this Object into an array ?
You could implement a custom pipe for this. Here is a sample one that creates an array from object entries:
#Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push({key: key, value: value[key]);
}
return keys;
}
}
and use it like that:
<span *ngFor="#entry of content">
Key: {{entry.key}}, value: {{entry.value}}
</span>
Don't forget to add the pipe into the pipes attribute of the component where you want to use it.
I would use the library Lodash for this conversion.
onVideoClick(video_id) {
this._dashboardService.getVideoData(video_id)
.subscribe(videoData => {
if (videoData.ok) {
console.log(videoData.json());
this.videoData = _.values(videoData.json());
} else {
console.log('pas de données vidéos');
}
});
}
For documentation see here.

Push object into new array with interfaces in Angular2

Im retrieving a list of objects from a service in Angular2.
On click I want to put the object from my ngFor onto a new array (with the same interface).
I'm able to display the list with items just fine. I'm also able to display the item I selected in the console, but I'm not able to push this object into a new array with the same interface.
My interface:
export interface Item {
id: string,
name: string,
icon_url: string,
exterior: string,
type: string,
tag: string,
addedClass: string,
selected: boolean
}
My component:
import {Component, OnInit, DoCheck} from 'angular2/core';
import {Item} from './../interfaces/interfaces.ts';
import {ItemService} from './../services/item.service.ts';
#Component({
template: `
<h2>Add item</h2>
<div class="itemlist">
<ul class="items">
<li
*ngFor="#item of items"
[class.selected]="item === selectedItem"
(click)="onSelect(item)"
>
<span class="item">
{{item.name}}
</span>
</li>
</ul>
</div>
`
})
export class AddTradeComponent implements OnInit {
public items: Item[];
public selectedItems: Item[];
constructor(private _itemService: ItemService) { }
getUserItems() {
this._itemService.getUserItems().then(userItems => this.items = userItems);
}
ngOnInit() {
this.getUserItems();
}
onSelect(item) {
console.log('items ', item);
this.selectedItems.push(item);
}
}
Errors that I'm getting:
Error during evaluation of "click"
TypeError: Cannot read property 'push' of undefined
Logging the object works fine, I just cannot push it onto this.selectedItems.
You didn't initialize your arrays. Either initialize the arrays when you declare your member variables:
public items: Item[] = [];
public selectedItems: Item[] = [];
Or initialize them in your constructor:
constructor() {
this.items = [];
this.selectedItems = [];
}

Resources