Changing html element styles with Angular 2 based on logic - asp.net

I'm using .net core 2 as backend and Angular2 as frontend for this app. I encountered the issue where I need to check whether price difference between products in other shops are greater than price in BaseEshop for e.g. 10%, if yes then I need to change that '' background-color to red. There are like 100 products in each eshop which I need to check and change the background color
What is the best way to do it. This is my code:
import { ProductService } from './../../services/product.service';
import { Product } from './../../models/product';
import { Component, OnInit } from '#angular/core';
import { ToastyService } from 'ng2-toasty';
import { Router, ActivatedRoute } from '#angular/router';
import { Price } from '../../models/price';
#Component({
templateUrl: 'product-list.html'
})
export class ProductListComponent implements OnInit {
product: Product[];
prices: Price[];
constructor(private productService: ProductService) { }
ngOnInit() {
this.productService.getProducts().subscribe(product => this.product = product);
this.productService.getPrices().subscribe(prices => this.prices = prices);
}
}
Here is html file
<table class="table">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>BaseEshop</th>
<th>Eshop2</th>
<th>Eshop3</th>
<th>Eshop4</th>
<th>Eshop5</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of product" >
<td>{{ p.code }}</td>
<td >{{ p.name }}</td>
<ng-container *ngFor="let pr of p.prices">
<td *ngIf=" pr.eshopId==1" >{{ pr.value }}</td>
<td *ngIf=" pr.eshopId==2" >{{ pr.value }} <span class='glyphicon glyphicon-arrow-up'></span></td>
<td *ngIf=" pr.eshopId==3" >{{ pr.value }}</td>
<td *ngIf=" pr.eshopId==4" >{{ pr.value }}</td>
<td *ngIf=" pr.eshopId==5" >{{ pr.value }}</td>
</ng-container>
</tr>
</tbody>
</table>
This what my json looks like
{
"id": 218374,
"name": "\"Dell Inspiron 15 5578 Silver, 15.6 \"\", Touchscreen, Full HD, 1920 x 1080 pixels, Gloss, Intel Core i5, i5-7200U, 8 GB, DDR4, SSD 256 GB, Intel HD, Linux, 802.11ac, Bluetooth version 4.2, Keyboard language English, Keyboard backlit\"",
"code": "272771020",
"edited": false,
"prices": [
{
"id": 448664,
"value": "929.79",
"updatedAt": "2018-04-16T22:41:59",
"eshopId": 1
},
{
"id": 490139,
"value": "811.00",
"updatedAt": "2018-04-20T11:42:26",
"eshopId": 2
},
{
"id": 490789,
"value": "781.00",
"updatedAt": "2018-04-20T11:22:42",
"eshopId": 3
}
]
}

You can do this:
<td *ngIf=" pr.eshopId==1" [ngClass]="{'RedClass': pr.value > 500}">{{ pr.value }}</td>
Instead of pr.value > 500 you can add your logic to work out the %. If true the class is applied, false and no class is applied.

You have 2 options: [ngStyle] or [ngClass]. I would prefer ngClass whenever you can use it. What it does is it applies certain css class to an element if some condition is met.
ref:https://angular.io/guide/template-syntax

Related

Vue JS unable to properly show data table (v-for)

i'm trying to show a table of data that is coming from Firebase Firestore, i've already managed to put all data into a array, but when i try to show that content, the entire array shows up instead of the single items, see the image:
And heres my code:
<template>
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">
Name
</th>
<th class="text-left">
Calories
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in result"
v-bind:key="index"
>
<td>{{ result }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</template>
<script>
import firebase from 'firebase'
import {db} from '../service/firebase'
export default {
data () {
return {
userName: null,
result: [],
name: null,
email: null,
userMail: null,
telefone: null,
userPhone: null,
userAccept: null,
}
},
async created() {
var userData = []
await db.collection("users").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(`${doc.id} => ${doc.data()}`);
userData.push(doc.data())
});
});
this.result = userData.map(a => a.name)
console.log(result)
}
}
</script>
How do i show a table of single items of the array instead of a table of arrays?
Thank you in advance!
You're printing the whole content using {{result}}, the right syntax is the following :
<tr v-for="(item, index) in result" v-bind:key="index" >
<td v-for="(record,i) in item" :key="i">{{ record }}</td>
</tr>

