Durandal recursive composing of template - knockout-2.0

I am trying to compose a template in my application recursively. I have a multi-dimensional array that I am trying to flatten out into table rows. I can get the first level to work great, but I cannot get subsequent levels to render properly.
I know that Durandal requires the view to have a single root element. I am using a virtual container to compose my template.
Here is my parent view
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th></th>
<th>Condition</th>
<th>Index Field</th>
<th>Operator</th>
<th>Value</th>
</tr>
</thead>
<tbody data-bind="foreach:queryItems">
<!--ko compose: {view:'documentquery/querytemplate.html', model: $data, preserveContext: true } --><!--/ko-->
</tbody>
</table>
Here is my template
<tr>
<td data-bind="text: $data.subItems().length"></td>
<td style="width: 10%;"><button type="button" class="btn btn-sm btn-info" data-bind="click: $root.onAddSubconditionClick">Add sub-condition</button></td>
<td data-bind="text: $data.condition"></td>
<td data-bind="text: $data.indexField"></td>
<td data-bind="text: $data.operator"></td>
<td data-bind="text: $data.value"></td>
</tr>
<!--ko foreach: $data.subItems-->
<!--ko compose: {view:'documentquery/querytemplate.html', model: $data, preserveContext: true } --><!--/ko-->
<!--/ko-->
Everything is fine until I next an element and then the composition still works but I don't get the final element.
Does anyone have a work-around for this one?
Thank you.

I would use a view and view model pair instead of trying to debug what the state is at that point. It's not to say you can't do it without it, but it's just easier.
<!--ko foreach: subItems-->
<!--ko compose: { model:'viewmodels/documentquery/querytemplate', activationData: { data: $data }} -->
<!--/ko-->
<!--/ko-->
And then give your query template a view model called querytemplate.js
define([], function () {
var ctor = function () {};
ctor.prototype.activate = function (data) {
var self = this;
self.subItem = self.settings.data;
}
return ctor;
});
You will need to update your template to bind it using the with binding to subitem but that should be pretty straight forward. Also when you are referencing a property in the current context you don't need to use $data.whatever you can just use whatever.

This is what I ended up with:
<div class="panel-heading" style="font-size: 12px; font-weight: 700; padding: 2px 0;">
<!--ko compose: {view: 'widgets/querybuilder/predicate-row/predicate-row', model: 'widgets/querybuilder/predicate-row/predicate-row', activationData: {rowIndex: -1, predicate: predicate,settings: {root: isRoot()}}}-->
<!--/ko-->
</div>
<!--ko foreach: predicate().conditions-->
<!--ko if: $data.name == 'condition'-->
<!--ko compose: {view: 'widgets/querybuilder/condition-row/condition-row', model: 'widgets/querybuilder/condition-row/condition-row', activationData: {rowIndex: $index, condition: $data, indexes: $parent.indexes, root: false}}--><!--/ko-->
<!--/ko-->
<!--ko if: $data.name == 'predicate'-->
<!--ko compose: {model: 'widgets/querybuilder/predicate/predicate', view: 'widgets/querybuilder/predicate/predicate', activationData: {rowIndex: $index, predicate: $data, indexes: $parent.indexes, root: false } }--><!--/ko-->
<!--/ko-->
<!--/ko-->
Basically, I needed 2 view/models. This one is the root and it just composes more of itself.
The root is composed like this from the parent view:
<div id="query-builder-root">
<!--ko compose: {model: 'widgets/querybuilder/predicate/predicate', view: 'widgets/querybuilder/predicate/predicate', activationData: {predicate: predicate(), indexes: indexes, root: true } }--><!--/ko-->
</div>
I think the reason this works for me is because it is generating a new root on each row.

Related

Uncaught IntegrationError: Invalid stripe.redirectToCheckout parameter: items.0.price is not an accepted parameter

My goal is to use react front end to setup a stripe checkout using node. I mapped the apiId using the key prop to iterate over all the items in my list. But there is an integration warning inside the console and I'm not sure why. I've added the js.stripe script src into my html file so I don't understand why an API call is not being made.
function checkout() {
stripe.redirectToCheckout({
items: items.map(item => ({
quantity: item.quantity,
price: item.apiId
})),
successUrl: "https://www.website.com/success",
cancelUrl: "https://www.website.com/canceled",
})
}
return (
<div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Image</th>
<th>Quanity</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{items.map((item) => (
<tr key={item.apiId}>
<td>{item.name}</td>
<td>
<img
src={`/images/${item.apiId}.jpg`}
alt={item.name}
width={180}
/>
</td>
<td>{item.quantity}</td>
<td>{formatPrice(item.price)}</td>
</tr>
))}
<tr>
<td style={{ textAlign: "right" }} colSpan={3}>
Total:
</td>
<td>{formatPrice(totalPrice(items))}</td>
</tr>
<tr>
<td style={{ textAlign: "right" }} colSpan={4}>
<button onClick={checkout}>Complete checkout</button>
</td>
</tr>
</tbody>
</table>
</div>
)
}
The items field only accepts an array of SKU/Plans and a quantity. If you're using Prices you want to use lineItems: https://stripe.com/docs/js/checkout/redirect_to_checkout#stripe_checkout_redirect_to_checkout-options-lineItems

