Routing using object.$key returns undefined - firebase - javascript

I want to route the link by using $key in the firebase. However, after going through all the documentations and forums, the answer that I get is to use object.$key to retrieve the value. I use it and it still returns undefined.
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of products$ | async">
<td>{{ p.title }}</td>
<td>{{ p.price }}</td>
<td>
<a [routerLink]="['/admin/products/', p.$key]">Edit</a>
</td>
</tr>
</tbody>
products$ returns an observable of the AngularFireList
products$;
constructor(private productService: ProductService) {
this.products$ = this.productService.getAll().valueChanges();
}
This is the Product Service definition.
export class ProductService {
constructor(private db: AngularFireDatabase) { }
create(product) {
return this.db.list('/products').push(product);
}
getAll() {
return this.db.list('/products');
}
get(productId) {
return this.db.object('/products/' + productId);
}
}
I still am unsure what is wrong with this as it returns /admin/products/undefined
As all of the reference I read state in using this object.$key

.$key is no longer present in AngularFire2 v5. If you need the key use snapshotChanges() see our migration guide for more details.
constructor(private productService: ProductService) {
this.products$ = this.productService.getAll().snapshotChanges();
}
Then use the payload in your view:
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of products$ | async">
<td>{{ p.payload.val().title }}</td>
<td>{{ p.payload.val().price }}</td>
<td>
<a [routerLink]="['/admin/products/', p.key]">Edit</a>
</td>
</tr>
</tbody>
Or you could map the snapshot changes into a structure you're more familar with:
constructor(private productService: ProductService) {
this.products$ = this.productService.getAll()
.snapshotChanges()
.map(s => ({"$key": s.key, ...s.payload.val()}))
}

Related

Vue <router-link> not clickable

