How to use ngFor index variable in child component - javascript

I have a created a list out of an array using ngFor, and I am importing in element's of that list via other components.
In my list component, I am using ngFor to get the index, I would like to use this index within the child components (see code) to create dynamic variables but I can't seem to get it to work.
//main list html template
<li *ngFor="#task of taskService.tasks; #i = index" class="list-group-item">
<task-item [task]="task"></task-item>
</li>
//task item component html template
<div class="read-only">
<label [contentEditable]="taskService.editable[i] == true" ng-model="task.title">{{task.title}}</label>
<p [contentEditable]="taskService.editable[i] == true" ng-model="task.description">{{task.description}}</p>
<task-actions [task]="task"></task-actions>
</div>
//task actions component html template
<button type="button" (click)="taskService.deleteTask(task.title)" class="btn btn-danger btn-xs">delete</button>
<button type="button" (click)="taskService.editTask(i)" class="btn btn-info btn-xs">Edit</button>
<button type="button" (click)="completeTask()" class="btn btn-success btn-xs">complete</button>
Within the 'taskService', I have a method on click called - editTask(i) - I want to be able to pass the index of the array item
my class looks something like this:
export taskService {
editable = [];
editTask(i){
this.editable[i] == true;
}
}
I hope I have explained that well enough, let me know if you need more info!

You could provide it as an input:
<task-item [task]="task" [index]="i"></task-item>
And in your TaskItemComponent class:
#Component({
selector: 'task-item',
(...)
})
export class TaskItemComponent {
#Input()
index: number;
(...)
}
Edit
Since you want to use the index into the actions component, you also need to define it as input:
<task-actions [task]="task" [index]="index"></task-item>
And in your TaskItemComponent class:
#Component({
selector: 'task-actions',
(...)
})
export class TaskItemActionsComponent {
#Input()
index: number;
(...)
}

Related

apply function from methods to reverse string in paragraph in vue.js

