Vue js for loop with a condition - javascript

I have an object that look like this:
groupedContacts:
{
4: [
{name: 'foo'},
{name: 'bar'}
],
6: [
{name: 'foo bar'},
]
}
Then I have an other array:
companyList.models:
models: [
{id:4, name: 'company A'},
{id:6, name: 'company B'},
]
So the Id's in my companies resemble the keys in my groupedContacts, in 1 array I have the company names and in the other I have the contacts of the companies.
Now I want to show them in multiple tables ofcourse like this
Table 1
Company A (id4)
- foo
- bar
Table2
Company B (id6)
- foo bar
Here's my code, unfortunately I get my 2 tables of my 2 companies but no contacts. And I get no errors whatsoever:
<div
v-for="(company, key) in companyList.models"
:key="key"
class="table table--hover mt-4"
>
<h4 class="mb-4" v-html="company.name" />
<table>
<thead>
<tr>
<th class="text-left" v-html="__t('name')" />
</tr>
</thead>
<tbody
v-for="(groupContacts, key) in groupedContacts"
:key="key"
v-if="company.id === key"
>
<tr
v-for="(contact, contactKey) in groupContacts"
:key="contactKey"
>
<td v-html="contact.name" />
</tr>
</tbody>
</table>
</div>
This is my result in my browser:

I recommend to use a computed property called companies as follows :
computed: {
companies() {
return this.models.map(c => {
c.groups = this.groupContacts[c.id];
return c;
})
}
}
then loop through it like :
<div v-for="(company, key) in companies" :key="key" class="table table--hover mt-4">
<h4 class="mb-4">{{company.name}}</h4>
<table>
<thead>
<tr>
<th class="text-left">Name</th>
</tr>
</thead>
<tbody>
<tr v-for="(contact, contactKey) in company.groups" :key="contactKey">
<td> {{contact.name}}</td>
</tr>
</tbody>
</table>
</div>
new Vue({
el: '#app',
data() {
return {
groupContacts:
{
4: [{
name: 'foo'
},
{
name: 'bar'
}
],
6: [{
name: 'foo bar'
}, ]
},
models: [{
id: 4,
name: 'company A'
},
{
id: 6,
name: 'company B'
},
]
}
},
computed: {
companies() {
return this.models.map(c => {
c.groups = this.groupContacts[c.id];
return c;
})
}
}
})
<body>
<div id="app">
<div v-for="(company, key) in companies" :key="key" class="table table--hover mt-4">
<h4 class="mb-4">{{company.name}}</h4>
<table>
<thead>
<tr>
<th class="text-left">Name</th>
</tr>
</thead>
<tbody>
<tr v-for="(contact, contactKey) in company.groups" :key="contactKey">
<td> {{contact.name}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>

mixing v-if with v-for should be avoided (as stated in the Vue.js docs).
Following the recommendation from the docs, you could use a computed property for this case:
<template>
<div>
<div v-for="(company, i) in companyContacts" :key="`company_${i}`" class="table table--hover mt-4">
<h4 class="mb-4" v-html="company.name" />
<table>
<thead>
<tr>
<th class="text-left" v-html="__t('name')" />
</tr>
</thead>
<tbody>
<tr v-for="(contact, j) in company.contacts" :key="`company_${i}_contact_${j}`">
<td v-html="contact.name" />
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
computed: {
companyContacts() {
return this.companyList.models.map(model => {
model.contacts = this.groupContacts[model.id]
return model
})
}
},
data: () => {
groupedContacts: {
4: [{
name: 'foo'
},
{
name: 'bar'
}
],
6: [{
name: 'foo bar'
}, ]
},
companyList: {
models: [{
id: 4,
name: 'company A'
},
{
id: 6,
name: 'company B'
},
]
}
}
}
</script>
Hope this helps!

Related

How can I make Vue Sortable work on table?