I'm trying to set up a Vue app with some simple routing so I can navigate to different pages. I installed Vue-Router through npm and I've tried following a couple tutorials online but, I can't seem to figure out why my is not clickable or why, when I manually type in the URL, it doesn't change anything. Any help is appreciated. Thank you.
main.js:
import {createApp} from 'vue'
import App from './App.vue'
import "./vue-api-call/styles.css"
import VueRouter from 'vue-router'
import About from "./About"
import Home from "./Home";
const routes = [
{path: '/about', component: About},
{path: '/home', component: Home}
]
const router = new VueRouter({routes})
createApp(App, router).mount("#app")
export default router
App.vue:
<!-- This first part of the file, the template part, is basically like an HTML page. Whatever we put in here will
display when we use 'npm run serve'. -->
<template>
<!-- The <div> tag is used as a container for HTML elements - which is then styled with CSS or manipulated with
JavaScript. -->
<div id="app">
<!-- <nav>Hello</nav>-->
<!-- The router-view tag will display info that comes from the router-link tag. More information here:
https://next.router.vuejs.org/guide/#html -->
<router-view>
<router-link to="/about">About</router-link>
</router-view>
<Layout>
<!-- begin form -->
Add a new video game with the form below:
<form id="demo">
<!-- text -->
<br>
<p>
<input type="text" v-model="msg" placeholder="Add Game">
{{ msg }}
</p>
<br>
<p>
<input type="checkbox" v-model="checked">
{{ checked ? "yes" : "no" }}
</p>
<br>
<!-- select -->
<p>
<select v-model="selected">
<option>one</option>
<option>two</option>
</select>
</p>
<br>
<p>
<button type="submit" v-on:click="createVideoGame()">Submit Form</button>
</p>
</form>
<br>
<button v-on:click="customer()"> Customer Button</button>
<br>
<table>
<thead>
<tr>
<th scope="col">Customer ID</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Middle Name</th>
<th scope="col">Birthday</th>
<th scope="col">Gender</th>
<th scope="col">Email</th>
<th scope="col">Username</th>
<th scope="col">Is Deleted</th>
</tr>
</thead>
<tbody>
<tr v-for="customer in customerInfo" :key="customer.customerId">
<td>{{ customer.customerId }}</td>
<td>{{ customer.cusFirstName }}</td>
<td>{{ customer.cusLastName }}</td>
<td>{{ customer.cusMiddleName }}</td>
<td>{{ customer.dateOfBirth }}</td>
<td>{{ customer.gender }}</td>
<td>{{ customer.email }}</td>
<td>{{ customer.username }}</td>
<td>{{ customer.isDeleted }}</td>
</tr>
</tbody>
</table>
<br><br>
<button v-on:click="videoGame()"> VideoGame Button</button>
<br>
<table>
<thead>
<tr>
<th scope="col">Video Game ID</th>
<th scope="col">Title</th>
<th scope="col">Release Year</th>
<th scope="col">Price</th>
<th scope="col">Stock</th>
<th scope="col">Image</th>
</tr>
</thead>
<tbody>
<tr v-for="game in gameInfo" :key="game.videoGameId">
<td>{{ game.videoGameId }}</td>
<td>{{ game.videoGameTitle }}</td>
<td>{{ game.yearReleased }}</td>
<td>{{ game.price }}</td>
<td>{{ game.stock }}</td>
<td>{{ game.videoGameImage }}</td>
</tr>
</tbody>
</table>
<br><br>
<button v-on:click="order()"> Order Button</button>
<br>
<table>
<thead>
<tr>
<th scope="col">Order ID</th>
<th scope="col">Order Date</th>
<th scope="col">Credit Card Provider</th>
<th scope="col">Credit Card Number</th>
<th scope="col">Total</th>
<th scope="col">Subtotal</th>
<th scope="col">Discount</th>
<th scope="col">Customer ID</th>
<th scope="col">Address ID</th>
</tr>
</thead>
<tbody>
<tr v-for="order in orderInfo" :key="order.orderId">
<td>{{ order.orderId }}</td>
<td>{{ order.orderDate }}</td>
<td>{{ order.creditCardProvider }}</td>
<td>{{ order.creditCardNumber }}</td>
<td>{{ order.total }}</td>
<td>{{ order.subtotal }}</td>
<td>{{ order.discount }}</td>
<td>{{ order.customerId }}</td>
<td>{{ order.addressId }}</td>
</tr>
</tbody>
</table>
<br><br>
<button v-on:click="purchase()"> Purchase Button</button>
<br>
<table>
<thead>
<tr>
<th scope="col">Purchase ID</th>
<th scope="col">Order ID</th>
<th scope="col">Video Game ID</th>
<th scope="col">Shipment Date</th>
</tr>
</thead>
<tbody>
<tr v-for="[purchase] in [purchaseInfo]" :key="purchase.purchaseId">
<td>{{ purchase.purchaseId }}</td>
<td>{{ purchase.orderId }}</td>
<td>{{ purchase.videoGameId }}</td>
<td>{{ purchase.shipmentDate }}</td>
</tr>
</tbody>
</table>
<br><br>
<button v-on:click="address()"> Address Button</button>
<br>
<table>
<thead>
<tr>
<th scope="col">Address ID</th>
<th scope="col">Customer ID</th>
<th scope="col">State ID</th>
<th scope="col">City</th>
<th scope="col">Zip</th>
<th scope="col">Suite Number</th>
<th scope="col">PO Box</th>
</tr>
</thead>
<tbody>
<tr v-for="address in addressInfo" :key="address.addressId">
<td>{{ address.addressId }}</td>
<td>{{ address.customerId }}</td>
<td>{{ address.stateId }}</td>
<td>{{ address.city }}</td>
<td>{{ address.zip }}</td>
<td>{{ address.suiteNumber }}</td>
<td>{{ address.poBox }}</td>
</tr>
</tbody>
</table>
<br><br>
<button v-on:click="state()"> State Button</button>
<br>
<table>
<thead>
<tr>
<th scope="col">State ID</th>
<th scope="col">State</th>
</tr>
</thead>
<tbody>
<tr v-for="state in stateInfo" :key="state.stateId">
<td>{{ state.stateId }}</td>
<td>{{ state.state }}</td>
</tr>
</tbody>
</table>
<br><br>
</Layout>
</div>
</template>
<!-- Still not entirely sure what this script part is. Pretty sure it's some functionality stuff added by Vue -->
<script>
import Layout from "./components/Layout.vue"
import axios from "axios"
// import PageHeading from "./components/PageHeading.vue"
// import NavBar from "./components/NavBar.vue"
export default {
name: 'App', variant: "variant", variantType: "variantType", disableButton: "disableButton",
username: "", password: "",
data: () => {
return {
// These variables are used above and are what initially is shown above the buttons.
customerInfo: [],
gameInfo: [],
orderInfo: "waiting for order",
purchaseInfo: "waiting for purchase",
addressInfo: "waiting for address",
stateInfo: "waiting for state",
username: "",
password: "",
msg: "",
checked: true,
picked: "",
selected: ""
}
},
components: {
Layout,
// NavBar, PageHeading,
},
mounted() {
// this.test()
},
methods: {
// Gets customer info and displays it.
customer: function () {
if (this.customerInfo === "waiting for customer") {
axios
.get('http://localhost:8000/customers/?format=json')
.then(response => (this.customerInfo = response.data))
} else {
this.customerInfo = "waiting for customer"
}
},
// Gets video game info and displays it.
videoGame: function () {
if (this.gameInfo === "waiting for game") {
axios
.get('http://localhost:8000/videoGames/?format=json')
.then(response => (this.gameInfo = response.data))
} else {
this.gameInfo = "waiting for game"
}
},
// Gets order info and displays it.
order: function () {
if (this.orderInfo === "waiting for order") {
axios
.get('http://localhost:8000/orders/?format=json')
.then(response => (this.orderInfo = response.data))
} else {
this.orderInfo = "waiting for order"
}
},
// Gets purchase info and displays it.
purchase: function () {
if (this.purchaseInfo === "waiting for purchase") {
axios
.get('http://localhost:8000/purchases/?format=json')
.then(response => (this.purchaseInfo = response.data))
} else {
this.purchaseInfo = "waiting for purchase"
}
},
// Gets address info and displays it.
address: function () {
if (this.addressInfo === "waiting for address") {
axios
.get('http://localhost:8000/addresses/?format=json')
.then(response => (this.addressInfo = response.data))
} else {
this.addressInfo = "waiting for address"
}
},
// Gets state info and displays it.
state: function () {
if (this.stateInfo === "waiting for state") {
axios
.get('http://localhost:8000/states/?format=json')
.then(response => (this.stateInfo = response.data))
} else {
this.stateInfo = "waiting for state"
}
},
createVideoGame() {
axios.post('http://127.0.0.1:8000/videoGames/', {
videoGameId: '111',
videoGameTitle: 'Test Video Game 1',
yearReleased: '2021',
price: '10.00',
stock: '5',
videoGameImage: 'testImage'
})
},
// login: function () {
// console.log(this.username)
// console.log(this.password)
// const bcrypt = require("bcryptjs")
// bcrypt.compare(this.password, "hash", (err, result) => {
// if (err) {
// console.error(err)
// return
// }
// console.log(result) // true or false
// });
}
}
// methods: {
// addUser(){
// console.log(this.encryptPassword(this.password))
// },
// encryptPassword(password)
// const salt = bcrypt.genSaltSync(10)
// return bcrypt.hashSync(password, salt)
// },
</script>
<!-- This style tag is for the entire page, I think. If you change something in here, like the background-color, it
will change it on the webpage. It's exactly like the style tag in a CSS/HTML page I'm pretty sure. -->
<!-- if you use the !important; rule, it will override ALL previous styling rules for that specific property on that
element! -->
<style>
body {
margin: 0;
padding: 0;
background-color: #c5fcc5 !important;
}
table, th, td {
border: 1px solid darkgreen;
text-align: center;
}
</style>
You forgot to tell Vue to use the VueRouter. From the Official Docs.
import { createApp } from 'vue';
import App from './App.vue';
...
...
const routes = [
{path: '/about', component: About},
{path: '/home', component: Home}
];
const router = new VueRouter({routes});
createApp(App).use(router).mount('#app'); // <== Here
export default router;