Dears, I have tried to apply function to reverse string in paragraph text in vue.js,
I have created function to reverse words in methods called (reverseword) and added it card using :rule="reverseword()",but it does not work. your support is highly appreciated
Code:
<div class="post-box">
<span class="post-viwes">{{viwes}}</span>
<h3 class="post-title">{{title}}</h3>
<span class="post-date">{{date}}</span>
<p class="post-content">{{content}}</p>
<div class="row">
<div class = "col-sm-6 text-right">
<span class="post-author">{{author}} </span>
</div>
<div class = "col-sm-6 text-right" :rules="reverseword()">
<span class="post-category" >{{category.toUpperCase()}}</span>
</div>
</div>
)
</div>
</template>
<script>
export default {
props:["viwes","title","date","content","author","category"],
name:"posts",
methods: {
reverseWord: function () {
this.category = this.category.split('').reverse().join('')
}
}};
</script>```
Your reverseWord method attempts to mutate a prop (category).
You can't mutate a prop, because the mutation would be overwritten by the first update from the parent.
If/when you do want/need to change a prop value, you have do it in the parent component which will then pass the change down to the child component, through the props binding.
Updating the parent from child can be done by
either using $emit
or by using a store, external to both the child and the parent.
If, in fact, you don't want to mutate category, you just need to be able to use its reverse inside the template, create a computed property:
computed: {
reversedCategory() {
return this.category.split('').reverse().join('');
}
}
Use it in template as you would use a normal property:
<div class = "col-sm-6 text-right" :rules="reversedCategory">
<span class="post-category" >{{category.toUpperCase()}}</span>
</div>
The computed is reactive. Meaning every time category changes, reverseCategory will update accordingly.

Angular2+ whole DOM flicker on component load ngFor

When a user clicks an item it adds it to a list. To render the list i'm using an ngFor. After the user adds the first item to the selected list the whole screen/DOM flickers (everything disappears and reappears). This does not happen when the user then adds a second element to the selected array
Here is my ngFor loop:
<div
*ngFor="let sel of selected"
class="cw-selected-list-item"
>
<div class="cw-selected-name t4">{{ sel.id }}</div>
<app-checkbox class="cw-selected-r-tick"></app-checkbox>
<app-checkbox class="cw-selected-rw-tick"></app-checkbox>
</div>
When I comment out my app-checkbox components the flicker does not appear. Below is my app-checkbox component
TS
import { Component, OnInit, Input, Output, EventEmitter } from "#angular/core";
#Component({
selector: "app-checkbox",
templateUrl: "./checkbox.component.html",
styleUrls: ["./checkbox.component.scss"],
})
export class CheckboxComponent implements OnInit {
#Input() checked = false;
#Output() checkChanged = new EventEmitter();
constructor() {}
ngOnInit(): void {}
toggleChecked() {
this.checked = !this.checked;
this.checkChanged.emit(this.checked);
}
}
HTML
<div
class="checkbox clickable"
[ngClass]="{ 'checkbox-active': this.checked }"
(click)="toggleChecked()"
>
<img
class="checkbox-image"
[ngStyle]="{ opacity: !this.checked ? 0 : 1 }"
src="assets/buttons/tick.png"
/>
Any help would be much appreciated
EDIT
When the user clicks it simply call this function
selected = [];
public addToSelected(item: Document) {
this.selected.push(item);
}
HTML
<div
*ngFor="let hit of hits"
class="aisd-hit t4"
[ngClass]="{ 'hit-disabled': this.isAlreadySelected(hit) }"
(click)="
this.isAlreadySelected(hit) ? undefined : this.addToSelected(hit)
"
>
isAlreadySelected function
isAlreadySelected(doc: Document) {
return this.selected.includes(doc);
}
I found it!
It was importing my fonts locally through .woff2 files which was creating a full DOM refresh when a new component was created after view init.
Hope this helps someone
Example import:
url(/assets/fonts/opensans/mem8YaGs126MiZpBA-UFUZ0bbck.woff2)
format("woff2");

Can I search filter through a child component? (Angular)

I've got a parent component that I'm passing data down to a child component with *ngFor / #input. That child component is created x number of times depending on how many objects are in the pciData array
pciData is an array of about 700 data objects, all with a "hostname" property/value. when passed down to app-system, the hostname of that object shows on a button. I want the end user to be able to filter through those buttons by that hostname, only showing the app-system components that match.
On the parent component template, how would I create a search bar that can filter the app-system components by their hostname property values? In the code below, I've tried piping this like:
<ng-container *ngFor="let system of pciData | searchFilter: system.hostname | paginate: { itemsPerPage: 180, currentPage: p }; let i = index ">
but "system" comes back undefined. However, when I just type in {{system.hostname}} in the template under the loop, it does loop through every single object and display the hostname. Any assistance would be appreciated.
Here's the parent component. I've removed the implimentation that I tried to do with the filter to avoid confusion:
import { Observable } from 'rxjs';
import { Ipcidata } from '../Shared/Ipcidata';
import { map, filter, switchMap } from 'rxjs/operators';
import { Ng2SearchPipeModule } from 'ng2-search-filter';
#Component({
selector: 'app-system-status',
templateUrl: './system-status.component.html',
styleUrls: ['./system-status.component.css'],
})
export class SystemStatusComponent implements OnInit {
#Input() pciData: any;
constructor() {}
searchText;
p: number;
filteredValues : any;
ngOnInit() {
}
}
Here's the parent template, "app-system" is what I'm trying to search through the values of :
<ngx-spinner></ngx-spinner>
<section class="app-container">
<div class ="card-container">
<ng-container *ngFor="let system of pciData | paginate: { itemsPerPage: 180,
currentPage: p }; let i = index ">
<div class='cardboi'>
<app-system [systemInput]="system"></app-system>
</div>
</ng-container>
</div>
<div class="pagination-container">
<pagination-controls class='paginator' (pageChange)="p = $event"></pagination-controls>
</div>
</section>
lastly, here is the child template, to which the search bar filter should only show what the user inputs as the hostname. This is essentially just a button that pops up more data about that host when clicked. Again, the point of this is only to show the buttons with a certain hostname.
<button (click)="openDialog()" [ngClass]="{
'btn buttonGood':isValid(),
'btn buttonDateAlert':isValid()=='datewarning',
'btn buttonAlert':isValid()==false
}">{{systemInput.hostname.slice(0,13) | uppercase}}</button>
</div>
Thanks again for anyone who can help me with this.
In your *ngFor loop, the array you are looping though is being piped. The pipe has to be processed before the loop begins. So pciData is piped through the two pipes and then the resulting array is looped through. Which is why system doesn't exist at the time that the pipes are being processed.
My recommendation would be to not use a pipe for the search filters. Instead, create another pciData variable. You can call it something like filteredPciData. Bind to the onChange event of the search box in the parent component. When the search value changes, filter pciData (which should have all values) and use filteredPciData to store the results. Then in your HTML loop through filteredPciData instead of pciData

Facing issue while rendering index value of *ngFor directive in Angular 5

I am facing an issue while rendering index of *ngFor directive for a particular use case as follows.
Lets say we have an array of objects as
this.temp = [
{name:'John',age:24,visibility:'visible',
{name:'Kane',age:26,visibility:'hidden',
{name:'Ross',age:28,visibility:'visible',
{name:'Lui',age:21,visibility:'visible'
]
For rendering this in my app.component.html file I have html as follows
<div *ngFor="let user of temp; let i = index">
<div *ngIf="user.visibility === 'visible' ">
<div>{{i+1}}</div>
<div>{{user.name}}</div>
</div>
</div>
So as per the above array example, it renders users
1.John
2.Ross
3.Lui
Now there is a button name 'Change visibility' against each user in my UI, where in it will toggle the visibility state of user from 'hidden' to 'visible' and viceversa.
So clicking on button mentioned against John, it will set its visibility as
hidden but the UI rendered is
2.Ross
3.Lui
My expected output is
1.Ross
2.Lui
How to make the index render properly ?
The use case here is that I cannot modify/alter my this.temp array in terms of length.Basically I need the entire array with only visiblity property changed in it as per user actions.
Please help.
you can filter array first:
<div *ngFor="let user of temp.filter(us => us.visibility === 'visible'); let i = index">
<div>
<div>{{i+1}}</div>
<div>{{user.name}}</div>
</div>
</div>
like this way, you dont analize all array items too, more efficient and desired output.
Cheers!
You can also achieve your required result by using Pipe like this
HTML component
<div *ngFor="let user of temp | visiblefilter ; let i=index">
<span>{{i+1}} {{user.name}}</span> <button name={{user.name}} (click)="onHide($event)">Hide</button>
</div>
PIPE
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'visiblefilter',
pure: false
})
export class VisibleFilterPipe implements PipeTransform {
transform(items: any[]): any {
return items.filter(({visibility}) =>visibility=='visible');
}
}
You can check here with working example stackblitz
use a custom trackby function :
*ngFor="let user of temp; let i = index; trackBy: customTB"
customTB(index, item) {
return index + ' - ' item.name;
}

Changing the attribute of each element in a v-for array

I am attempting to figure things out with Vue way of writing javascript.
Considering this scenario:
at .vue template
<button v-on:click="biggerFont()" class="btn btn-s btn-default" type="button" name="button">A</button>
<div v-for="(tweet, index) in tweets">
<div class="each_tweet">
<textarea v-on:keyup="typing(index)"
v-model="tweet.content"
placeholder="What's happening now">{{ tweet.content }}</textarea>
</div>
</div>
at .vue <script>
export default {
methods: {
biggerFont: function() {
//can I somehow use this.tweets.style.fontSize to influence all the fontSize?
document.getElementsByClassName("each_tweet").style.fontSize = "xx-large"
}
}
}
My question is:
how do I go about changing the font-size of each value of the textarea in tweets? Is there a default Vue way of influencing these fontSize?
I tried and failed using document.getElementsByClassName.style.fontSize and it does not seems to be the Vue way. Thank you!
I believe the Vue way of doing this is using class and style bindings. For example:
<textarea v-bind:class="{ big: enlarge}">{{item}}</textarea>
https://jsfiddle.net/e064m859/
document.getElementsByClassName method returns a nodeList so you have to access a DOM element using its index.
export default {
methods: {
biggerFont: function() {
var tweets=document.getElementsByClassName("each_tweet");
for(var i=0;i<tweets.length;i++){
tweets[i].style.fontSize = "xx-large";
}
}
}
}

Categories