How can I show comments of clicked feed only - javascript

I'm stuck in a situation, in a nested loop. The top loop iterating over the Feeds array and every feed in Feeds have comments array. This comments is used for inner loop.
My problem is, I have Comment button and on click of that button, I want to show the list of comments only for that feed post. But instead of showing comments on current feed post, it is showing comments for all other feeds also, that's because of use of only one Boolean variable showComments created inside .ts file.
So how can make that work for only the given context Feed post?
HTML
<ng-container *ngFor="let feed of feeds">
<div class="ed-card ed-card-feed">
<div class="ed-card-feed--footer">
<div class="card-feed-values">
<a class="mr-4"><span>{{feed.likes_count}}</span> Likes</a>
<a><span>{{feed.comments_count}}</span> Comments</a>
</div>
<div class="card-feed-actions">
<a class="mr-5" (click)="likePost(feed.id)"><span></span> Like</a>
<span></span> Comment button
</div>
</div>
// Below div needs to be open for that particular feed only
<div *ngIf="showComments" class="ed-card-feed--comments">
<div class="feed-comments-list">
<ng-container *ngFor="let comment of feed.comments?.edges">
<div class="comment-list-item">
<p class="comment-box mt-1">
{{comment.node?.body}}
</p>
</div>
</ng-container>
</div>
</div>
</div>
</ng-container>
P.S. - Please don't confuse with feed.comments?.edges that's just because of GraphQL. It is just an array.
Thanx

You can maintain an array of showComments[] equal to size of feeds with initially set to false. showComments[false,false...]
<ng-container *ngFor="let feed of feeds; let myIndex = index"">
//code here
<span></span> Comment button
//check if true
<div *ngIf="showComments[myindex] === true" class="ed-card-feed--comments">
</ng-container>

Instead of a boolean, you can define a selectedFeeds array:
selectedFeeds = new Array<Feed>();
In the template, you add the feed that has been clicked to selectedFeeds:
<a href="javascript:void(0)" class="mr-5" (click)="selectedFeeds.push(feed)>Comment button</a>
and you filter the comments according to the selected feeds:
<div *ngIf="selectedFeeds.indexOf(feed) >= 0" class="ed-card-feed--comments">

The best way to achieve this is to separate the code to different components. And since each of the component will have it's own instance of comments - you won't have a problem creating a local variable and showing/displaying the comments in there.
I've created an example of how it can be implemented: stackblitz

Another approach
<ng-container *ngFor="let feed of feeds">
Comment button
<div *ngIf="feed.showComments" class="ed-card-feed--comments">
</ng-container>
Js file
toggleComments(feed) {
feed.showComments = true // (feed.showComments = !feed.showComments => to show or hide (toggle))
}

Related

How to name dynamically a reference in a div on Angular template inside a loop

I'm developing a project using Angular 8.3.
I have a loop (using *ngFor) and I need that every div that I add has an unique reference name created dinamically).
This is my example code:
<div *ngFor="let item of list; let i = index;">
<div
cdkDropList
#done0="cdkDropList"></div>
</div>
Below I have another drag and drop zone that must receive these elements:
<div
cdkDropList
#donesList="cdkDropList"
[cdkDropListData]="DonesList"
[cdkDropListConnectedTo]="[done0, done1]"
class="movie-list"
(cdkDropListDropped)="onDrop($event)">
<div *ngFor="let itemList of DonesList" cdkDrag>{{itemList}}</div>
</div>
I need to change dynamically #done0, e.g. (#done0, #done1, #done2, #done3, etc).
How can I do it? I have tryed using #done{{i}}} but it doesn't work.
I don't know if I can use trackBy in this case and how to apply it.
Many thanks,
Reference a container:
<div *ngFor="let item of list; let i = index;" #doneContainer>
<div cdkDropList></div>
</div>
and in .ts you have different references in a NodeList:
const list = this.doneContainer.childNodes
or in a more Angular way, which is better, as it will update when necessary:
#ViewChildren('doneContainer') list: QueryList<any>;