Displaying JSON data on condition using V-for and V-if in Vue.js

I am fetching data(Orders) from external Api in Vue using axios. I obtain JSON data and i am able to show it in a HTML table. Now i am trying filter the data to show only related data to use. In my Json data, i have a field called "order status: Completed / processing". Now i only want to show the json data which are have status like "Processing" to achieve my goal.
I am trying to use v-if with v-for but I m unable to get the certain orders data and view.
The table is set to update for each minute.
Here is my code:
html code
**<div class ="container mt-4" id="app">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Order id</th>
<th scope="col">Name</th>
<th scope="col">Order Date</th>
<th scope="col">Phone</th>
<th scope="col">Address</th>
<th scope="col">Items</th>
<th scope="col">Total</th>
<th scope="col">Print</th>
</tr>
</thead>
<tbody>
<tr
v-for="(order, index) in orders" v-if="order.status === "processing""
:key="order.id"
:class="{highlight: !order.is_printed}"
>
<td>{{ order.id }}</td>
<td>{{ order.billing.first_name + " " +order.billing.last_name }}</td>
<td>{{ order.date_created }}</td>
<td>{{ order.billing.phone}}</td>
<td>{{ order.billing.address_1 + ", " + order.billing.address_2 + ", " + order.billing.city + order.billing.postcode }}</td>
<td>{{ order.line_items[0].name}} </td>
<td>{{ order.total}}</td>
<td><button class="btn btn-primary" #click="printBill(order)">Print</button>
</tr>
</tbody>
</table>**
Vue
<script>
var app = new Vue({
el: '#app',
data: {
orders: []
},
mounted: function() {
// API Call function to be implemented here....
</script>
I think this should do the trick.
According to Vue documentation it's best to put any logic into computed properties https://v2.vuejs.org/v2/guide/computed.html
new Vue({
el: "#app",
data: {
orders: [
{ id: 1, status: "processing"},
{ id: 2, status: "other" }
]
},
computed: {
filteredOrders() {
return this.orders.filter(order => order.status === 'processing');
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<tr>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr v-for="order in filteredOrders" :key="order.id">
<td>{{ order.status }}</td>
</tr>
</tbody>
</table>
</div>
its better to filter data after you get them from api.
based on vue.js document it’s not recommended to use v-if and v-for together, read this:
https://v2.vuejs.org/v2/guide/list.html#v-for-with-v-if
try this
let filteredData = this.orders.filter(order => order.status === "processing")

Angular 4-Add rows in table

I have this service subscribtion
callSearchService($event) {
this.searchService.searchUser(this.firstName,this.lastName).subscribe(
data => {
console.log("we got: ", data);
this.isNull(data.firstName,data.lastName);
},
error => {
console.log("Error",error);
},
);
and also I have a table .
<thead>
<tr>
<th>#</th>
<th>FirstName</th>
<th>LastName</th>
</tr>
</thead>
I am interested how to add rows dynamically to my table, when i get user info (firstname and lastname) from the callSearchService
something like this
users = []; //user array
<thead>
<tr>
<th>#</th>
<th>FirstName</th>
<th>LastName</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let user of users; let i=index;">
<tr>
<th>{{i}}</th>
<td>{{user.firstName}}</td>
<td>{{user.lastName}}</td>
</tr>
</ng-container>
</tbody>
when you receive new user just update array and remaining things will work fine
callSearchService($event) {
this.searchService.searchUser(this.firstName,this.lastName).subscribe(
data => {
console.log("we got: ", data);
this.isNull(data.firstName,data.lastName);
this.users.push(data);
},
error => {
console.log("Error",error);
},
);
You can use ngFor.
suppose you are getting the results in userdetails[] variable.
<tr *ngFor="let userdetail of userdetails | async">
<td>
{{userdetail.firstName}}
</td>
<td>
{{userdetail.lastName}}
</td>
</tr>

Pass ID(Index) to onEditDetail() Parameters

How can i pass the value index or id to EditDetail() parameters. I want it to be put inside the parenthesis of onEditDetail() once i click the onEditDetail() in html
ngOnInit() {
this.route.params
.subscribe((params: Params) => {
this.id = +params['id'];
this.user = this.userService.getUser(this.id);
});
}
onEditDetail(id: string) {
console.log(id);
}
user-detail.component.html
<div class="container">
<div class = "row">
<div class="col-md-3"></div>
<div class = "col-md-8">
<div class="card">
<div class="card-header">
{{ user.l_name}} 's Complete Information<span class="pull-right"><input class="form-control" placeholder="Search User" name="srch-term" id="srch-term" type="text"></span>
</div>
<table class="table table-responsive">
<thead class="thead-default">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Contact Number</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ user.f_name }}</td>
<td>{{ user.l_name }}</td>
<td>{{ user.contact_no }}</td>
<td><button class=" btn btn-primary" style="cursor: pointer;" (click)="onEditDetail()">Edit</button> </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
Adding to other answers,
You need to understand that you are making an asynchronous call.
So If you need to access id anywhere else, you need to get that on completion of asynchronous call, not before that.
And this is reason, you need to call onEditDetail() inside Subscribe block as last parameter.
there are 3 params to subscribe.
Data which is returned by call,
Error if any occured.
On complete if you wish to do something.
In your case, as 3rd parameter, you have to call onEditDetail method
ngOnInit() {
this.route.params
.subscribe((params: Params) => {
this.id = +params['id'];
this.user = this.userService.getUser(this.id);
// 3rd parameter, this is where call is subscribe is completed
this.onEditDetail(this.id);
});
}
You can do that
ngOnInit() {
this.route.params
.subscribe((params: Params) => {
this.id = +params['id'];
this.user = this.userService.getUser(this.id);
// Your method call
this.onEditDetail(this.id);
});
}
onEditDetail(id: string) {
console.log(id);
}
ngOnInit() {
this.route.params
.subscribe((params: Params) => {
this.id = +params['id'];
this.onEditDetail(this.id); //call onEditDetails and pass id to it.
this.user = this.userService.getUser(this.id);
});
}
onEditDetail(id: any) {
console.log(id);
}

using ngif inside the loop and toggling the table row with specific class when clicking on table row with another class in angular 2

I have a table generated from the data and there are 2 tr with class default and toggle-row when i click on tr with class default it should only toggle the corresponding tr with class toggle-row however my code toggles all the toggle-row class when clicked on any one of table row with class default. how do if fix this. i am using *ngIF to toggle the table rows.
Template file is like this
<table class="table table-container table-responsive" id = "team-members">
<thead class="table-heading">
<tr>
</tr>
</thead>
<tbody class="data-item" *ngFor = "let member of teamMember; let i = index" >
<tr id ="{{i}}" (click)="Toggle(i)" class="default">
<td *ngFor = "let hrs of member.Value.hoursLogged">
{{ hrs }}
</td>
</tr>
<ng-container *ngFor = "let subtask of member.Value.subTasks">
<tr class="toggle-row" *ngIf="toggle" >
<td>
{{ subtask.taskType }}
</td>
<td *ngFor="let hrs of subtask.subtaskHoursLogged">
{{ hrs }}
</td>
</tr>
</ng-container>
<tbody>
</table>
basically this loop creates the structure
<table>
<thead></thead>
<tbody>
<tr id="1" class="default"><tr>
<tr class="toggle-row"></tr>
<tr class="toggle-row"></tr>
<tr class="toggle-row"></tr>
</tbody>
<tbody>
<tr id="2" class="default"><tr>
<tr class="toggle-row"></tr>
<tr class="toggle-row"></tr>
<tr class="toggle-row"></tr>
</tbody>
<tbody>
<tr id="3" class="default"><tr>
<tr class="toggle-row"></tr>
<tr class="toggle-row"></tr>
<tr class="toggle-row"></tr>
</tbody>
</table>
and i want to toggle table-row class when clicked on default class only inside that tbody
and typescript file for this template is like this
import { Component, OnInit } from '#angular/core';
import { DataServiceService } from "../../services/data-service.service";
#Component({
selector: 'app-projects',
templateUrl: './projects.component.html',
styleUrls: ['./projects.component.css']
})
export class ProjectsComponent implements OnInit {
private teamMember: any[];
public toggle = false;
constructor(private dataserve: DataServiceService) { }
ngOnInit() {
this.dataserve.getTeamMemberData()
.subscribe(
(data: any) => {
var localarray= [];
for (let key in data){
localarray.push({key:key, Value:data[key]});
}
this.teamMember = localarray;
console.log(this.teamMember);
}
);
}
Toggle(value){
this.toggle = !this.toggle;
}
}
you need to move toggle variable inside member.Value.subTasks variable to make things work for each row.
As toggle is global variable, it will simply update the view for all rows.
<ng-container *ngFor = "let subtask of member.Value.subTasks">
<tr class="toggle-row" *ngIf="subtask.toggle" >
<td>
{{ subtask.taskType }}
</td>
<td *ngFor="let hrs of subtask.subtaskHoursLogged">
{{ hrs }}
</td>
</tr>
</ng-container>
you need to change Toggle(i) function to update the member.Value.subTasks variable.
Hope it helps!.

Categories

Resources