Add style to first row of v-data-table in vuetify

I have defined the table as below using vuetify data table component. The issue I am facing here is I not able to figure out how can I make the first row of the table bold. The first item record to be bold. Please help find a solution.
I am using vuetify 1.0.5.
<v-data-table>
:headers="headers"
:items="agents"
hide-actions
class="agent-table"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</template>
</v-data-table>
use v-if to search for first row index or something unique about first row and bind it to style or class. Few more ways listed here reference
<template slot="items" slot-scope="props">
<tr v-if="unique condition" v-bind:style="{ 'font-weight': 'bold'}>
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</tr>
<tr v-else>
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</tr>
</template>
Another approach that can be used is using computed properties to insert the index to each element in the data. This can be useful if you need to update the table later on as computed properties are updated automatically.
For example, say the item data is stored in items.
data() {
return {
items: [{
fruit_name: 'Banana',
calories: 30
}, {
fruit_name: 'Apples',
calories: 40
}]
}
}
Here, every element to be itself plus additional attribute, i.e. index. Element addition is achieved using spread operator. All mapped elements are combined into single array using functional-programming style of map function.
computed: {
itemsWithIndex: () {
return this.items.map(
(items, index) => ({
...items,
index: index + 1
}))
}
}
Note: index: index+1 will make the numbering start from 1.
Then, inside headers data needed for v-data-table, you can make reference to index item data for numbering.
data() {
return {
items: {
...
},
headers: [{
text: 'Num',
value: 'index',
},
{
text: 'Fruit Name',
value: 'fruit_name',
},
{
text: 'Calories',
value: 'calories',
}
]
}
}
Codepen example: https://codepen.io/72ridwan/pen/NWPMxXp
Reference
<template slot="items" slot-scope="props">
<tr v-bind:class="getClass(props.item.name)">
<td>{{ props.item.name }}</td>
<td>{{ props.item.address }}</td>
</tr>
</template>
<script>
export default {
methods: {
getClass: function (name) {
if (name == 'XYZ') return 'header2';
},
}
}
</script>
<style>
.header2 {
// added style here
}
<style>

Vue: [Vue warn]: Error in render: "TypeError: product.data is not a function"