How to set a default sorting to a jh-table?

I have a jh-table with a working header that sorts by each field, but I would like to have it sorted by a specific field by default when the page is loaded.
I tried to set orderBy in the ng-repeat of the tbody, but I think it is causing a conflict with jh-sort and the sorting buttons stop working properly.
Is there a way to define the default sorting in the thead?
This is my thead:
<thead>
<tr jh-sort="vm.predicate" ascending="vm.reverse" callback="vm.transition()">
<th jh-sort-by="id"><span data-translate="global.field.id">ID</span> <span class="glyphicon glyphicon-sort"></span></th>
<th jh-sort-by="name"><span data-translate="global.field.name">Nome</span> <span class="glyphicon glyphicon-sort"></span></th>
<th jh-sort-by="date"><span data-translate="cccApp.certificateRequestBatch.creationDate">Data de cadastro</span> <span class="glyphicon glyphicon-sort"></span></th>
<th jh-sort-by="status"><span data-translate="global.field.status">Situo</span> <span class="glyphicon glyphicon-sort"></span></th>
<th></th>
</tr>
</thead>
In your certificateRequestBatch entity state file (certificate-request-batch.state.js), you can set the default params for sorting/pagination via the sort key:
params: {
page: {
value: '1',
squash: true
},
sort: {
value: 'id,asc',
squash: true
},
search: null
},
A full example of the entity state for AngularJS can be seen here.

V-Model won't update

