AngularJS filter on deep properties - javascript

I am trying to filter some data on a string value but I can't get the syntax right. Here is my code :
<tbody ng-repeat="qbRating in vm.scope.qbRatings | filter:'tournament.season.seasonName':'2022' | ratingFilter | orderBy:'-rating'">
<tr class="qbrating2022">
<td>{{ qbRating.tournament.season.seasonName }}</td>
<td>{{ qbRating.team.teamName }}</td>
<td>{{ qbRating.completion }}</td>
<td>{{ qbRating.gain }}</td>
<td>{{ qbRating.touchdown }}</td>
<td>{{ qbRating.interception }}</td>
<td>{{ qbRating.rating }}</td>
</tr>
</tbody>
The filter does not return any data even though it should do, but there is no error returned in the console.
Can anybody help me get the syntax right?

You should put filter on specific property by specifying object to be match like filter: {'tournament': {season : {seasonName:'2022'}}}
ng-repeat="qbRating in vm.scope.qbRatings | filter: {'tournament': {season : {seasonName:'2022'}}} | ratingFilter | orderBy:'-rating'"

filter takes a string, object or function, not an array filter api

Related

How can I convert integers to corresponding strings in Vue markup?

I have a table for fetching data from the database. I used the "status" field for managing this table.
if the status is = 1, it means "Active" else if the status is = 2, it means "Completed" else if the status is = 0, t means "Deleted".
Then I need to show the above status word in the web page table.
I used the below to show the status number.
<tr v-for="(depAcc, index) in depAccs" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ depAcc.created_at }}</td>
<td>{{ depAcc.name }}</td>
<td>{{ depAcc.status }}</td>
Please instruct me to show the status word on the table.
I would create a data property containing the status map, like this:
data() {
return {
statusNames: {
0: 'Deleted',
1: 'Active',
2: 'Completed'
}
}
}
Then you can reference that in your markup:
<td>{{ statusNames[depAcc.status] }}</td>
In Vue, anything inside double braces {{ //this }} is evaluated as JS. You can therefore write a method to return strings based on your status, then put a function into your double braces.
<tr v-for="(depAcc, index) in depAccs" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ depAcc.created_at }}</td>
<td>{{ depAcc.name }}</td>
<td>{{ statusText(depAcc.status) }}</td>
</tr>
Then in your page script, add this method
var app = new Vue({
el: '#app',
methods:{
statusText(status){
let message = "Unknown Status"
if (status == 0) message = "Deleted"
if (status == 1) message = "Active"
if (status == 2) message = "Completed"
return message
}
}
});

How to call Laravel Model function with parameter into Vuejs template?

I have a product table and stock table. In stock table data about product sale and purchase are stored. I need to show the stock of every product base on purchase and sale. So I need to call a model function with product Id in vuejs template to calculate the stock quantity of the product. How to do this, or is there any alternative way? please help me out.
My product controller function is-
public function stock() {
return Product::with(['category', 'stock'])->get();
}
My product model function is-
public function stock($id){
$purchase_quantity = Stock::where([['product_id', $id], ['stock_type', 'purchase']])->sum('quantity');
$sale_quantity = Stock::where([['product_id', $id], ['stock_type', 'sale']])->sum('quantity');
return $purchase_quantity - $sale_quantity;
}
My vuejs template code where in every v-for iteration I want to call the model function-
<tr v-for="product in products.data" :key="product.id">
<td>{{ product.id }}</td>
<td>{{ product.category.name }}</td>
<td>{{ product.name }}</td>
<td>{{ product.unit }}</td>
<td>{{ product.stock(product.id) }}</td>
</tr>
Here product.stock(product.id) is not working.
It shows the error-
Too few arguments to function App\Models\Product::stock(), 0 passed
you need to use accessor in this case so
in Product.php
protected $appends = ['stocks'];
/**
* Get the Product's stock
*
* #return int
*/
public function getStocksAttribute()
{
$purchase_quantity = Stock::where([['product_id', $this->id], ['stock_type', 'purchase']])->sum('quantity');
$sale_quantity = Stock::where([['product_id', $this->id], ['stock_type', 'sale']])->sum('quantity');
return $purchase_quantity - $sale_quantity;
}
then in side javascript you can you can get stock in each product row like product.stocks
your vuejs code will be like this
<tr v-for="product in products.data" :key="product.id">
<td>{{ product.id }}</td>
<td>{{ product.category.name }}</td>
<td>{{ product.name }}</td>
<td>{{ product.unit }}</td>
<td>{{ product.stocks }}</td>
</tr>
ref link https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor

How to sort columns in Vue when the values to sort are the result of calculations

I've found many resources for sorting data that is already in an array but can't find anything on sorting dynamically generated data.
<table>
<thead>
<tr>
<th>Program</th>
<th>Rewards</th>
</tr>
</thead>
<tbody>
<tr v-for="program in programs" :key="program.id">
<td>{{ program.program_name }}</td>
<td>{{ pointValue(program) | percent }}</td>
</tr>
</tbody>
</table>
pointValue() is a method which calculates and returns a value which is displayed as a %. this is the Rewards column. i would like the table to be sortable by Programs and by Rewards. (Program is just a string).
Create computed array for programs using map and sort method and iterate it instead
computed: {
computedPrograms() {
return this.programs
.map(program => {
return {
...program,
value: this.pointValue(program)
}
})
.sort((a, b) => a.value - b.value)
}
}
<tr v-for="program in computedPrograms" :key="program.id">
<td>{{ program.program_name }}</td>
<td>{{ program.value | percent }}</td>
</tr>

ng-repeat takes too much time to render data

I know there are many questions already posted for the same issue but none of the solutions work in my case.
On calling a web service I get JSON response. In this JSON, there are around 2000+ objects from which I need to display data on the table. I want to display all (2000+) records in the table and Yes, I cannot limit or paginate, need to display it on a single page (I know it's stupid but it's the business requirement). I don't need sorting or searching.
Data transfer is about 2MB and the request completes in about 2-4 secs approx. but it takes around 10-15 secs to data render on the page.
Now, what I am looking for is either speed ng-repeat binding things up (if possible) or display the data as soon as I receive it and keep on adding it until all rows are displayed.
Check out the code below :
HTML
<table class="table table-bordered table-striped cf">
<thead style="color: #333;">
<tr>
<td>Asset Name</td>
<td>Date/ Time</td>
<td>Location</td>
<td>Ignition</td>
<td>Speed</td>
<td>Heading</td>
<td>Direction</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="cols in tableData">
<td>{{ cols.aN }}</td>
<td>{{ cols.dT }}</td>
<td>{{ cols.Lat }}, {{ cols.Lon }}</td>
<td>{{ cols.I }}</td>
<td>{{ cols.S }}</td>
<td>{{ cols.H }}</td>
<td>{{ cols.D }}</td>
</tr>
</tbody>
</table>
JS
var ignition_text = '';
var lat = '';
var lon = '';
for (var i = 0; i < data.length; i++) {
if (data[i].ignition = 1) {
ignition_text = "On";
} else {
ignition_text = "Off";
}
$scope.$apply(function() {
$scope.tableData.push({
aN: name,
dT: data[i].eventUTCTime,
Lat: data[i].latitudeDegrees,
Lon: data[i].longitudeDegrees,
I: ignition_text,
S: data[i].speedMPH,
H: data[i].longitudeDegrees,
D: data[i].latitudeDegrees
});
});
}
Thanks in advance!
You probably wont need $scope.$apply at all. And even if you need it, you should only use it once you pushed all data to the table. Otherwise, every added entry will force an digest-cycle. Just build your array and assign the finished array to the scope-variable. Then angular will only build the table once.
Depending on the nature of your variable name you may be able to eliminate the array building as well and just use the data you are downloading. Apart from nameyou just use that data anyway.
Here is a plunk that has a similar data size but loads much faster http://plnkr.co/edit/I4rN1ZMaR3e1mbcsJ9Ka. If you were to make a quick plunk I could use your data and edit your code but from the looks you just need the main assignment to the scope without the apply for the data and add a track by to the ng-repeat. SN: You would want to manipulate your data inside the for loop then do the assignment to the scope.
for (var i = 0; i < data.length; i++) {
if (data[i].ignition = 1) {
ignition_text = "On";
} else {
ignition_text = "Off";
}
}
$scope.tableData=data;
JS
$http.get("largeData.json").then(function(response) {
vm.test = response.data;
});
HTML
<tbody>
<tr ng-repeat="(key, value) in main.test track by $index ">
<td>{{ value.ask }}</td>
<td>{{ value.bid }}</td>
<td>{{ value.volume_btc }}, {{ value.volume_percent }}</td>
<td>{{ value.last }}</td>
<td>{{ value.timestamp }}</td>
</tr>
</tbody>