ng-repeat item reference is showing up differently in angular ui router link

I have a ng-repeat for an array of message objects, which prints out message.text and is accompanied by message.author.username. When I use {{message.author.username}}, it prints out the correct user, but I have that DOM element wrapped in an anchor, where ui_sref=profile(message.author.username) is set as the link.
For some reason the ui-sref link's reference to the username is presenting something totally different than what it's wrapped around. Like, when I hover over the linked username on the site, it shows that the link goes to a different username found later in the array. If it's using the same exact syntax (message.author.username) is there any reason for the text to be different when listed in an ui-router anchor tag? I feel like it's a bug but I'm not sure why and how to fix.
Hopefully that made sense... here's some code to be clear
<div class="col-sm-9 offset-sm-1"
ng-repeat="message in wall | orderBy: createdon:true | limitTo: 8">
<div class="comment-container">
<div >
<h6> <a ui-sref="profile(message.author.username)">{{message.author.username}}</a></h6>
</div>
<div class="comment-text">
{{message.text}}
</div>
<hr>
</div>
</div>

If all array items are hidden show another div in AngularJS

I'm using ng-repeat on my page. ng-class working very well.
<div class="card news" ng-repeat="item in news track by $index" id="{{news.nid}}" ng-init="parentIndex = $index" ng-class="{hidden: '{{getCheck($index)}}' == 'true'}">
...
</div>
Now I need, if all items are hidden, show this div:
<h3 class="news-empty">No news</h3>
Whats the rules? How can I do it? Thanks.
You need another method that checks if all elements are hidden:
$scope.everythingIsHidden = function() {
return $scope.news.every((new, index) => $scope.getCheck(index));
}
$scope.getCheck = function(index) { // Your getChek function that I suppose it checks if an element is hidden based on index
//...
}
<h3 class="news-empty" ng-if="everythingIsHidden()">No news</h3>
TheCog's answer will work. If you want to do this in a more 'Angular' way you're going to need to refactor what you have.
You shouldn't be trying to hide them with a CSS class. ngRepeats have a built in filter syntax. So, you should be filtering them out.
<div class="card news"
ng-repeat="item in news | filterMethod as results track by $index"
id="{{news.nid}}"
ng-init="parentIndex = $index"
>
<h3 class="news-empty" ng-if="results.length === 0" >No news</h3>
The as results statement in the repeat will store the filtered array in results. filterMethodneeds to be an angular filter and it will probably work similarly to your getCheck($index) method.
You want to add an ngShow to the h3 tag, and aim it at a function you write in your controller that checks if the array is empty, probably by iterating over the same array that's hidden and running getCheck($index) on each.

extract JSON data in Angular2

I have the following JSON output: http://pastebin.com/9bHaBrbX
I would like to make this pseudocode a reality:
For every Model {
For every Year {
For every Style {
if submodel.body is NOT in Array:
load submodel.body to Array;
}
}
}
I would like to store this Array in my model.component so I can use it to make buttons such as:
<button *ngFor="let item in Array" ...> item </button>
How can I extract the data I want (Model[x].Years[y].Style[z].submodel.body) (where x,y,z are variables) from my json response?
I am having trouble conceptualizing how to iterate through nested json objects with JS/TS/Angular2.
Here is a hint :
<div *ngFor="let years of model.years">
<div *ngFor="let year of years">
<div *ngFor="let styles of year.styles">
<div *ngFor="let style of styles">
<!-- your conditions, submodel.body... -->
</div>
</div>
</div>
</div>
Side note: I agree with others above, you should try to write your code yourself.
Use simple http request, it will download the data and store it inside the items variable. Then you can do whatever you want with it - display, sort or just keep.
https://plnkr.co/edit/bU7W6f0aO6eelvyCnKzc?p=preview

How to get consolidated results from ng-repeat?