I have made a simple Datatable with search input,
Student.js
Vue.component('student-list',{
props: ['students'],
template:`
<div class="table-responsive">
<table class="table table-hover mails m-0 table table-actions-bar">
<thead>
<tr>
<th>10</th>
<th>9</th>
<th>8</th>
<th>7</th>
<th>6</th>
<th>5</th>
<th>4</th>
<th>3</th>
<th>2</th>
<th>1</th>
</tr>
</thead>
<tbody>
<student v-for="student in searchResults" :student="student"></student>
</tbody>
</table>
</div>
`,
computed:
{
searchResults: function(student){
var self = this;
return this.students.filter(
function(student){
if(this.searchQuery == '' || this.searchQuery == null) { return true; }
console.log(this.searchQuery);
return (student.toString().indexOf(this.searchQuery) != -1);
}
);
}
},
// v-if="searchResults(student)"
});
Vue.component('student',{
props: ['student'],
template:`
<tr>
<td>
{{ student.name }} {{ student.lname }}
</td>
<td>
<i class="md-phone"></i>
<i class="ion-woman"></i>
<i class="ion-man"></i>
<i class="ion-android-settings"></i>
</td>
<td>
{{ student.address }}
</td>
<td>
{{ student.totalTopay }}
</td>
<td>
{{ student.lessonsTaken }}
</td>
<td>
{{ student.cancelCount }}
</td>
<td>
{{ student.phone }}
</td>
<td>
{{ student.momPhone }}
</td>
<td>
{{ student.dadPhone }}
</td>
<td>
{{ student.email }}
</td>
</tr>
`
});
new Vue({
el: '#content-page',
data: {
'searchQuery': ''
}
});
HTML
....
<div id="content-page">
<input type="text" id="search" class="form-control" placeholder="Search..." v-model="searchQuery">
</div>
....
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="{{ asset('js/students.js') }}"></script>
...
Now, for some reason whenever I leave the search blank it works as needed, but when I change the input field by typing in the text box(which his v-model is attached to the searchQuery variable), nothing changes, and when I call it (this.searchQuery) on the Dev Tools console, it says undefined. What am I missing?
Thanks in advance everyone!
I've edited your code a little bit to make it work and removed some lines of code, because I don't want to recreate the whole student object.
I passed the search-query as prop and added <student-list /> to your template.
In your filter function I replaced toString with JSON.stringify, because student is an object and toString prints out [object Object].
Also, I changed the props object and added their types.
And one last tiny error:
Interpolation inside attributes has been deprecated. Use v-bind or the
colon shorthand instead.
Don't use href="tel:{{ student.phone }}" use something like this instead :href="'tel:' + student.phone".
Vue.component('student-list',{
props: {
students: {
type: Array
},
searchQuery: {
type: String
}
},
template:`<div class="table-responsive">
searchQuery: {{searchQuery}}
<table class="table table-hover mails m-0 table table-actions-bar">
<thead>
<tr>
<th>10</th>
<th>9</th>
<th>8</th>
<th>7</th>
<th>6</th>
<th>5</th>
<th>4</th>
<th>3</th>
<th>2</th>
<th>1</th>
</tr>
</thead>
<student v-for="student in searchResults" :student="student"></student>
</tbody>
</table>
</div>`,
computed: {
searchResults() {
return this.students.filter(student => JSON.stringify(student).indexOf(this.searchQuery) != -1);
}
},
});
Vue.component('student',{
props: {
student: {
type: Object
}
},
template:`<tr>
<td>
{{ student.name }} {{ student.lname }}
</td>
<td>
<a :href="'tel:' + student.phone" title="התקשר" class="table-action-btn">tel-icon</a>
</td>
</tr>`
});
new Vue({
el: '#content-page',
data: {
'searchQuery': ''
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
</head>
<body>
<div id="content-page">
<student-list :search-query="searchQuery" :students="[{
id: 1,
name: 'Peter',
lname: 'Parker',
phone: 12345
},{
id: 2,
name: 'Bat',
lname: 'Man',
phone: 1234
}]"></student-list>
<input type="text" id="search" class="form-control" placeholder="Search..." v-model="searchQuery">
</div>
</body>
</html>
And my second solution
Add this to your computed:
computed: {
searchQuery() {
return this.$parent.searchQuery || '';
},
...
}
...
You access the parent of student-list, but I think this is not a nice way, because you can't see from where do you get this data.
Hope this will help you a little bit.

Jquery datatable not working for first row in colspan

I tried to bind dynamically partial view its working but for jquery
datatable against first row what I specified below its not working.
#model IEnumerable<PurModelClass>
<table id="stock-audit-table" class="table table-striped table-bordered dt-responsive nowrap">
<thead>
<tr>
#*<th>Purchase From</th>
<th>Purchase Date</th>*#
<th>Item Name</th>
<th>Qty</th>
<th>Unit</th>
<th>Cost</th>
<th>Net Amount</th>
<th>Discount</th>
<th>Vat</th>
<th>Gross</th>
</tr>
</thead>
<tbody id="stock-audit-tbody">
#foreach (var item in Model)
{
<tr><td colspan="8">Invoice No: #item.PurInvNo Inv Date: #($"{#item.PurDate:MMM-dd-yyyy}") Vendor: #item.VendorName </td></tr>
foreach (var detail in item.PurDetails)
{
<tr>
<td>#detail.StockName</td>
<td>#detail.PurQty</td>
<td>#detail.UnitName</td>
<td>#detail.UnitPrice</td>
<td>#detail.Total</td>
<td>#detail.Discount</td>
<td>#detail.VAT</td>
<td>#detail.NetAmount</td>
</tr>
}
}
</tbody>
<tfoot>
</tfoot>
</table>
This the datatable script, is there any way to implement jquery datatable by demanding specific row with colspan properties.
<script type="text/javascript">
$(document)
.ready(function() {
$('#stock-audit-table').DataTable({
"autoWidth": false,
"order": [[0, "desc"]]
});
}
);
</script>
Try the rowGroup extension. You can use startRender and endRender to customize the summary data
$('#stock-audit-table').DataTable({
"autoWidth": false,
"order": [[0, "desc"]],
"rowGroup": {
dataSrc: 0,
startRender: function(groupName){
return $('<tr/>').append("<td colspan="8">TODO put data here</td>");
}
}
});
<tbody id="stock-audit-tbody">
#foreach (var item in Model)
{
foreach (var detail in item.PurDetails)
{
<tr>
<td>
<!-- Define a key to be referenced by the
'datasrc' property in dataTables -->
#item.GroupName
</td>
<td>#detail.StockName</td>
<td>#detail.PurQty</td>
<td>#detail.UnitName</td>
<td>#detail.UnitPrice</td>
<td>#detail.Total</td>
<td>#detail.Discount</td>
<td>#detail.VAT</td>
<td>#detail.NetAmount</td>
</tr>
}
}
</tbody>

Magnific popup: show “The image could not be loaded” when slide images

I am using Magnific popup to slide my images in table, here is part of html code:
.....
<div id="portfolio" class="clearfix">
<table class="table table-striped table-bordered table-hover table-condensed" id="dataTables-brickset">
<thead>
<tr><th>Image</th></tr>
</thead>
<tbody>
<tr>
<td>
<a href="http://www.brickshelf.com/gallery/mikezang/clonebrick/stardiamond/80029.jpg" title="Star Diamond/Military/CV-12 Reconnaissance Vehicle">
<img src="http://www.brickshelf.com/gallery/mikezang/clonebrick/stardiamond/80029.jpg" height="64" />
</a>
</td>
</tr>
<tr>
<td>
<a href="http://www.brickshelf.com/gallery/mikezang/clonebrick/stardiamond/80030.jpg" title="Star Diamond/Military/AH-1 Aemed Helicopter">
<img src="http://www.brickshelf.com/gallery/mikezang/clonebrick/stardiamond/80030.jpg" height="64" />
</a>
</td>
</tr>
.....
<script>
$(function(){
$('#portfolio').magnificPopup({
delegate: 'a',
type: 'image',
image: {
cursor: null,
titleSrc: 'title'
},
gallery: {
enabled: true,
preload: [1, 1], // Will preload 0 - before current, and 1 after the current image
navigateByImgClick: true
}
});
});
</script>
....
The thumbnail images are good as below:
it is also ok when I click on image as such:
when I want to slide images, I got screen like this: !
How can I solve this issue?

Resources