I have an issue with this library. First of all, to test the library, I made a simple example work with ul and li tags. It was straightforward. Then, I needed to do a table, and when converting my example to a table it didn't work.
Table is showed but I cannot move any row.
I'm using the cdn way.
I think is something what I missing.
html
<div id='app-example-drag' >
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th scope="col">Id</th>
<th scope="col">Name</th>
<th scope="col">Sport</th>
</tr>
</thead>
<draggable v-model="list" tag="tbody">
<tr v-for="item in list" :key="item.name">
<td scope="row">{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.sport }}</td>
</tr>
</draggable>
</table>
</div>
js
Vue.component('draggable',vuedraggable);
applicationExample = new Vue({
el: '#app-example-drag',
display: "Table",
order: 8,
data() {
return {
list: [
{ id: 1, name: "Abby", sport: "basket" },
{ id: 2, name: "Brooke", sport: "foot" },
{ id: 3, name: "Courtenay", sport: "volley" },
{ id: 4, name: "David", sport: "rugby" }
]
};
}
});
https://jsfiddle.net/0Luhd694/3/
Thanks in advance
I just ran into this same problem. I think it has something to with recent version. Replace the draggable element with a tbody and make is='draggable'.
<div id='app-example-drag' >
<table class='table'>
<thead>
<tr><th scope='col'>description</th></tr>
</thead>
<tbody tag='tbody' v-model='lista1' is='draggable' group='items'>
<tr v-for='item in lista1' :key='item.id'>
<td scope='row'>{{item.description}}</td>
</tr>
</tbody>
</table>
</div>
https://jsfiddle.net/oqf64kdx/

How to dynamically load JSON data in a table with AngularJS?

