Angular - Displaying array as list in frontend - arrays

I'm working on my web-app and I am facing a problem.
I have an array with several values, which I'd like to display in the frontend as list or something similar.
app.component.ts
in this function I split the tags from the string into an array
splitTags() {
if (this.data.tags != null) {
var tag = this.data.tags.split(";")
console.log(tag)
}
}
ngOnInit() {
this.splitTags()
}
app.component.html
here I d'like to display the tags in a list
<li *ngFor="let tag in tags">
{{ tag }}
</li>
but nothing appears, also if I see the values in the console.

you need to create a property to hold the split result
tags:any[]; // 1️⃣
splitTags() {
if (this.data.tags != null) {
this.tags = this.data.tags.split(";"); // 2️⃣
console.log(this.tags)
}
}
ngOnInit() {
this.splitTags()
}
template
<li *ngFor="let tag of tags">
{{ tag }}
</li>

Related

Can't filter aync data on mounted vuejs

I'm working on a vuejs/(vuex) for state management/firebase project of posts.
So I have a firestore collection of posts (array of objects that have name id owner content and timestamp for creation ...)
I'm retrieving that data by using the onSnapshot methode and it's stored on blogPosts variable... and we show theme all, when a user want to visit a single post it will redirect him to the route of the single post (..../view-post/postid) and i filter that array using the id of the post to have an array of one element (which is the post he visited)
when the filter complete i got all the data and i fill theme on the template like these
<template>
<NavBarView />
<section class="preview" v-if="currentBlog">
<h2>{{ currentBlog[0].blogTitle }}</h2>
<p>Posted on: <span>{{ dateFormat() }}</span> <span v-if="currentBlog[0].editTime">(Last edit:
{{ editFormat() }})</span></p>
<img class="blogCover" :src="currentBlog[0].blogCoverFileURL" :alt="currentBlog[0].blogCoverPhoto">
<div class="html-blog" v-html="currentBlog[0].blogHTML"></div>
<hr />
</section>
</template>
<script>
export default {
data() {
return {
currentBlog: null
}
},
mounted: {
this.currentBlog = this.blogPosts.filter((post) => {
return post.blogID == this.$route.params.id
})
},
computed: {
...mapState(['blogPosts'])
}
//note that i have exported all the requirements such as mapState and firebase functions ... didn't write theme here
}
</script>
now the problem is that the filter is occurring before the data are fetched from firebase and i can't handle that so it's always returning can't read property of null (currentBlog[0])
i found a solution which is sending a getDoc request to firebase but it's a bad idea, (why i send request and i have all the posts here so i can filter it directly and get the specific post) which didn't work!!!!!
any solution??
Looking at your template, I'm under the impression currentBlog should not be an array, as you don't ever have more than 1 element in that array (you're filtering by blogID, which I'm guessing is a unique identifier). You need to .find() the blog entry, not .filter() it:
computed: {
blogID() {
return this.$route.params.id;
},
currentBlog() {
return this.$store.state.blogPosts.find((b) => b.blogID === this.blogID);
}
}
Note: if state.blogPosts does not start as an empty array (as it should), you might want to use:
currentBlog() {
return (this.$store.state.blogPosts || []).find(
(b) => b.blogID === this.blogID
);
}
And now replace all currentBlog[0]s with currentBlog:
<template>
<NavBarView />
<section class="preview" v-if="currentBlog">
<h2>{{ currentBlog.blogTitle }}</h2>
<p>
Posted on: <span>{{ dateFormat() }}</span>
<span v-if="currentBlog.editTime">(Last edit: {{ editFormat() }})</span>
</p>
<img
class="blogCover"
:src="currentBlog.blogCoverFileURL"
:alt="currentBlog.blogCoverPhoto"
/>
<div class="html-blog" v-html="currentBlog.blogHTML"></div>
<hr />
</section>
</template>
Important: make sure in every single method/computed/watch in the script part of the component, if using currentBlog, you're first checking if it's not falsy. Generic example:
computed: {
blogHTML() {
return this.currentBlog?.blogHTML
/* shorthand for:
* return this.currentBlog && currentBlog.blogHTML
*/
}
}
I would suggest that rather than trying to do this operation once on mounted that you instead re-run the filter every time that state.blogPosts is updated. You can easily do this through a computed property. See below:
export default {
data() {
return {
}
},
computed: {
...mapState(['blogPosts']),
currentBlog(){
const posts = this.$store.state.blogPosts;
if(Array.isArray(posts)) {
return posts.filter((post) => {
return post.blogID == this.$route.params.id
});
} else {
return null
}
}
}
}

