I am using Angularfire2 v5. In my database I have two lists one is cartand other one is product. In cart list there is a field which contains product id and now I want a Observable that contains data for both the cart and it's product. Currently I am trying with this way
cart: Observable<any>;
constructor(public db: AngularFireDatabase) {}
ionViewDidLoad() {
this.cart = this.db.list(`/cart`)
.snapshotChanges()
.map(changes => {
return changes.map(c => {
let productObservables:Observable<any> = this.db
.list(`product/${c.key}`).snapshotChanges()
.map(m=>{
m.map(p=>({
key:c.payload.key, ...p.payload.val()
}));
return m;
});
return productObservables;
})
});
now in html using this
<ion-item *ngFor="let item of cart|async">
<ion-label>
{{item.key}}
{{item.name}}
</ion-label>
but it shows me null. How can I display both data the cart and product list in one ngFor async
Say you had a structure like this:
cart:
yyy:
productId: aaa
qty: 2
zzz:
productId: bbb
qty: 1
products:
aaa:
name: AAA
bbb:
name: BBB
Then you could map it against the other part of the db, since you already have the key I'm just using valueChanges() here.
this.cart = this.db.list(`/cart`)
.snapshotChanges()
.map(changes =>
changes.map(c => ({
key: c.payload.key,
product: this.db.object(`products/${c.payload.val().productId}`).valueChanges(),
...p.payload.val()
}))
);
The product will then be async, so you'd need to pipe that in your view too:
<ion-item *ngFor="let item of cart | async">
<ion-label *ngIf="item.product | async; let product; else loading">
{{product.name}} x {{item.qty}} {{item.key}}
</ion-label>
<ion-item>
<ng-template #loading><ion-spinner></ion-spinner></ng-template>
Related
I have a search bar as a globalfilter but I want to know if in react-table v7 it is possible to have multivalue in it for example.
I want to search words and it search in all my columns not the exact string but each word in each column.
This is my search input
const TableSearchFilter = ({ preGlobalFilteredRows, globalFilter, setGlobalFilter }) => {
return (<Input
value={globalFilter || ""}
onKeyDown={(e) => {
if (e.keyCode ==13) {
e.preventDefault()
}
}}
onChange={(e) => {
setGlobalFilter(e.target.value)
}}
startAdornment={<i className="fas fa-search" style={{ marginRight:'10px' }}></i>}
/>)
}
I saved the timestamp as a data field inside firebase DB, this way I can now retrieve it like {{ post.timestamp }} to display it, how would I go from where I am at to order the posts by timestamp order regardless of the user object order, for example, what I get in the UI is the posts ordered by the user and not by time.
data on firebase looks like this:
Code looks like this :
<template>
<div>
<div
v-for="post in allPosts.slice().reverse()"
:key="post._key">
<v-card class=" feed-card my-3">
<v-row no-gutters>
<v-col cols="1">
<v-img
class="align-center rounded-xl "
width="30"
:src="post.photoURL">
</v-img>
</v-col>
<v-col cols="10">
<p class="">{{post.postText}}</p>
<p class="blue-grey--text ">{{post.displayName}}</p>
<p class="mb-n1 mt-n5 d-flex flex-row-reverse"> {{post.date}} {{post.time}}</p>
</v-col>
</v-row>
</v-card>
</div>
</div>
</template>
<script>
import firebase from '#/plugins/firebase'
let db = firebase.database();
//let usersRef = db.ref('users');
let postRef = db.ref('posts');
export default {
name: 'FintechSocialFeed',
data: () => ({
authUser: null,
allPosts: [] // initialise an array
}),
methods: {
},
created: function() {
data => console.log(data.user, data.credential.accessToken)
firebase.auth().onAuthStateChanged(user => {
if (user) {
postRef.on('value', snapshot => {
const val = snapshot.val()
if (val) {
this.allPosts = Object.values(val).flatMap(posts =>
Object.entries(posts).map(([ _key, post ]) => ({ _key, ...post})))
}
console.log(snapshot.val())
});
}
})
}
}
</script>
here is the UI showing the latest post at the bottom because it is sorting by the user and not date:
I don't use firebase, but it looks like db reference provides orderByKey, so...
let postRef = db.ref('posts').orderByKey('timestamp');
An alternative would be sorting yourself, after retrieval...
this.allPosts = Object.values(val).flatMap(posts =>
Object.entries(posts).map(([ _key, post ]) => ({ _key, ...post}))
).sort((a, b) => a.timestamp.toMillis() - b.timestamp.toMillis());
I have a profile page that displays the user info. The page shows the user name / email and a button to create a list.
I can also edit the name and email correctly, and it reflects in the firebase instantaneously. Ok. I get the user data and I can edit it.
What I'm trying to do now is to show the lists that the user has created.
Look, this user has created one list, and what is returned to me is that he doesn't have lists.
I'll try to shorten the code as much as possible:
<script>
imports.....
import { db } from '../../firebase.config.js'
let listings = []
let auth = getAuth()
// fetch the user's listings
const fetchUserListings = async () => {
const listingsRef = collection(db, 'listings')
const q = query(
listingsRef,
where('userRef', '==', auth.currentUser.uid),
orderBy('timestamp', 'desc')
)
const querySnap = await getDocs(q)
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
}
fetchUserListings()
</script>
<!-- display the user's listings -->
<div>
{#if listings.length > 0}
<p class="listingText">My lists</p>
{#each listings as listing}
<ListingItem listing={listing.data} id={listing.id} />
{/each}
{:else}
<p class="noListings">You have no lists</p>
{/if}
</div>
My ListItem component:
<script>
export let listing
export let id
export let handleDelete
import DeleteIcon from '../../static/assets/svg/deleteIcon.svg'
</script>
<li class="categoryListing">
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
<img src={listing.imgUrls[0]} alt={listing.name} class="categoryListingImg" />
<div class="categoryListingDetails">
<p class="categoryListingLocation">
{listing.location}
</p>
<p class="CategoryListingName">
{listing.name}
</p>
<p class="categoryListingPrice">
${listing.offer ? listing.discountedPrice : listing.regularPrice}
{listing.type === 'rent' ? '/ por mês' : ''}
</p>
<div class="categoryListingInfoDiv">
<img src="/assets/svg/bedIcon.svg" alt="cama" />
<p class="categoryListingInfoText">
{listing.bedrooms > 1 ? `${listing.bedrooms} camas` : `${listing.bedrooms} cama`}
</p>
<img src="/assets/svg/bathtubIcon.svg" alt="banheiro" />
<p class="categoryListingInfoText">
{listing.bathrooms > 1
? `${listing.bathrooms} banheiros`
: `${listing.bathrooms} banheiro`}
</p>
</div>
</div>
</a>
{#if handleDelete}
<DeleteIcon
class="removeIcon"
fill="rgb(231, 76, 60)"
onClick={() => {
handleDelete(listing.id, listing.name)
}}
/>
{/if}
</li>
Just when you think you've reached the simplest part, it's still tough.
Update:
I think that the problem is in firebase. The "docs" are empty:
Now I am in serious trouble!
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
I see two things here. The less important: The .forEach() method returns undefined, so the return is redundant. The more important: the .push() alone won't automatically trigger updates. Have a look at this section in the Docs
Did you try logging listings? I assume the data is there, it's just not displayed, so I propose to change this part to
querySnap.forEach((doc) => {
listings = [...listings, {
id: doc.id,
data: doc.data()
}]
})
or
querySnap.forEach((doc) => {
listings.push({
id: doc.id,
data: doc.data()
})
listings = listings
})
I have array of items which are rendered on UI as angular material cards. I have a search box where user input for items. once the search is implemented I get the searchItems as another array. Now I want to highlight (changing the background color of the card or a rectangular animation) items array angular material cards which are matched with searched items. I was stuck at implementing this css part although I am able to render and match the items with searchdItems
<div *ngIf="searchedItems">
<div class="alert alert-danger alert-dismissible" *ngIf="searchedItems.length === 0">
×
<strong>{{data.value}}</strong> not found
</div>
<div *ngIf="searchedItems.length > 0" class='searchitem'>
{{data.value}} found in
<div *ngFor="let item of searchedItems; let i = index">
{{item}} {{i}}
</div>
</div>
</div>
<mat-grid-list cols="3" rowHeight="100px">
<div *ngFor="let item of items; let x = index">
<mat-grid-tile [ngClass]="item.name == item ? 'searchexample-card': 'example-card'" routerLink="/inventory/items/{{item.name}}">
<mat-card>
<mat-card-header>
<b>Item{{item.name}}</b>
</mat-card-header>
</mat-card>
</mat-grid-tile>
</div>
</mat-grid-list>
If you are unable to change the items array I have modified the answer for the pipe which will not impact your original array
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'highlight', pure: false
})
export class HighlightPipe implements PipeTransform {
transform(items: any, filtered?: any[]): any {
let newArray = [];
if(Array.isArray(items) && Array.isArray(filtered)) {
for(let item of items){
newArray.push({label: item, highlight: filtered.indexOf(item)>-1})
}
} else {
newArray = items.map(item => ({label: item}));
}
console.log(newArray)
return newArray;
}
}
But your html template will be modified to accommodate the highlight
<div *ngFor="let item of items | highlight: searchedItems" [ngClass]="{card: true, highlight: item.highlight}">
{{item.label}}
</div>
Here I am passing the array that you have after you search searchedItems to my highlight Pipe
Hope this answers your question
Updated Stackblitz: https://stackblitz.com/edit/angular-oyhva7
The problem is with the condition that you have implemented for activating [ngClass]. You are comparing an object with object property, this comparison for item with item.name will always return false.
consider you have the searched result in searchedItems[] array. Now you want to highlight those items in the loop of whole items. then your code should be something like this
<div *ngFor="let item of items; let x = index">
<mat-grid-tile [ngClass]="item in searchedItems ? 'searchexample-card': 'example-card'">
<mat-card>
<mat-card-header>
<b>Item{{item.name}}</b>
</mat-card-header>
</mat-card>
</mat-grid-tile>
</div>
Considering your items array and searchedItems[] is of same type.
You can do it using a pipe from Angular
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'highlight',
pure: false
})
export class HighlightPipe implements PipeTransform {
transform(items: any, search: string, clip: boolean): any {
const regex = new RegExp(search, 'ig');
if(Array.isArray(items) && search) {
for(let item of items) {
item.highlight = regex.test(item.label);
}
}
return clip ? items.filter(item => item.highlight) : items;
}
}
I have passed in the search term in the pipe and the full array always returning from the pipe, but using a regular expression or maybe your custom logic of search add another attribute to the list item highlight boolean which is true if your logic says it matches the item, else false
I have modified the items array from string[] to {label:string, highlight?:boolean}[] to accommodate the pipe change so instead we are using the array as
items: {label:string, highlight?:boolean}[] = [
{label: 'INDIA'},
{label: 'USA'},
{label: 'RUSSIA'},
{label: 'UK'},
{label: 'ITALY'},
];
Now to all put it into the template
<div
*ngFor="let item of items | highlight: search"
[ngClass]="{highlight: item.highlight, card: true}"
>
{{item.label}}
</div>
Here when you search, if it matches with any of the item from items array will have a highlight:true attribute as true and also adds a class called 'highlight` to your existing div.card so in css defining the highlight card as
.card.highlight{
background-color: yellow;
}
Notice that we are sending the search term to the pipe that we created as an argument, so we have to define the variable as a class level variable which is set when we click search in your method devicesearch which you are already calling.
search: string;
devicesearch(input) {
this.search = input;
}
Edit 2:
If you wanted the clipped array you can use the Pipe in your class to get the clipped array
search: string;
searchedItems: any[];
highlightPipe = new HighlightPipe();
devicesearch(input) {
this.search = input;
this.searchedItems = this.highlightPipe.transform(this.items, input, true);
// only names --> const names = this.searchedItems.map(item => item.label);
}
Updated StackBlitz for your reference: https://stackblitz.com/edit/angular-usswkn
I load a list of items from the Firestore and put them in the ion select, the problem is that as each item has an id I need to get the id of the selection.
This is my code to get the items in the controller.
this.MesasColeccion = this.mesasser.getMesaListfromFirestore();
this.MesasColeccion.snapshotChanges().subscribe(mesasLista => {
this.mesas = mesasLista.map(mesa => {
return {
nombre: mesa.payload.doc.data().nombre,
capacidad: mesa.payload.doc.data().capacidad,
estado: mesa.payload.doc.data().estado,
union:mesa.payload.doc.data().union,
id: mesa.payload.doc.id
}
});
this.mesas2 = mesasLista.map(mesa2 => {
return {
nombre: mesa2.payload.doc.data().nombre,
capacidad: mesa2.payload.doc.data().capacidad,
estado: mesa2.payload.doc.data().estado,
union:mesa2.payload.doc.data().union,
id: mesa2.payload.doc.id
}
});
});
this.MesasObservable = this.MesasColeccion.valueChanges();
This is the code to show the list in the ion-select:
<ion-card>
<ion-card-content>
<ion-item id="mesas-select1">
<ion-label >
Mesa #1:
</ion-label>
<ion-select [(ngModel)]='Gruposobj.nombrem1' name="Mesa#1" placeholder="Selecciona la mesa" >
<ion-option *ngFor="let mesa of mesas">
{{mesa.nombre}}
</ion-option>
</ion-select>
</ion-item>
<ion-item id="mesas-select1">
<ion-label >
Mesa #2:
</ion-label>
<ion-select [(ngModel)]='Grupos2obj.nombrem2' name="Mesa#2" placeholder="Selecciona la mesa" >
<ion-option *ngFor="let mesa2 of mesas2" >
{{mesa2.nombre}}
</ion-option>
</ion-select>
</ion-item>
The ngmodel assigns the selected option to the attribute in the GruposModel. In this case the object of vinculacionmodel is Gruposobj and Grupos2obj. When the user selects an item of ionic-select, I need to get the id selected from an item. Because I will assign this mesa.id && mesa2.id to the vinculacionmodel with Gruposobj.mesa1 && Gruposobj.mesa2.
This is the vinculacionmodel:
export interface vinculacionmodel {
id?:string;
mesa1:string;
mesa2:string;
mesa3:string;
nombrem1:string;
nombrem2:string;
nombrem3:string;
}
As you want ID from the object you can implement ionChange event on your ion-select and will get the selected object.
Please refer to the below code for more detail.
<ion-select [(ngModel)]='Grupos2obj.nombrem2' name="Mesa#2" placeholder="Selecciona la mesa" (ionChange)="onSelectChange($event)">
<ion-option *ngFor="let mesa2 of mesas2" [value]="mesa2['id']">
{{mesa2.nombre}}
</ion-option>
</ion-select>
ionChange event in .ts file:
onSelectChange(selectedValue: any) {
//Selected Value Id will get as param ==> selectedValue
//Selected Object
var item = this.mesas2.find(item => item['id'] === selectedValue);
//Position of object in array
var postion = this.mesas2.findIndex(item => item['id'] === selectedValue);
}
Hope this will help you to get your ID from ios-select.