I have a column chooser widget that contains list of names with unique IDs. There is a backend service that returns an Object with the data.
output = {
id: 1, name: "john", title: "developer",
id: 2, name: "mark", title: "designer",
id: 3, name: "sally", title: "HR"
...
}
I need to create a table in html using AngularJS that dynamically adds/removes rows as I select the specific ID from my column choser.
<div ng-app="MyApp" ng-controller="MyController">
<table border = "1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Title</th>
</tr>
<tbody ng-repeat="m in output">
<tr>
<td>{{m.id}}</td>
<td>{{m.name}}</td>
<td>{{m.title}}</td>
</tr>
</tbody>
</table>
</div>
What should I add in my controller to get the desired result? Is there any way other than using $scope?
Your output object should be an array of objects:
Example Blow:
$sope.output = [
{ id: 1, name: "john", title: "developer"} ,
{ id: 2, name: "mark", title: "designer"} ,
{ id: 3, name: "sally", title: "HR"}
];
Backend service should return an Object with the data in below format to iterate that in the HTML template.
[{id: 1, name: "john", title: "developer"},
{id: 2, name: "mark", title: "designer"},
{id: 3, name: "sally", title: "HR"}]
Demo :
var module = angular.module('myApp',[]);
module.controller("myController", function($scope) {
$scope.output = [{id: 1, name: "john", title: "developer"},
{id: 2, name: "mark", title: "designer"},
{id: 3, name: "sally", title: "HR"}];
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<table border = "1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Title</th>
</tr>
<tbody ng-repeat="m in output">
<tr>
<td>{{m.id}}</td>
<td>{{m.name}}</td>
<td>{{m.title}}</td>
</tr>
</tbody>
</table>
</div>
I think you want something like this, try it and let me know
Select the multiple column names to be displayed by holding ctrl key
angular.module("MyApp", [])
.controller("MyController", ($scope) => {
$scope.columns = []
$scope.output = [{
id: 1,
name: "john",
title: "developer"
},
{
id: 2,
name: "mark",
title: "designer"
},
{
id: 3,
name: "sally",
title: "HR"
}
]
$scope.getColumns = () => {
return Object.keys($scope.ouput).filter(name => $scope.columns.indexOf(name) !== -1)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyController">
<select ng-model="columns" multiple>
<option value="id">ID</option>
<option value="name">Name</option>
<option value="title">Title</option>
</select>
<table border="1">
<tr>
<th ng-repeat="column in columns">{{column}}</th>
</tr>
<tbody>
<tr ng-repeat="m in output">
<td ng-repeat="column in columns">{{m[column]}}</td>
</tr>
</tbody>
</table>
</div>

vue.js render table with rowspans

I'm new to vue.js and I couldn't find a way to render the following data into an html table with rowspans using vue.
{
"title":"Monthly Sales",
"monthlySales":[
{
"product":"P123",
"months":[
{
"month":"January",
"unitPrice":"$80",
"unitsSold":2200
},
{
"month":"February",
"unitPrice":"$82",
"unitsSold":1900
},
{
"month":"March",
"unitPrice":"$81",
"unitsSold":1800
}
]
},
{
"product":"Q456",
"months":[
{
"month":"January",
"unitPrice":"$20",
"unitsSold":200
},
{
"month":"February",
"unitPrice":"$22",
"unitsSold":100
}
]
}
]
}
I wanted to create an output like this: http://jsbin.com/hucufezayu/edit?html,output
How can we render this kind of table with this data?
This should do the trick:
<template>
<div id="app">
<table border="1" style="border-collapse: collapse">
<thead>
<th>Product</th>
<th>Month</th>
<th>Unit price</th>
<th>No. sold</th>
</thead>
<tbody>
<template v-for="mSale in salesData.monthlySales">
<tr v-for="(month, key) in mSale.months">
<td v-if="key == 0" :rowspan="mSale.months.length"> {{mSale.product}}</td>
<td>{{month.month}}</td>
<td>{{month.unitPrice}}</td>
<td>{{month.unitsSold}}</td>
</tr>
</template>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
salesData: jsonData
}
}
}
</script>

How to handle nested structures with ng-repeat using HTML tables in Angular 1.x

Let us assume we have following data structure:
var data = [
{
name: "item name",
nestedData: [{
name: "nested name",
quantity: 1
},
{
name: "nested name 2",
quantity: 2
}
]
},
{
name: "item name 2",
nestedData: [{
name: "nested name 3",
quantity: 3
}
]
}
];
Standard behavior of ng-repeat directive will iterate over high level elements. If we run ng-repeat="item in data" it will produce two items.
Is it possible - without using custom directive - to iterate over first item ("item name") twice (multiply it by a length of nestedData array)?
The output I'd like to achieve is:
<table>
<thead>
<th>Name</th>
<th>Nested name</th>
<th>Nested quantity</th>
</thead>
<tbody>
<tr>
<td rowspan="2">item name</td>
<td>nested name</td>
<td>1</td>
</tr>
<tr>
<td ng-hide="true">item name</td>
<td>nested name 2</td>
<td>2</td>
</tr>
<tr>
<td>item name 2</td>
<td>nested name 3</td>
<td>3</td>
</tr>
</tbody>
</table>
Nested ng-repeat is not suitable in this situation because there's a need to iterate over <tr>'s.
You can use a nested ng-repeat to get your desired result as it's valid HTML to have multiple tbody elements.
Here is a JSFiddle for a working example
<table>
<thead>
<th>Name</th>
<th>Nested name</th>
<th>Nested quantity</th>
</thead>
<tbody ng-repeat="item in data">
<tr ng-repeat="nestedItem in item.nestedData">
<td rowspan="{{item.nestedData.length}}" ng-hide="$index == 1">{{item.name}}</td>
<td>{{nestedItem.name}}</td>
<td>{{nestedItem.quantity}}</td>
</tr>
</tbody>
</table>
It's a different approach to achieve expected output.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
var data = [
{
name: "item name",
nestedData: [
{
name: "nested name",
quantity: 1
},
{
name: "nested name 2",
quantity: 2
},
{
name: "nested name 3",
quantity: 3
}
]
},
{
name: "item name 2",
nestedData: [{
name: "nested name 3",
quantity: 3
}
]
}
];
var nestedData = [];
angular.forEach(data, function(item){
if(item.nestedData.length > 1){
angular.forEach(item.nestedData, function(nestedItem){
nestedData.push({
name : item.name,
nestedName: nestedItem.name,
nestedQty: nestedItem.quantity,
colspan: item.nestedData.length
});
});
} else {
nestedData.push({
name : item.name,
nestedName: item.nestedData[0].name,
nestedQty: item.nestedData[0].quantity
});
}
});
$scope.data = nestedData;
});
tr.multiple > td:first-child {
display: none;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div class="container" ng-app="myApp" ng-controller="myCtrl">
<table class="table table-stripped">
<thead>
<th>Name</th>
<th>Nested name</th>
<th>Nested quantity</th>
</thead>
<tbody>
<tr ng-repeat="item in data" ng-class="{'multiple': item.colspan > '1' && !$first}">
<td rowspan="{{item.colspan ? item.colspan : '1'}}">{{item.name}}</td>
<td>{{item.nestedName}}</td>
<td>{{item.nestedQty}}</td>
</tr>
</tbody>
</table>
</div>

Knockout JS: Filtering a list unable to bind

I'm having a hard time getting a filter working. I'm roughly following the example at http://www.knockmeout.net/2011/06/10-things-to-know-about-knockoutjs-on.html, but I just can't seem to make it work. Any help is greatly appreciated.
HTML:
<h1><b>Medical Product Survey</b></h1>
<div class='facilities available'>
<div class='bar'>
<h2>Hospitals / Facilities</h2>
<a class='button add'>+</a>
</div>
<table>
<thead>
<tr>
<th>
<input data-bind="value: filter, valueUpdate: 'afterkeydown'" />
</th>
<th>Modified</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="template: { name: 'facilitiesAvailableRow', foreach: filteredFacilties }"></tbody>
</table>
<!-- Row template for available facilities -->
<script type="text/html" id="facilitiesAvailableRow">
<tr>
<td>${name}</td>
<td>${modified}</td>
<td>${status}</td>
</tr>
</script>
</div>
<div class='facilities available'>
<div class='bar'>
<h2>Archived</h2>
</div>
<table>
<thead>
<tr>
<th><input type='search' id='facility_search' /></th>
<th>Archive Date</th>
<th>Size</th>
<th></th>
</tr>
</thead>
<tbody data-bind="template: { name: 'facilitiesArchivedRow', foreach: facilitiesArchived }"></tbody>
</table>
<!-- Row template for available facilities -->
<script type="text/html" id="facilitiesArchivedRow">
<tr>
<td>${name}</td>
<td>${modified}</td>
<td>${size}mb</td>
<td><input type='button' value='restore' /></td>
</tr>
</script>
</div>
Javascript:
//Model for facilities interaction on the front page.
var viewModel = {
facilitiesAvailable: ko.observableArray([
{ name: "Test 1", modified: "1/20/1986", status: "Good to go" },
{ name: "Test 2", modified: "1/21/1987", status: "Good to go2" },
{ name: "Test 3", modified: "1/22/1988", status: "Good to go3" },
{ name: "Test 4", modified: "1/23/1989", status: "Good to go4" },
{ name: "Test 5", modified: "1/24/1990", status: "Good to go5" }
]),
facilitiesArchived: ko.observableArray([
{ name: "Archive 1", modified: "1/20/1982", size: 123 },
{ name: "Archive 2", modified: "1/21/1983", size: 198 },
{ name: "Archive 3", modified: "1/22/1984", size: 340 }
]),
filter: ko.observable(""),
};
//Filtering.
viewModel.filteredFacilities = ko.dependentObservable(function() {
var filter = this.filter().toLowerCase();
if(!filter) {
return this.facilitiesAvailable();
} else {
return ko.utils.arrayFilter(this.facilitiesAvailable(), function(item) {
if(item.name.toLowerCase().search(filter) != -1) {
return true;
}
});
}
}, viewModel);
ko.applyBindings(viewModel);
The error I'm getting is:
Uncaught Error: Unable to parse binding attribute.
Message: ReferenceError: filteredFacilties is not defined;
Attribute value: template: { name: 'facilitiesAvailableRow', foreach: filteredFacilties }
You misspelled filteredFacilties. It should be filteredFacilities in your template binding.
Change:
data-bind="template: { name: 'facilitiesAvailableRow', foreach: filteredFacilties }"
to:
data-bind="template: { name: 'facilitiesAvailableRow', foreach: filteredFacilities }"

Categories