I am trying to retrieve the data from cloud firestore database.
But I got an error,
[Vue warn]: Error in render: "TypeError: product.data is not a
function"
I want to show the each product name and price in my table.
But I have no idea why this issue comes up.
So I hope somebody can help me out.
If I don't use data() in the vue template, I can see all the data as I expected.
<template>
<h3>Product List</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Modify</th>
</tr>
</thead>
<tbody>
<tr v-for="(product, index) in products" :key="index">
<td>{{ product.data().name }}</td>
<td>{{ product.data().price }}</td>
<td>
<button #click="editProduct(product)" class="btn btn-primary">Edit</button>
<button #click="deleteProduct(product.id)" class="btn btn-danger">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { fb, db } from '../firebase'
export default {
name: 'Products',
props: {
msg: String
},
data () {
return {
products: [],
product: {//object
name: null,
price: null
}
}
},
methods: {
editProduct(product) {
$('#edit').modal('show')
},
readData() {
db.collection("products").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.products.push(doc.data());
});
});
},
saveData() {
// Add a new document with a generated id.
db.collection("products").add(this.product)
.then((docRef) => {
console.log("Document written with ID: ", docRef.id);
this.product.name = "",
this.product.price = ""
this.readData()
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
}
},
created() {
this.readData();
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
I will have to agree with #Mostafa, the naming convention is not very readable. Your error is telling you that you are trying to invoke a function that is not a function or does not exist in your data.
Change:
<td>{{ product.data().name }}</td>
<td>{{ product.data().price }}</td>
To:
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
This should fix it, as you are iterating over the products list (of which isn't clear), so i advise you should change:
<tr v-for="(product, index) in products" :key="index">
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
<td>
<button #click="editProduct(product)" class="btn btn-primary">Edit</button>
<button #click="deleteProduct(product.id)" class="btn btn-danger">Delete</button>
To:
<tr v-for="(productItem, index) in products" :key="index">
<td>{{ productItem.name }}</td>
<td>{{ productItem.price }}</td>
<td>
<button #click="editProduct(productItem)" class="btn btn-primary">Edit</button>
<button #click="deleteProduct(productItem.id)" class="btn btn-danger">Delete</button>
Your code is very confusing.
I don't understand why you are calling data method on product and why you have product and products in your data when you just need one.
So i'm assuming Vue is mixing product in your for loop and the product object in your data.
So either change the product name in your for loop to something else:
v-for="(item,index) in products"
or change product in your data (just remove it if you can) cause it doesn't have any data method in it.

Update only a single property of a given object

I have an entity called worker and each worker has a property called active which is boolean.
My twig is an index that shows the list of workers with active=true.
I have a button in front of each worker, when I press this button I want it to change that worker's active property to false.
The problem: I couldn't figure out how to change that value in the controller without making a form since I'm still an amateur when it comes to Symfony
Here's my twig:
<table id="file_export" class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last name</th>
<th>Active</th>
<th>edit</th>
</tr>
</thead>
<tbody>
{% for worker in workers %}
<tr>
<td>{{ worker.id }}</td>
<td>{{ worker.Firstname }}</td>
<td>{{ woker.Lastname }}</td>
<td>{{ worker.active ? 'active' : 'inactive' }}</td>
<td>
<i class="fa fa-pencil"></i>
</td>
</tr>
{% endfor %}
</tbody>
</table>
and my controller (which doesn't work):
/**
* #Route("/{id}/edit", name="worker_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Worker $worker): Response
{
if ($this->isCsrfTokenValid('edit'.$worker->getId(), $request->request->get('_token'))) {
$worker->setActive(false);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($worker);
$entityManager->flush();
}
return $this->redirectToRoute('index');
}
you actually have to add a csrf token to your path call:
path('worker_edit', {'id': worker.id, '_token': csrf_token('worker'~worker.id)})
or otherwise your check for the csrf token obviously cannot succeed.
however, since a link will trigger a GET request, you have to look into
$request->query->get('_token')
in the isCsrfTokenValid call.
As a hint: give your routes and actions semantically better names. Like ... "worker_deactivate", if it is used to deactivate a worker (which it apparently is). it's also quite common, to call the routed methods of a controller actionAction, so that would be deactivateAction.
If you want to make HTTP requests without reloading the web page, then you've to go for AJAX calls. A very simple implementation using fetch that doesn't require any additional packages (like jQuery) would look like this:
<script>
(function() {
document.getElementById({{worker.id}}).addEventListener('click', function(e) {
e.preventDefault();
fetch({{path('worker_edit', {'id': worker.id})}}, {method: 'POST'})
.then(function(response) {
// you can catch eventual errors here, and of course refresh your button or display a nice message..
});
});
})()
</script>
<table id="file_export" class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last name</th>
<th>Active</th>
<th>edit</th>
</tr>
</thead>
<tbody>
{% for worker in workers %}
<tr>
<td>{{ worker.id }}</td>
<td>{{ worker.Firstname }}</td>
<td>{{ woker.Lastname }}</td>
<td>{{ worker.active ? 'active' : 'inactive' }}</td>
<td>
<i class="fa fa-pencil"></i>
</td>
</tr>
{% endfor %}
</tbody>
</table>
p.s: The javascript code above is not tested as I have to reproduce the twig and controller, but it could give you an idea on how to achieve the task.

Firebase + VueJS: Highlighting data in a v-for loop

I am making a user- / login- / authentication-system with Firebase and VueJS.
I wonder how to highlight the name of each person that is online (has an entry in my firebase-sessions-list).
My database-structure:
for users:
- users:
- [userKey]:
- ... user information
for sessions:
- sessions:
- [userKey]:
- ... session information
The template part:
<table>
<tr class="user" v-for="user in $store.state.authentication.users">
<td class="name">{{ user.name }}</td>
...
</tr>
</table>
Advice?
You may add a class to td if user is online
<table>
<tr class="user" v-for="user in $store.state.authentication.users">
<td class="name" v-bind:class="getUserStatusClass(user)">
{{ user.name }}
</td>
...
</tr>
</table>
You also have to add a method to vue instance, like
methods: {
getUserStatusClass: function (user) {
if(// userKey is in sessons) {
return 'active'
}
return ''
}
},
A 'active' CSS class will be added to td if user is online. Than you can edit the style for 'active' CSS class

Resources