Angular Scope length not working

I have a scope dumping into a table via an ng-repeat. This data is filtered by 3 different things, 2 selects and a text box. All of this works fine, but I need to echo out how many results are in the table. Using {{ sounds.length }} obviously doesn't work because this is a count prior to the filtering and filtering has no effect on that. so i added a variable sound in soundsres = (sounds | filter: filters here) but doing {{ soundsres.length }} echos out nothing with no errors in the console.
Below is a complete set of code. What am I missing here. all indications (i even checked the docs and several older threads here on SO) indicate that this should be working. Thanks in advance.
Code:
<input type="text" ng-model="gearsearch">
<select ng-model="stypesearch"><option>1</option></select>
<select ng-model="stypesearch2"><option>2</option></select>
<span>{{ soundsres.length }}</span>
<table ng-controller="GearController">
<tr ng-repeat="sound in soundsres = (sounds | filter: gearsearch | filter: stypesearch | filter: stypesearch2)">
<td>{{ sound.id }}</td>
<td>{{ sound.model }}</td>
<td>{{ sound.make }}</td>
<td>{{ sound.type }}</td>
<td>{{ sound.class }}</td>
<td>{{ sound.status }}</td>
<td>{{ sound.cost | currency }}</td>
</tr>
</table>
You could use controllerAs syntax and add the soundRes to your MainController or you could use $parent.soundRes to add your filter result to parent scope. I think the controllerAs method is more clear but both will work.
Please have a look at the demo below or in this fiddle.
(Sorry for my poor data model in the demo but I don't have a better one. But it's OK to show that the length is correctly updated.)
angular.module('demoApp', [])
.controller('ViewController', function() {
var vm = this,
sameDate = new Date();
sameDate.setMinutes(sameDate.getMinutes() - 5);
var dateLimit = new Date(sameDate);
var dates = [
{
name: 'test1',
date: new Date(sameDate)
},
{
name: 'test2',
date: new Date(sameDate)
},
{
name: 'test3',
date: new Date()
}];
vm.dates = dates;
})
.controller('MainController', MainController);
function MainController() {
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp" ng-controller="MainController as mainCtrl">
<input ng-model="mainCtrl.searchText"/>
<select ng-model="mainCtrl.stypesearch"><option>1</option></select>
<select ng-model="mainCtrl.stypesearch2"><option>2</option></select>
<!--results: {{mainCtrl.filterRes.length}}-->
results: {{filterRes.length}}
<ul ng-controller="ViewController as viewCtrl">
<li ng-repeat="date in $parent.filterRes = ( viewCtrl.dates | filter:mainCtrl.searchText | filter: mainCtrl.stypesearch | filter: mainCtrl.stypesearch2 )">{{date}}</li>
</ul>
</div>

Categories