See this plunker.
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
You are now seeing details of <span ng-init="subCats = subCats + ' ' + subCategory.name">{{subCats}}</span>
</div>
This HTML page shows a filtered result from an object. However, I want to display a consolidated result of the names after "You are now seeing details of" like for example, "You are now seeing details of jim tom". This consolidated list should appear after the element which has ng-repeat directive.
How can this be done?
Thanks
I made an updated plunker for you.
Please try to make your example plunker way more reduced to the specific problem in the future as this helps us to help you.
First I added the search binding as filter to the ng-repeat to make the filter workable:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | filter:{id:search} | orderBy:'id'">
To avoid executing the filter twice you can save the filter result directly into a scope variable by simply assinging it (in my example to subCategorysFilter):
<div ng-repeat="subCategory in subCategorysFilter = (subCategorys | filter:{tags:tag}:true | filter:{id:search} | orderBy:'id')">
I further changed your getAllFilteredNames() method to take a filter object as argument and made it loop through the results, build an array of the names and join them with a , as separation:
$scope.getAllFilteredNames = function(filter){
var names = [];
angular.forEach(filter, function(element){
names.push(element.name);
});
return names.join(", ");
};
This is now called outside the ng-repeat directive:
You are now seeing details of {{getAllFilteredNames(subCategorysFilter)}}
Have fun!
Update
Two possible solutions for getting a multilined output:
1 - You might change the line
<div>You are now seeing details of {{getAllFilteredNames(subCategorysFilter)}}</div>
to
<div>You are now seeing details of <span ng-bind-html="getAllFilteredNames(subCategorysFilter)"></span></div>
Then any html tags within the expression are compiled as html code. But there are meaningful reasons for angular disabling this feature by default. If your objects are editable by users you need to prevent them from breaking your design by escaping all html tags...
2 - But if you do not need to display the cosolidated information within a single string, you might simply use another ng-repeat combined with an <ul> like this:
<div>You are now seeing details of <br/>
<ul>
<li ng-repeat="subCategory in subCategorysFilter">{{subCategoryName}}</li>
</ul>
</div>
Just style your li accordingly to be displayed underneath each other and you're ready to go.
You can do this in your HTML by moving your consolidated list outside of the ngRepeat and calling the filter again:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
</div>
<div>
You are now seeing details of
<span ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.name}}
</span>
</div>
The drawback to this approach is that you are calling the filter twice. A better alternative would be to set up a $watch in your parent controller and invoke the $filter manually. I.e. Save the filtered results in a scope variable. The benefit is that the filter is called half as many times and the scope variables you set up are visible to the original list and the consolidated list.
app.controller('ParentController', function($scope, $filter) {
$scope.subCategorys = [{...}];
$scope.tag = {...};
$scope.$watchCollection('subCategorys', function(newList){
//if the collection changes, create a new tag
//reference that is a copy of the old one to trigger
//the tag watch listener
if (newList)
$scope.tag = angular.copy($scope.tag);
});
$scope.$watch('tag', function(newTag){
// if tag changes, apply the filter,
// and save the result to a scope variable
if(newTag)
$scope.filteredList = $filter('filter')
($scope.subCategories, { tags: newTag}, true);
});
});
HTML
<div ng-controller="ParentController">
<div ng-repeat="subCategory in filteredList | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
</div>
<div>
You are now seeing details of
<span ng-repeat="subCategory in filteredList | orderBy:'id'">
{{subCategory.name}}
</span>
</div>
</div>
I'm afraid there is no way of doing that except for selecting the subCategory back. Fortunately, there is a pretty elegant 'angular' way of doing that. Add this to your controller:
$scope.getSubCatById = function(someId) {
return $filter('filter')($scope.subCategorys, {id:someId})[0];
}
And then your html:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
You are now seeing details of {{ getSubCatById(2).name }}
</div>
I hope I interpreted your question correctly.

Categories

Resources