I am making application in angular 6 and As i am sending and receiving data in dynamic mode, i am in the need to pass and retrieve the array values through attribute for which i have used the following,
HTML:
<ul class="list-unstyled" id="list" [attr.parent_id]="123">
<li #li class="media p-2 column" *ngFor="let item of items;let i = index;" [attr.items]="item"> {{item.name}} </li>
</ul>
TS:
import { Component, ViewChildren, QueryList, ElementRef } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
#ViewChildren("li") listElements: QueryList<ElementRef<HTMLLIElement>>;
name = 'Angular';
items = [
{ "id": 123, "name": "hello" },
{ "id": 234, "name": "world" }
]
ngAfterViewInit() {
this.printElements();
this.listElements.changes.subscribe(_ => this.printElements());
}
private printElements() {
const elements = this.listElements.toArray();
elements.forEach(element => {
console.log(element.nativeElement.getAttribute('items'));
})
}
}
Working Stackblitz:
https://stackblitz.com/edit/angular-jndtv1
Here instead of
console.log(element.nativeElement.getAttribute('items'));
(which gives [object object])
If i include JSON.stringify,
console.log(JSON.stringify(element.nativeElement.getAttribute('items')));
(gives "[object object]")
Kindly help me to get the array values through the attribute using only Pure javascript/typescript without jquery.
you can do this, not the good way but it works:
in your html:
<ul class="list-unstyled" id="list" [attr.parent_id]="123">
<li #li class="media p-2 column" *ngFor="let item of items;let i = index;"
[attr.items]="item | json"> {{item.name}} </li>
</ul>
in your ts:
private printElements() {
const elements = this.listElements.toArray();
elements.forEach(element => {
console.log(JSON.parse(element.nativeElement.getAttribute('items')).id);
console.log(JSON.parse(element.nativeElement.getAttribute('items')).name);
})
}
DEMO
Related
I am building a basic to do app, and I am trying to append a new 2D item with properties Item: string, and completed: boolean. The item value gets read from an html tag, and added to the array.
How do I initialize the array and can I do it while populating that array?
HTML:
<div>
<input id='listinput' type="text">
<button id='submitbutton' (click)=addtolist()>Submit</button>
<button id='clearbutton'>Clear list</button>
</div>
<ul id='itemlist'>list here</ul>
JS:
import { Component, OnInit } from '#angular/core';
export interface ListInterface{
todolistarray: {listitem: string, completed: boolean}[]
}
#Component({
selector: 'app-todolist',
templateUrl: './todolist.component.html',
styleUrls: ['./todolist.component.css']
})
export class TodolistComponent implements OnInit {
addtolist() {
var todolistarray:ListInterface[]
var listitemvar = (document.getElementById('listinput') as HTMLInputElement).value;
alert(listitemvar);
(document.getElementById('itemlist') as HTMLDataListElement).innerText = listitemvar;
}
constructor() {}
ngOnInit(): void {}
}
Thank you in advance for assistance.
<div>
<input id='listinput' type="text" #input>
<button id='submitbutton' (click)="list.push({ item: input.value, completed: false })">Submit</button>
<button id='clearbutton' (click)="list = []">Clear list</button>
</div>
<ul id='itemlist'>
<li *ngFor="let item of list">
{{ item.item }}. Completed ? {{ item.completed }}
</li>
</ul>
export class TodolistComponent implements OnInit {
list: { item: string, completed: boolean }[] = [];
}
I have a single array whose data I need to display at two different sections using *ngFor. Like I have two sections: Section 1 and Section 2. From the below array I need to display keys having values section1 inside the first tag and those having values as section2 inside the second tag. I have tried a lot but not able to do so, considerably new with Angular. Please help!
Note: I cannot use two different variables and then filter it out in ts file and then use those 2 variables inside my *ngFor.
arr=[{age:"29",data1:"section1"},{age:"30",data1:"section2"},{age:"22",data1:"section1"},{age:"32",data1:"section2"}]
HTML Code:
<div *ngFor="let data of arr;">
<p>Section 1</p>
<h1>{{data.age}}</h1>
<p>Section 2</p>
<h1>{{data.age}}</h1>
</div>
You can try like below.
component.ts
import { Component, OnInit, VERSION } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
arr = [
{ age: '29', data1: 'section1' },
{ age: '30', data1: 'section2' },
{ age: '22', data1: 'section1' },
{ age: '32', data1: 'section2' },
];
sortedData;
constructor() {}
ngOnInit() {
this.sortedData = this.arr.reduce((acc, curr) => {
if (acc.hasOwnProperty(curr.data1)) {
acc[curr.data1].push(curr);
return acc;
}
acc[curr.data1] = [curr];
return acc;
}, {});
}
}
component.html
<div *ngFor="let item of sortedData | keyValue">
<h2>{{ item.key }}</h2>
<div *ngFor="let obj of item.value">{{ obj.age }}</div>
</div>
key-value pipe
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'keyValue',
})
export class KeyValuePipe implements PipeTransform {
transform(input: any): any {
let keys = [];
for (let key in input) {
if (input.hasOwnProperty(key)) {
keys.push({ key: key, value: input[key] });
}
}
return keys;
}
}
Updated code.
Working Demo
Let me know if you have any issue.
The easy way if make two *ngFor. You can use *ngTemplateOutlet if you can not "repeat" the "inner html"
<div>Section 1</div>
<div *ngFor="let item of arr">
<ng-container *ngIf="item.data1 == 'section1'">
<ng-container
*ngTemplateOutlet="template; context: { $implicit: item }"
></ng-container>
</ng-container>
</div>
<div>Section 2</div>
<div *ngFor="let item of arr">
<ng-container *ngIf="item.data1 == 'section2'">
<ng-container
*ngTemplateOutlet="template; context: { $implicit: item }"
></ng-container>
</ng-container>
</div>
<ng-template #template let-item>
{{ item | json }}
</ng-template>
If you only want an unique loop you need "play" with css flex order
<div class="wrapper">
<div [style.order]="0">Section 1</div>
<div [style.order]="1000">Section 2</div>
<div
*ngFor="let item of arr; let i = index"
[style.order]="item.data1 == 'section2' ? 1000 + i : 1 + i"
>
{{ item | json }}
</div>
</div>
where
.wrapper{
display:flex;
flex-direction: column;
}
You can see the two approach in this stackbliz
In Angular 7 + Electron 4 I use ngx-pagination but can't resolve problem with filter. I make as in a documentation, but I get error Uncaught Error: Template parse errors:
The pipe 'stringFilter' could not be found
Help me please. Thanks in advance
Html-1:
<input
type="text"
name="search"
class="search__input"
placeholder="Search by Name..."
[(ngModel)]="tableService.filter">
Html-2:
<ul class="table-body__list">
<li *ngFor="let item of tableService.items | stringFilter: tableService.filter | paginate: config">
<app-item [item]="item"></app-item>
</li>
</ul>
<pagination-controls
[maxSize]="maxSize"
directionLinks="true"
responsive="true"
previousLabel="Previous page"
nextLabel="Next page"
(pageChange)="onPageChange($event)">
</pagination-controls>
TypeScript:
import { Component, OnInit } from '#angular/core';
import { PaginationInstance } from 'ngx-pagination';
import { TableService } from '../../services/table.service';
#Component({
selector: 'app-jobs-table',
templateUrl: './jobs-table.component.html',
styleUrls: ['./jobs-table.component.scss']
})
export class JobsTableComponent implements OnInit {
filter = '';
maxSize = 9;
config: PaginationInstance = {
itemsPerPage: 11,
currentPage: 1
};
constructor(public tableService: TableService) { }
ngOnInit() {
}
onPageChange(number: number) {
this.config.currentPage = number;
}
}
In TableService:
filter = '';
As found on github (search on filter in the repository). apparently npx-pagination doesn't come with any standard filter pipes. their doc is .... sub-optimal
import {Pipe} from "#angular/core";
/**
* A simple string filter, since Angular does not yet have a filter pipe built in.
*/
#Pipe({
name: 'stringFilter'
})
export class StringFilterPipe {
transform(value: string[], q: string) {
if (!q || q === '') {
return value;
}
return value.filter(item => -1 < item.toLowerCase().indexOf(q.toLowerCase()));
}
}
Funny comment btw: Angular removed pipes for filtering because of performance reasons.
you ca only change stringFilter with filter .
<ul class="table-body__list">
<li *ngFor="let item of tableService.items | filter: tableService.filter | paginate: config">
<app-item [item]="item"></app-item>
</li>
</ul>
I am actually trying to inject the array and the data inside the array to another component but is constantly getting errors.
My list.component.ts
Here i injected the itemList array from app.component and this component is working just fine. No errors here.
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
import {List} from './list.model'
#Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit {
#Input() itemList: List[] = [];
#Output() onItemSelected: EventEmitter<List>;
private currentItem: List;
constructor(){
this.onItemSelected = new EventEmitter();
}
onClick(list: List): void {
this.currentItem = list;
this.onItemSelected.emit(list);
console.log(`clicking list title: ${list.title}`);
}
isSelected(list: List): boolean {
if (!list || !this.currentItem) {
return false;
}
return list.title === this.currentItem.title;
}
ngOnInit() {
}
}
list.component.html
Here i try to inject both the array and then using ngFor i try to inject the single list also.
<div class="ui grid posts">
<app-list-row
[lists]="itemList"
*ngFor="let list of itemList"
[list]="list"
(click)='onClick(list)'
[class.selected]="isSelected(list)">
</app-list-row>
</div>
list-row.component.ts
I am mainly trying to input the array in this component so that i can use the splice method to delete my list. I tried the delete list;method but this says i cannot use delete in strict mode. Therefore i am trying to input the array and use the splice method.
import { Component, OnInit, Input} from '#angular/core';
import {List} from '.././list/list.model';
#Component({
selector: 'app-list-row',
inputs: ['list: List'],
templateUrl: './list-row.component.html',
styleUrls: ['./list-row.component.css'],
host: {'class': 'row'},
})
export class ListRowComponent implements OnInit {
list: List;
#Input() lists: List[];
deletelist(list: List): void {
let index: number = this.lists.indexOf(list);
if (index !== -1) {
this.lists.splice(index, 1);
}
}
ngOnInit() {
}
}
list-row.component.html
In this write a div and use a lable of delete icon and give an event of click with the "deleteList(list)".
<div class="Eight wide column left aligned title">
<div class="value">
<div class = "hello">
<b>
{{ list.title | uppercase }}
</b>
<div style="float: right;" class="ui label">
<i class="delete icon"
(click)="deleteList(list)"
></i>
</div>
</div>
</div>
</div>
These are my codes and i dont know whether i can do the dependency injection of both the array and its single data in the array. If i can, what ways are there to do it. while running in server the console error is
Unhandled Promise rejection: Template parse errors:
Can't bind to 'list' since it isn't a known property of 'app-list-row'.
1. If 'app-list-row' is an Angular component and it has 'list' input, then verify that it is part of this module.
2. If 'app-list-row' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '#NgModule.schemas' of this component to suppress this message.
("
[lists]="itemList"
*ngFor="let list of itemList"
[ERROR ->][list]="list"
(click)='onClick(list)'
[class.selected]="isSelected(list)">
"): ListComponent#4:2 ; Zone: <root> ; Task: Promise.then ; Value: SyntaxError {__zone_symbol__error: Error: Template parse errors:
Can't bind to 'list' since it isn't a known property of 'app-list-row'…, _nativeError: ZoneAwareError, __zone_symbol__currentTask: ZoneTask, __zone_symbol__stack: "Error: Template parse errors:↵Can't bind to 'list'…ttp://localhost:4200/polyfills.bundle.js:6060:47)", __zone_symbol__message: "Template parse errors:↵Can't bind to 'list' since …lected]="isSelected(list)">↵"): ListComponent#4:2"} Error: Template parse errors:
Can't bind to 'list' since it isn't a known property of 'app-list-row'.
1. If 'app-list-row' is an Angular component and it has 'list' input, then verify that it is part of this module.
2. If 'app-list-row' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '#NgModule.schemas' of this component to suppress this message.
("
[lists]="itemList"
*ngFor="let list of itemList"
[ERROR ->][list]="list"
(click)='onClick(list)'
[class.selected]="isSelected(list)">
Thank you.
Add #Input() to list variable in ListRowComponent class and check if it is working or not and remove inputs from metadata.
import { Component, OnInit, Input} from '#angular/core';
import {List} from '.././list/list.model';
#Component({
selector: 'app-list-row',
templateUrl: './list-row.component.html',
styleUrls: ['./list-row.component.css'],
host: {'class': 'row'},
})
export class ListRowComponent implements OnInit {
#Input() list: List;
#Input() lists: List[];
deletelist(list: List): void {
let index: number = this.lists.indexOf(list);
if (index !== -1) {
this.lists.splice(index, 1);
}
}
ngOnInit() {
}
}
or
Remove :List from inputs as
import { Component, OnInit, Input} from '#angular/core';
import {List} from '.././list/list.model';
#Component({
selector: 'app-list-row',
templateUrl: './list-row.component.html',
inputs :['list']
styleUrls: ['./list-row.component.css'],
host: {'class': 'row'},
})
export class ListRowComponent implements OnInit {
list: List;
#Input() lists: List[];
deletelist(list: List): void {
let index: number = this.lists.indexOf(list);
if (index !== -1) {
this.lists.splice(index, 1);
}
}
ngOnInit() {
}
}
I got the answer. I did not do two input bindings but i created a custom event in the list-row.component and emmited the list to list.Component.
import { Component, Input, Output, EventEmitter} from '#angular/core';
import {List} from '.././list/list.model';
#Component({
selector: 'app-list-row',
templateUrl: './list-row.component.html',
styleUrls: ['./list-row.component.css'],
host: {
'class': 'row'
}
})
export class ListRowComponent {
#Input() list: List;
#Output() deleted = new EventEmitter<List>();
deletedl() {
const listing: List = this.list;
this.deleted.emit(listing);
}
In the template I used the click event to call the deletel() method.
<div class="Eight wide column left aligned title">
<div class="value">
<div class = "hello">
<b>
{{ list.title | uppercase }}
</b>
<div style="float: right;" class="ui label">
<i class="delete icon"
(click)="deletedl()">
</i>
</div>
</div>
</div>
</div>
Then I just called the event in the list.component
list.component.html
<div class="ui grid posts">
<app-list-row
*ngFor="let list of itemList"
[list]="list"
(click)='onClick(list)'
[class.selected]="isSelected(list)"
(deleted)="deletedl($event)">
</app-list-row>
</div>
Then i called a method in list.component to delete the list from the array using Splice method.
list.component
import { Component, Input} from '#angular/core';
import {List} from './list.model';
#Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
})
export class ListComponent {
#Input() itemList: List[] = [];
private currentItem: List;
onClick(list: List): void {
this.currentItem = list;
console.log(`clicking list title: ${list.title}`);
}
isSelected(list: List): boolean {
if (!list || !this.currentItem) {
return false;
}
return list.title === this.currentItem.title;
}
deletedl(list: List) {
console.log(`deleting list title: ${list.title}`);
let index: number = this.itemList.indexOf(list);
if (index !== -1) {
this.itemList.splice(index, 1);
}
}
}
I have learned that if we want to get some input from the parent component than using property binding helps and when we want to run some output from our parent component then event binding is helpful.
I do not know much English, so I use a translator, there may be errors((
I learn AngularFire2 and ran into a problem at this point (the fourth of five):
https://github.com/angular/angularfire2/blob/master/docs/4-querying-lists.md
If we scroll down, we can understand what makes this app.
By pressing the button (eg small) script comes in the items and there check item1, item2, item3, if any one of item (1,2,3) it is small, it takes its text and displays it.
(What is the problem)
And how to make brute force the entire database without giving exact values check item1/2/3
That is too much to do this:
https://hsto.org/files/0a5/d13/4ba/0a5d134ba32440dcb0bd5a2f90c37c86.png
My code:
import { Component } from '#angular/core';
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2';
import { Subject } from 'rxjs/Subject';
#Component({
selector: 'app-root',
template: `
<ul>
<li *ngFor="let item of items | async">
{{ item.text }}
</li>
</ul>
<div>
<h4>Filter by size</h4>
<button (click)="filterBy('small')">Small</button>
<button (click)="filterBy('medium')">Medium</button>
<button (click)="filterBy('large')">Large</button>
</div>
`,
})
export class AppComponent {
items: FirebaseListObservable<any[]>;
sizeSubject: Subject<any>;
constructor(af: AngularFire) {
this.sizeSubject = new Subject();
this.items = af.database.list('/items', {
query: {
orderByChild: 'size',
equalTo: this.sizeSubject
}
});
}
filterBy(size: string) {
this.sizeSubject.next(size);
}
}