Is there is any possibilty for binding array in angular

Service module:
baseurl: string = 'https://fireman-7cc06-default-rtdb.firebaseio.com/'
getFunction(): Observable<object> {
return this.http.get(this.baseurl + 'items.json')
}
Component module:
ngOnInit(): void {
this.itemservices.getFunction().subscribe(data => {
this.listItems= data
console.log(this.listItems)
}, err => {
console.log('error' + err)
})
}
View:
<div *ngIf="listItems">
<ul *ngFor="let info of listItems">
<li>{{info.Item_name}}</li>
</ul>
</div>
As the error indicates *ngFor could only loop through iterables like an array. However your API returns an object. Ideally any data conversions must be done in the backend. However, if you so desire, you have the following options in the frontend.
Option 1: convert object to array
You could use JS Object.values() with RxJS map operator to fetch the values of the object as an array.
const input = {"-MhJ49AIHh-53nnF07a1":{"Item_color":"white","Item_name":"amma","Item_price":"369","Item_type":"human"},"-MhJXlVgPBdZ6Q7L0Tnn":{"Item_color":"red","Item_name":"manu","Item_price":"1000","Item_type":"sunny"},"-MhJa9Xzmdq9OvkZyVN7":{"Item_color":"yellow","Item_name":"chinnu","Item_price":"500000","Item_type":"sunny"}}
console.log(Object.values(input))
.as-console-wrapper { max-height: 100% !important; top: 0; }
import { map } from 'rxjs/operators';
baseurl: string = 'https://fireman-7cc06-default-rtdb.firebaseio.com/'
getFunction(): Observable<object> {
return this.http.get(this.baseurl + 'items.json').pipe(
map((res: any) => Object.values(res))
);
}
Option 2: use Angular keyvalue pipe
You could skip the conversion in using Object.values() and use the keyvalue pipe in component template to iterate through an object.
Service
baseurl: string = 'https://fireman-7cc06-default-rtdb.firebaseio.com/'
getFunction(): Observable<object> {
return this.http.get(this.baseurl + 'items.json')
}
Template (*.html)
<div *ngIf="listItems">
<ul *ngFor="let info of listItems | keyvalue">
<li>{{info?.value?.Item_name}}</li>
</ul>
</div>
The problem here is you are trying to loop through an object instead of array. Please check how you define this.listItems.
Declare it as array and assign the values accordingly. Then you should be able to loop through in html code.
listItems = [];

Angular - show hidden values from array list via button

I'm working on a list of values in my web-app. There I'm facing the following problem:
app.component.html
This part of the code shows my array as a list:
<div class="body__tags">
<ng-container *ngFor="let tag of tags; let i=index">
<li class="tags__list" *ngIf="i<5" [label]="tag"></li>
</ng-container>
</div>
Here I set limit of 5 values in my list. But sometimes there are more than 5 values in the array. How can I add a button "show all" and then display everything in that list?
app.component.ts
This is my ts code:
tags:any[];
splitTags() {
if (this.data.tags != null) {
this.tags = this.data.tags.split(";");
console.log(this.tags)
}
}
ngOnInit() {
this.splitTags()
}
Using *ngIf is not the best idea for such scenario.
You should slice the array upto 5 and store into another variable.
once User clicks on the button to show all the data, assign the entire array to the new variable.
for Example :
this.data = [1,2,3,4,5,6,7,8,9,10];
this.newData = data.slice(0,5);
then in the component.html file : ;
<div class="body__tags">
<ng-container *ngFor="let tag of newData; let i=index">
<li class="tags__list" [label]="tag"></li>
</ng-container>
</div>
on button click you should assign entire data to newData variable.
public showAll(){
this.newData = this.data;
}
Working demo
you can keep 5 in some variable and then on click of 'ShowAll()' you can increase the limit

