Printing multidimensional arrays with Mandrill handlebars - mailchimp

All questions I can find pertaining to this topic are >4 years old, and no longer seem to be relevant. Such as How would I render a list with Mandrill template out of array passed in MERGE VARS
How can I print multidimensional arrays in a Mandrill template using handlebars?
Consider the following data structure
"global_merge_vars": [
{
"name": "items",
"content": [
{
"ID": 5009,
"Qty": 2,
"Name": "Firefly Candle - Green Bamboo"
}
{
"ID": 3544,
"Qty": 1,
"Name": "Aeropress Coffee Maker"
}
]
}
]
Then, in my template I have
<table>
{{#each items}}
<tr>
<td><img src="https://example.com/imgs/{{ID}}.jpg" /></td>
<td>{{Qty}}x {{Name}}</td>
</tr>
{{/each}}
</table>
And it always comes though empty. No content at all. I've tried {{#each items.content}}. I've tried {{this.Qty}}. I've tried every combination and every permutation thereof I can find online, but none of it seems to work.

The solution did end being {{#each items}} AND {{this.Qty}}. Thus making the final template look like:
<table>
{{#each items}}
<tr>
<td><img src="https://example.com/imgs/{{this.ID}}.jpg" /></td>
<td>{{this.Qty}}x {{this.Name}}</td>
</tr>
{{/each}}
</table>

Related

calculate shopping cart sale total in laravel and vuejs

i'm developing a shopping cart with laravel and vuejs i'm new to programming. I want to get the total amount of the products in the shopping cart but I don't understand how to do it.
any help is appreciated
I am using vuejs components, in my data element I have a cart that is the one who has the cart with the products.
<script >
import Axios from 'axios'
export default {
data(){
return{
csrf: document.head.querySelector('meta[name="csrf-token"]').content,
carrito: [],
}
},
mounted(){
Axios.get('carrito')
.then(Response => {this.carrito = Response.data})
},
}
</script>
Inside my template I have a table where it traverses the products with the v-for directive, I would like to have the total in a but I don't understand how I can do this operation
<table class="table">
<thead>
<tr>
<th scope="col">Producto</th>
<th scope="col">Cantidad</th>
<th scope="col">precio</th>
<th scope="col">total</th>
<th scope="col">accion</th>
</tr>
</thead>
<tbody >
<tr v-for="(ProductCart, index) in carrito" :key="index.id">
<td>{{ProductCart.name}}</td>
<td>{{ProductCart.cantidad}}</td>
<td>{{ProductCart.precio}}</td>
<td>{{ProductCart.cantidad * ProductCart.precio}}</td>
<td>
<form action="" method="post">
<input :value="csrf" type="hidden" name="_token" >
<input type="hidden" name="id" id="id" value="<?php echo $producto['id'] ?>" >
<button name="btnAccion" value="Eliminar" class="btn btn-danger" type="submit"> Remove</button>
</form>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th colspan="2"></th>
<td>
<h2>Total</h2>
</td>
<td align="right">
<h3> </h3>
</td>
</tr>
</tfoot>
</table>
so I receive the data
{1: {id: "1", name: "Motor 1", cantidad: "1", precio: "20.00"}}
1: {id: "1", name: "Motor 1", cantidad: "1", precio: "20.00"}
cantidad: "1"
id: "1"
name: "Motor 1"
precio: "20.00"
It seems like computed properties would work best in your case. I'm not sure how the data in carrito is structured, but assuming it looks something like this:
carrito: {
1: {
"id":"1",
"name":"Motor 1",
"cantidad":"1",
"precio":"20.00"
}
...
}
...you would have to loop through carrito, take the precio times the cantidad of each object, and add it to a running total. Here's an example:
total: function () {
let total = 0;
Object.values(this.carrito).forEach(
(item) => (total += item.precio * item.cantidad)
);
return total;
}
Object.values returns an array of just the values of carrito, which is then iterated through by .forEach, and its precios/cantidads are multiplied together, and then then added to the running total, and returned.
total would go in computed: {}, within your Vue instance. Computed properties are re-evaluated when dependant data changes, so total will be reevaluated whenever carrito changes. You can then place it wherever you want in the page, like a normal data property:
...
<tfoot>
<tr>
<th colspan="2"></th>
<td>
<h2>Total</h2>
</td>
<td align="right">
<h3>{{ total }}</h3>
</td>
</tr>
</tfoot>
...
Check out the demo here, (network fetching is simulated, so it takes two seconds to load carrito) and its code here.
More info on computed properties:
Computed Properties and Watchers: Vue Docs
Computed Properties: Vue Mastery
You could create a computed property or a method for your component. There you could loop all the items and add each price to a total variable. Then you return the total.
Have a look here: Vue computed properties

Order table column in asc/desc order with datatables

I am using datatable v1.10.13. I am having problem displaying data according to created_at of laravel. While fetching data, I am getting posts according to created_at desc but while displaying data it shows data in alphabetical order. How can I get latest post first with datatables? I am not populating data with ajax.
I have this query to get posts in PostController:
$allPost = $this->post->orderBy('created_at', 'desc')->get();
I have following code in html:
<div class="dt-responsive table-responsive">
<table id="posts-data" class="table table-striped table-bordered nowrap dataTable no-footer" role="grid" aria-describedby="basic-col-reorder_info">
<thead>
<tr>
<th>Title</th>
<th>Category</th>
<th>Status</th>
<th>Image</th>
<th>Action</th>
</tr>
</thead>
<tbody>
#if(!empty($allPost)) #foreach($allPost as $postsLists)
<tr>
<td>{{ $postsLists->title }}</td>
<td>
#if(!empty($postsLists->categories)) #foreach($postsLists->categories as $cat_lists)
<i class="icofont icofont-arrow-right"></i> {{$cat_lists->name}}
<br> #endforeach #endif
</td>
<td>{{ $postsLists->status }}</td>
<td><img src="{{ $postsLists->image }}" alt="" width="100"></td>
<td>View image</td>
<td>
<a href="{{ route('posts.edit', $postsLists->id) }}" class="btn btn-primary btn-sm pull-left" style="margin-right: 5px">
<span class="icofont icofont-ui-edit"></span>
</a>
<a class="pull-left" onclick="return confirm('Are you sure you want to delete this post?')">
<form method="POST" action="{{ route('posts.destroy', $postsLists->id) }}" accept-charset="UTF-8">
<input name="_method" type="hidden" value="DELETE">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<button class="btn btn-danger btn-sm" type="submit"><span class="icofont icofont-ui-delete"></span></button>
</form>
</a>
</td>
</tr>
#endforeach #endif
</tbody>
</table>
I have following code to call initialize datatable:
$('#posts-data').DataTable({
colReorder: true,
pageLength: 0,
lengthMenu: [20, 40, 60, 80, 90, 100],
});
Adding order: [] should solve your problem:
$('#posts-data').DataTable({
colReorder: true,
order: [],
pageLength: 0,
lengthMenu: [20, 40, 60, 80, 90, 100],
});
By default datatable will sort the table by first column, so the title in your case, but since you are providing the inital ordering from your data, you can set the default ordering to an empty array.
Some suggestions:
1) The DataTables "no default sort" option
The solution proposed by #Remul - order: [] - should have worked. This suggests there is a problem elsewhere, potentially interfering with DataTables. Some suggestions:
(a) The foreach iterator is doing something surprising. Unlikely, but you may want to check that.
(b) Can you get DataTables to initially sort by any other field using order []? For example, sort by the first column in descending order:
order: [[ 0 , "desc" ]],
(c) Is your DataTables initializer placed inside a document ready function?
$(document).ready(function() {
$('#posts-data').DataTable({
colReorder: true,
order: [],
pageLength: 0,
lengthMenu: [20, 40, 60, 80, 90, 100],
});
});
I'm sure it is - but just in case...
2) Add your created_at data to the table
I assume this is a date, or datetime field. DataTables has somewhat limited support for sorting dates and times (without using plugins) so I recommend formatting your data as a sortable string field (e.g. YYYY-MM-DD HH24:MI:SS or whatever variant works for you).
(a) You can add it as a hidden field.
(b) Just a suggestion: Consider adding it as a visible field. Otherwise, users may sort the data by some other column, and they will not be able to return to the originally displayed sort order (created_at descending). Something like this:
"columnDefs": [
{
"targets": 5, // assuming the datetime is the 6th column
"visible": false
}
]
3) Build up from a Basic Solution
Strip out everything which is not needed to demonstrate the sort order you want. So, for example, remove the colReorder; remove all columns except the first one, and so on. Assuming it starts working at some point, you can add items back one-by-one.

Vue Component not works even i register it follow the official manual

just a very new to Vue 2.0, i actually use if for Laravel 5.4, now you can see from below link that i created one component which name is "canvas-chart", what actually i want show is a filterable table, and then to get more Json data from next steps, but now it always show me "Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option." , can not understand that i follow the official documentation to use it why it's not work?
new Vue({
el:'#filterTest',
data:{
keywords:[{"id":"9","name":"missy","phone":"21324234532"},
{"id":"3","name":"Mahama","phone":"345604542"},
{"id":"2","name":"Bernard","phone":"241242542"}]
},
computed: {
filterM: function () {
var self = this
return self.filter(function (word) {
return user.name.indexOf(self.searchQuery) !== -1
})
}
}
});
Vue.component('canvas-chat',{
template:'#canvasChart',
props:['keywordsData']
})
<script type="text/x-template" id="canvasChart">
<table>
<thead>
<tr>
<th v-for="key.id in keywordsData">
<span class="arrow" ></span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="key.name in keywordsData">
<td v-for="key.phone in keywordsData"> {{key.name}}</td>
</tr>
</tbody>
</table>
</script>
<div id="filterTest">
<canvas-chat keywordsData="keywords" ></canvas-chat>
</div>
You have a few issues:
You must register the canvas-chat component first before you use it in the main component (put Vue.component('canvas-chat', ... before new Vue(...)).
Your v-for loops are wrong. They should be like v-for="key in keywordsData", not v-for="key.id in keywordsData".
The v-fors on the tr and td elements don't make sense; you will have to process the keywordsData first (perhaps in a computed property) to get it into the correct format you want, then loop over that.
You must bind the keywordsData prop in the template like this: :keywords-data="keywords".

Spring MVC/Thymeleaf: two-dimensional iteration through Map where each value is a set

I'm using this data structure: Map<String,Set<String>> and I want to iterate through both dimensions in Thymeleaf. The map is a set of pairs where a directory is assigned to the files that are in this directory.
<table th:each="subdirectory : ${subdirectories}">
<tr th:each="files : ${subdirectories[__${subdirectory}__]}">
<td th:each="file: ${files}" th:text="file"></td>
</tr>
</table>
But all it renders are three empty <table></tables>. There is definitely data in the map.
My data structure looks like this:
{
"dirA/dir1": [
"file1.ext"
],
"dirA/dir2": [
"file1.ext",
"file2.ext"
]
}
For each directorypath I'd like to get a table (or a list) with all the files. Don't know how to get the value from the map and iterate through it.
Since iteration over a map returns an Entry (http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#iterable-values) you can use should access the value of the subdirectory (entry).
<table th:each="subdirectory : ${subdirectories}">
<tr th:each="files : ${subdirectory.value}">
<td th:each="file: ${files}" th:text="${file}"></td>
</tr>
</table>
If you want to display the directory, you can use ${subdirectory.key}
(Also fixed the variable in the variable in the td's th:text)
With a lot of try and error and a lot of using Google I found the following, working solution:
<table th:each="subdirectory : ${subdirectories}">
<thead>
<th th:text="${subdirectory.key}"></th>
</thead>
<tbody>
<tr th:each="file : ${subdirectory.value}">
<td th:text="${file}"></td>
</tr>
</tbody>
</table>

jQuery .append(html) command appending incorrectly

I am using a jQuery/Ajax call to append a partial view to a table. When the page loads, the partial view is created correctly. However, once the use attempts to append another item to the table, the formatting is incorrect despite the exact same partial view being used.
Here is the table. When this loads, the items are loaded onto the page correctly as the picture below illustrates:
<table id="fixedRows">
<thead>
<tr>
<th>State Code</th>
<th>Agent ID</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.BankListAgentId)
{
if (!String.IsNullOrWhiteSpace(item.AgentId) && item.FixedOrVariable.Equals("F"))
{
#Html.EditorFor(model => item, "FixedPartialView")
}
}
</tbody>
</table>
<br />
Add Another
Once you click the Add another link, this jQuery/Ajax call is activiated
<script type="text/javascript">
$(document).ready(function () {
$(".addFixed").click(function () {
//alert('test');
event.preventDefault();
$.ajax({
url: '#Url.Action("BlankFixedRow", "BankListMaster")',
cache: false,
success: function (html) { $("#fixedRows").append(html); }
});
});
$("#addVariable").click(function () {
event.preventDefault();
$.ajax({
url: '#Url.Action("BlankFixedRow", "BankListMaster")',
cache: false,
success: function (html) { $("#variableRows").append(html); }
});
});
});
</script>
That jQuery calls this method from the controller
public ViewResult BlankFixedRow()
{
SelectList tmpList = new SelectList(new[] { "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NA", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "US", "VT", "VI", "VA", "WA", "WV", "WI", "WY" });
ViewBag.StateCodeList = tmpList;
return View("FixedPartialView", new BankListAgentId());
}
Which calls this partial view
EDIT(a couple people noticed the id tag missing from the <tr>, this was just a copy/paste error for this post, the actual code has the id tag)
#model Monet.Models.BankListAgentId
#{
Layout = null;
}
#using (Html.BeginCollectionItem("BankListAgentId"))
{
<tr id="item-#Model.AgentId">
<td>
#Html.DropDownListFor(model => model.StateCode,
(SelectList)ViewBag.StateCodeList, Model.StateCode)
</td>
<td>
#Html.EditorFor(model => model.AgentId)
#Html.ValidationMessageFor(model => model.AgentId)
</td>
<td>
delete
</td>
#*<td>Delete</td>*#
</tr>
}
This is the same partial view that is called when the page first loads, which part of why I'm confused that the end result after hitting the Add another link turns out looking like this
EDIT
If you hit the Add another link twice, this is the result
EDIT
I've tried the following jQuery sucess commands with no luck
success: function (html) { $("#fixedRows > tbody:last").append(html); }
success: function (html) { $("#fixedRows tr:last").after(html); }
success: function (html) { $("#fixedRows > tbody").append(html); }
Here is the HTML that is rendered after the Add another link is clicked. I included the opening <form> tag for the form below it to show that the new rows are nowhere to be found.
<form action="/BankListMaster/Edit/11" method="post">
<fieldset>
<legend>Stat(s) Fixed</legend>
<table id="fixedRows">
<tr>
<th>State Code</th>
<th>Agent ID</th>
<th></th>
<th></th>
</tr>
<tr id="item-1164998320">
<td>
<select id="item_StateCode" name="item.StateCode"><option value="">HI</option>
<option>AL</option>
..
<option>WY</option>
</select>
</td>
<td>
<input class="text-box single-line" id="item_AgentId" name="item.AgentId" type="text" value="1164998320" />
<span class="field-validation-valid" data-valmsg-for="item.AgentId" data-valmsg-replace="true"></span>
</td>
<td>
delete
</td>
</tr>
<tr id="item-1164998219">
<td>
<select id="item_StateCode" name="item.StateCode">
<option value="">HI</option>
<option>AL</option>
..
<option>WY</option>
</select>
</td>
<td>
<input class="text-box single-line" id="item_AgentId" name="item.AgentId" type="text" value="1164998219" />
<span class="field-validation-valid" data-valmsg-for="item.AgentId" data-valmsg-replace="true"></span>
</td>
<td>
delete
</td>
</tr>
<tr id="item-0352926603">
<td>
<select id="item_StateCode" name="item.StateCode">
<option value="">GA</option>
<option>AL</option>
..
<option>WY</option>
</select>
</td>
<td>
<input class="text-box single-line" id="item_AgentId" name="item.AgentId" type="text" value="0352926603" />
<span class="field-validation-valid" data-valmsg-for="item.AgentId" data-valmsg-replace="true"></span>
</td>
<td>
delete
</td>
</tr>
</table>
<br />
Add Another
</fieldset>
</form>
Add Another
<form action="/BankListMaster/Edit/11" method="post">
EDIT
Here is a screen shot of the table in Chrome's debugger after the Add Another link is clicked. As you can see, the data pulled from the table is loaded properly in respective <tr> tags, however the empty row (which is sent via the same partial view as the rest) doesn't have any of the same table elements. The screen shot below that shows Response, however, which does include the <tr> tags
EDIT
I put a console.log(html) line in the success Ajax function so it now reads
success: function (html) {
console.log(html);
$("#fixedRows > tbody").append(html);
}
Here is the console output (state edited for readability)
<input type="hidden" name="BankListAgentId.index" autocomplete="off" value="3f7e0a92-8f20-4350-a188-0725919f9558" />
<tr>
<td>
<select id="BankListAgentId_3f7e0a92-8f20-4350-a188-0725919f9558__StateCode" name="BankListAgentId[3f7e0a92-8f20-4350-a188-0725919f9558].StateCode">
<option>AL</option>
..
<option>WY</option>
</select>
</td>
<td>
<input class="text-box single-line" id="BankListAgentId_3f7e0a92-8f20-4350-a188-0725919f9558__AgentId" name="BankListAgentId[3f7e0a92-8f20-4350-a188-0725919f9558].AgentId" type="text" value="" />
</td>
<td>
delete
</td>
</tr>
What a complete nightmare...
First off, the HTML that was being returned as viewable in Chrome's debugger was fine, however when I clicked on "View Source" for the page, I could not see anything but what was originally loaded. After finding this post, I found that this is normal. I then used this Chrome add-on to finally see that the <tr> and <td> tags were being stripped out. By simply adding an opening and closing tag to the append statement, I got the returned items to append to the table.
$(".addFixed").click(function () {
$.ajax({
url: '#Url.Action("BlankFixedRow", "BankListMaster")',
dataType: 'html',
cache: false,
success: function (html) {
$("#fixedRows > tbody").append('<tr>' + html + '</tr>');
}
});
});
I see a few things here. You're referencing <tbody> in some of your code but I don't see it anywhere in the page. So first I would suggest using <thead> and <tbody>. In your partial view I see <tr "item-#Model.AgentId"> which should have an id.
You should also remove the onclick handler and the delete button and put that in with the rest of your JavaScript. Set a class on your delete links instead.
For links that don't need urls and are only used for attaching JavaScript handlers, I recommend using href="javascript:void(0)" as this would prevent the browser from doing anything special with href="#" so then you'll be able to remove the calls to preventDefault().
As to the source of your problem, $("#fixedRows tbody").append(html) is the code you want so no need to try after(). It looks like your html is getting stripped. Try setting the dataType attribute in the $.ajax() call to html.

Resources