How to loop in ractive.js - ractivejs

I have got the following data (sample):
"data": {
"eventsHeading": "Upcoming events",
"eventsToBeDisplayed": 3,
"allEventLinkText": "See all events",
"eventsList": [
{
"eventDate": "22/12/2015",
"eventTitle": "EVENT INFO 1"
},
{
"eventDate": "22/12/2015",
"eventTitle": "EVENT INFO 2"
},
{
"eventDate": "14/01/2016",
"eventTitle": "EVENT INFO 3"
},
{
"eventDate": "14/01/2016",
"eventTitle": "EVENT INFO 4"
}
]
}
And I have something like this:
{{#eventsList}}
<tr>
<td class="date-column">{{eventDate}}</td>
<td class="text-link truncate-text"><span decorator="tooltip:{{eventTitle}}" tabindex="0">{{eventTitle}}</span>
</tr>
{{/}}
And now it will just print all the data for eventList. What I want to do is add a for loop like
i=0;i<={{eventsToBeDisplayed}};i++
so only 3 of the data will show.
What do you think is the best approach for this?

You can add an indexer to the loop and then conditionally filter inside the loop:
{{#eventsList:i}}
{{# i < eventsToDisplay}}
<tr>
<td class="date-column">{{eventDate}}</td>
<td class="text-link truncate-text"><span decorator="tooltip:{{eventTitle}}" tabindex="0">{{eventTitle}}</span>
</tr>
{{/}}
{{/}}

You could also use computed properties - I like to do this because it seems a little more explicit than a loop where the entire body is wrapped in an if, you can reuse it more easily, you can do calculations or checks against that subset of data in other areas, etc...
template
{{#topEvents}}
<tr>...</tr>
{{/}}
script (computed goes on same level as data)
computed: {
topEvents: function(){
return this.get('eventsToDisplay').slice(0, this.get('eventsToBeDisplayed'));
}
}

Related

How to create a recursive form with Angular 8?

I need to create a dynamic form with multiple nested items. I've found this example
but i'm not sure it's doing deep recursive since once i've tried to add more levels of nested items - the ui brakes down.
Here is the default json structure with my attempts :
{
key: "common",
title: "main fields",
group: [
{
key: "createdAt",
title: "Create Date",
type: "date"
},
// group:[{
// key: "foo",
// title: "Foo",
// type: "select",
// },
// {
// key: "goo",
// title: "Goo",
// type: "input",
// },
// ]
]
},
So as you can see under "common" - i've added 2 more levels of groups - the first group works fine - but the nested group with key "foo" and "goo" it's working.
I'm pretty sure the problem is in the template / markup
<form [formGroup]="filterForm" class="filter-form">
<ng-template #recursiveList let-filterFields let-fromGroup="fromGroup">
<ng-container *ngFor="let item of filterFields">
<ng-container *ngIf="item.group; else default;">
// in this area i'm not sure it's iterate over deeper nesting...
<p>{{item.key}} </p>
<div [formGroupName]="item.key">
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit:
item.group, fromGroup: {name: item.key}, isChild:true }"></ng-container>
</div>
</ng-container>
<ng-template #default>
<div class="form-group" [formGroupName]="fromGroup.name">
<input [type]="item.type" [formControlName]="item.key"
[placeholder]="item.title" [name]="item.key" />
</div>
</ng-template>
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: filterFields
}">.
From my understanding, there are two issues in the example you provided:
The data structure.
The template.
Data Structure
These are the interfaces I understand from your example:
interface Base {
key: string;
title: string;
}
interface Field extends Base {
type: 'select' | 'input' | 'date' | ...
}
interface Group extends Base {
group: Array<Field | Group>
}
So the JSON example you provided should look something like this:
{
"key": "common",
"title": "main fields",
"group": [
{
"key": "createdAt",
"title": "Create Date",
"type": "date"
},
{
"key": "test",
"title": "Test"
"group": [
{
"key": "foo",
"title": "Foo",
"type": "select"
},
{
"key": "goo",
"title": "Goo",
"type": "input"
}
]
}
]
}
Template
Let's look at a very simplified version of the form:
<form [formGroup]="filterForm">
<ng-container formGroupName="common">
<ng-container *ngTemplateOutlet="control;
context:{ controlName: 'foo', group: 'test' }">
</ng-container>
</ng-container>
<ng-template #control let-group="group" let-controlName="controlName">
<div class="col-md-3">
<div class="form-group" [formGroupName]="group">
<input type="input" [formControlName]="controlName" />
</div>
</div>
</ng-template>
</form>
The code won't work, why? Think about the ng-template as a function. If you want it to know about the formGroupName="common" it needs to be declared within that scope. What matters is the declaration context and not the invocation context, just like regular functions.
This is the working version of the above example:
<form [formGroup]="filterForm">
<ng-container formGroupName="common">
<ng-container *ngTemplateOutlet="control;
context:{ controlName: 'foo', group: 'test' }">
</ng-container>
<ng-template #control let-group="group" let-controlName="controlName">
<div class="col-md-3">
<div class="form-group" [formGroupName]="group">
<input type="input" [formControlName]="controlName" />
</div>
</div>
</ng-template>
</ng-container>
</form>
Things get trickier when you have nested and you need to use recursion.
That's why I think that the approach of using the formGroupName and formControlName directives in this scenario makes things more complicated than they are.
I suggest passing the form control directly into the input by providing the right path to it.
Here is a working example of the idea based on your original example.

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>

Bootstrap Table "data-data-field" attribute not working

I am defining a bootstrap table in HTML:
<table data-toggle="table" data-url="/api/myapi" data-data-field="objects" data-total-field="num_results" data-side-pagination="server">
<thead>
<tr>
<th>name</th>
<th>email</th>
</tr>
</thead>
</table>
The API call is being made and JSON is getting returned:
{
"num_results": 1,
"objects": [
{
"company": "My Company",
"create_date": "2018-07-04T06:29:06.290000",
"email": "test#gmail.com",
"id": 1,
"name": "Joe Bloggs"
}
],
"page": 1,
"total_pages": 1
}
I would expect that specifying data-data-field="objects" would instruct Bootstrap-Table to iterate the objects array. Alas, it doesn't seem to work..
Any ideas?
Appears to be a bug in Bootstrap-Table where it ignores dataField unless pagination is enabled. Bug report.
Fixed like this:
<table data-toggle="table" data-url="/api/myapi" data-data-field="objects" data-total-field="num_results" data-pagination=true data-side-pagination="server" data-id-field="id">

Meteor(spacebars):How to use #with and #each together

I have a helper function which is returning array of objects and each object of array has key publishers which is an object containing keys. Each key again has a object.
priceData:function(){
var colection=[
{contract:"nn",publishers:{GVM:{ask:1,bid:2},SET:{ask:6,bid:3}}},
{contract:"BB",publishers:{GVM:{ask:11,bid:99},SET:{ask:23,bid:34}}}
]
return colection
}
Now in template I am trying to use it like this
<table class="table">
<tbody>
{{#each priceData}}
<tr>
{{#with publishers}}
<td>{{ask}}</td>
<td>{{bid}}</td>
{{/with}}
</tr>
{{/each}}
</tbody>
</table>
Can I use #with in a #each iteration because it gives error like this. If not then how can I show such collection information in a table?Right now its empty table
There is no problem with each and with, you can combine them and nest at your will. The only thing to keep in mind is context: each of the blocks goes deeper into the context but at the same moment allows access outer contexts (which I personally wouldn't recommend). So if you remove all the typos and use the whole code in this way:
<table class="table">
<tbody>
{{#each priceData}}
<tr>
{{#with publishers}}
<td>{{ask}}</td>
<td>{{bid}}</td>
{{/with}}
</tr>
{{/each}}
</tbody>
</table>
then everything will be fine. But make sure the data structure corresponds with this code. But it doesn't.
What you need is to access ask and bid within publishers through either GVM or SET. Let's pretend you need the former:
<table class="table">
<tbody>
{{#each priceData}}
<tr>
{{#with publishers}}
<td>{{GVM.ask}}</td>
<td>{{GVM.bid}}</td>
{{/with}}
</tr>
{{/each}}
</tbody>
</table>
Let's deconstruct the whole code to make the picture clearer.
When you use
{{priceData}}
then you link to what the helper returns, i.e.
[{
contract: "nn",
publishers: {
GVM: {
ask: 1,
bid: 2
},
SET: {
ask: 6,
bid: 3
}
}
}, {
contract: "BB",
publishers: {
GVM: {
ask: 11,
bid: 99
},
SET: {
ask: 23,
bid: 34
}
}
}]
So when you use
{{#each priceData}}
...
{{/each}}
you dive into the context of what the helper returns and iterate over items of the array. For example, the first one would be
{
contract: "nn",
publishers: {
GVM: {
ask: 1,
bid: 2
},
SET: {
ask: 6,
bid: 3
}
}
}
Next what you do is
{{#with publishers}}
...
{{/with}}
For the first item of array the context is
GVM: {
ask: 1,
bid: 2
},
SET: {
ask: 6,
bid: 3
}
and for the second is
GVM: {
ask: 11,
bid: 99
},
SET: {
ask: 23,
bid: 34
}
Then you're trying
{{ask}}
and this is where your code fails because there's no ask property of the structure within current context. But there are properties GVM and SET. So pick one you like and use it like this:
{{GVM.ask}}
Hope it helps.

How to iterate over the current object in an array and only print the values in Handlebars?

Consider this array :
var urls = [
{ alias : 'home',
path : '/page/home',
title: 'Home',
desc : 'simple test'
},
{ alias : 'home1',
path : '/page/home1',
title: 'Home1',
desc : 'simple test1'
},
{ alias : 'home2',
path : '/page/home2',
title: 'Home2',
desc : 'simple test2'
},
];
How can I iterate over all the properties inside an object in the array and print their values? I don't care about the names of the properties.
I would like to do someting like:
{{#urls}}
<tr>
{{#each .}}
<td>
{{value}}
</td>
{{/each}}
</tr>
{{/urls}}
And this would output
<tr><td>home</td><td>/page/home</td><td>Home</td><td>simple test</td></tr>
<tr><td>home1</td><td>/page/home1</td><td>Home1</td><td>simple test1</td></tr>
<tr><td>home2</td><td>/page/home2</td><td>Home2</td><td>simple test2</td></tr>
I don't want to type:
{{#urls}}
<tr>
<td>
{{alias}}
</td>
<td>
{{path}}
</td>
<td>
{{title}}
</td>
<td>
{{desc}}
</td>
</tr>
{{/urls}}
I might have more than 10 properties so I don't want to type their names anywhere I want to iterate over them.
My problem is I don't know how to access the current object in iteration or pass it to a helper. Do you know how to do that in Handlebars?
I don't want to put the values in arrays instead because I still need to see the properties for debugging.
working jsFiddle
Helper:
Handlebars.registerHelper('renderUrl', function(urls) {
var dom = '';
urls.forEach(function (url) {
dom += '<tr>';
for(key in url) {
dom += '<td>' + url[key] + '</td>'
}
dom += '</tr>';
});
return new Handlebars.SafeString(dom);
});

Resources