nested Filter json Using Checkboxes with AngularJS

I had a Json data I need to create a nested filter search in angular. If you guys can help me since I am new to this I tried but I find difficulty.
I dont have your css but at high level it should be like THIS and for multilevel filter refer THIS
On click of checkbox i am adding filter category to one of my array using addToFilter function.
HTML
<input type="checkbox" ng-checked="item.checked" ng-model="item.checked" ng-click="addToFilter(item.node.category)"/> {{ item.node.category }}
HTML
code filter:categoryFilter will filter records from array according to selected category.
<div ng-repeat="item in nodes | filter:categoryFilter | orderBy:'node.location' | groupBy:['node.location'] ">
<h2 ng-show="item.group_by_CHANGED">{{item.node.location}}</h2>
<ul>
<li>{{item.node.title}}</li>
</ul></div>
JS
This js code is to add selected category from array when checked and remove when unchecked.
$scope.filtersApplied =[];
$scope.addToFilter = function(category)
{
var i = $.inArray(category.trim(), $scope.filtersApplied);
console.log(category);
if (i > -1) {
$scope.filtersApplied.splice(i, 1);
} else {
$scope.filtersApplied.push(category.trim());
}
}
$scope.categoryFilter = function(node) {
if ($scope.filtersApplied.length > 0) {
if ($.inArray(node.node.category.trim(), $scope.filtersApplied) < 0)
return;
}
return node;
}
** Please ignore my grouping code. I just want to simulate what you shown in image.
Updated SAMPLE with CSS

AngularJS ng-repeat filter, function over object property

I am looking for a way to check for each movie if the movie has the category which is selected. Movies is an array which contains objects, those objects have some properties, like you can see in the code below. The categories property is a array of categories where the movie is in. Now there is a variable selectedCategories where the current selected category is stored in.
I don't want to use custom filters, because I think it has te be possible with this one, I just can't quite get it. In the javascript function there can't be changed too much either.
If the return of hasSelectedCategory is true, then it has to execute the block, if false not.
Thanks in advance.
//in the javascript file
scope.hasSelectedCategory = function(categories){
var hasCategory = false;
if (categories.indexOf(scope.selectedCategory) !== -1){
hasCategory = true;
}
return hasCategory;
};
//in the html file
<div class="movieListItem {{listItemView}}" ng-repeat="movie in movies | filter:{hasSelectedCategory(categories): true}">
<h4>{{movie.title}}</h4>
<a href="http://www.youtube.com/embed/{{movie.youtubeId}}?rel=0&autoplay=1">
<img ng-src="{{findPosterSource(movie)}}" class="poster"> </a>
<p ng-hide="listItemView === 'grid'">
{{movie.description}}
</p>
<div class="categories" >
<span ng-repeat="category in movie.categories"> <a ng-href="#">{{category}}</a> </span>
</div>
</div>
You have to use the filter like this:
ng-repeat="movie in movies | filter:hasSelectedCategory"
The hasSelectedCategory function will be invoked for each movie in the movies list. In order to filter by selected categories you can use a function like this:
$scope.hasSelectedCategory = function(movie) {
var hasCategory = false;
angular.forEach($scope.selectedCategories, function(selectedCategory) {
if (!hasCategory && movie.categories.indexOf(selectedCategory) !== -1) {
hasCategory = true;
}
});
return hasCategory;
};
Demo (plunker)

Resources