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
Related
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.
I am having a little trouble with this one.
Using the approach discussed in this -- https://jasonwatmore.com/post/2019/06/18/angular-8-simple-pagination-example
Use-case difference is, my data all comes from database. So I have had to use a model class and all. But, due to some problem, I am getting the same data [1st row from db] in all the rows. Let me post the code I used --
ANGULAR
method inside my component to get data from service class
newsTopics: NewsTopic[];
pageOfItems: Array<NewsTopic>;
getAllNewsTopics() {
this.userService.getAllTopics()
.subscribe((data: NewsTopic[]) => {
this.newsTopics = (data as NewsTopic[]).map((x, i) => ({ Id: (i + 1), TopicId: data[0].TopicId, TopicName: data[0].TopicName, TopicInfo: data[0].TopicInfo, IssueStartDate: data[0].IssueStartDate, IssueCloseDate: data[0].IssueCloseDate, NewsCategory_Id: 0 }));
})
}
my model class
export class NewsTopic
{
Id: number;
TopicId: string;
TopicName: string;
TopicInfo: string;
IssueStartDate: Date;
IssueCloseDate: Date;
NewsCategory_Id: number;
}
the method in service class itself
getAllTopics(){
var reqHeader = new HttpHeaders({ 'No-Auth': 'True' });
return this.http.get<NewsTopic[]>(this.rootUrl + '/api/NewsTopic/GetAllNewsTopics', { headers: reqHeader });
}
my html for showing the data and the pagination control
<div class="card-body">
<table class="table table-striped" id="newstopics_tbl">
<thead>
<tr style="background:linear-gradient(50deg, #d9fcfc, #dafbde)">
<th>Topic ID</th>
<th>Topic Name</th>
<th>Topic Info</th>
<th>Start Date</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let nt of pageOfItems">
<td><b>{{nt.TopicId}}</b></td>
<td>{{nt.TopicName}}</td>
<td>{{nt.TopicInfo}}</td>
<td>{{nt.IssueStartDate}}</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer pb-0 pt-3" style="background:linear-gradient(50deg, #d9fcfc, #dafbde); margin-top:15px">
<jw-pagination [items]="newsTopics" (changePage)="onChangePage($event)"></jw-pagination>
</div>
My guess is, the problem lies in the getAllNewsTopics() method, where I am trying to fill and map the array of objects and assign to the variable. Something wrong there. If you see the original example from the link, they used a .fill(0) also. In my case I tried doing .fill(data[0]) however result is the same, ie. only first record, repeated multiple times.
What is the trick I am missing? Can someone just point me out.
Much needed, Thanks,
EDITS --
These are the changes->
getAllNewsTopics() {
var reqHeader = new HttpHeaders({ 'No-Auth': 'True' });
return this.http.get<NewsTopic[]>(this.rootUrl + '/api/NewsTopic/GetAllNewsTopics', { headers: reqHeader })
.subscribe((data: NewsTopic[]) => {
this.newsTopics = data;
});
}
and
<div class="card-body">
<table class="table table-striped" id="newstopics_tbl">
<thead>
<tr style="background:linear-gradient(50deg, #d9fcfc, #dafbde)">
<th>Topic ID</th>
<th>Topic Name</th>
<th>Topic Info</th>
<th>Start Date</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let nt of pageOfItems" style="cursor:pointer" (click)="NewsArticleAdd(nt)">
<td (click)=""><b>{{nt.TopicId}}</b></td>
<td style="color:mediumvioletred;" (click)="">{{nt.TopicName}}</td>
<td (click)="">{{nt.TopicInfo}}</td>
<td (click)="">{{nt.IssueStartDate}}</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer pb-0 pt-3" style="background:linear-gradient(50deg, #d9fcfc, #dafbde); margin-top:15px">
<jw-pagination [items]="newsTopics" (changePage)="onChangePage($event)"></jw-pagination>
</div>
That does the trick. Works as expected,
I'm trying to get Vue to update value through an api call. I log the searches two times: ones outside the beforeCreate and once inside. Outside it gives the initial value of 'searches', inside the correct, new value.
The main problem is that I don't see the updated values.
<div id="app">
<!-- shows when there are no searches -->
<p class="text-center" v-if="searches === null">Er werden nog geen zoekopdrachten uitgevoerd.</p>
<!-- this div gets repeated for every search -->
<div class="search border border-info rounded p-3 m-3 row" v-for="search in searches">
<table class="col-6">
<tr>
<td class="text-info">Zoekwoorden</td>
<td v-for="keyword in search.keywords">#{{ keyword }}</td>
</tr>
<tr class="even">
<td class="text-info">Platforms</td>
<td v-for="platform in search.platforms">#{{ platform }}</td>
</tr>
<tr>
<td class="text-info">Gerelateerde zoekwoorden</td>
<td v-for="keyword in search.all_keywords">
#{{ keyword }}
</td>
</tr>
<tr class="even">
<td class="text-info">Locatie</td>
<td>Voskenslaan, Gent</td>
</tr>
<tr>
<td class="text-info">Datum</td>
<td>#{{ search.created_at }}</td>
</tr>
</table>
<div class="col-6 text-right">
<button type="button" class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h3 class="search-id text-secondary">##{{ search.id }}</h3>
<a :href="'/searches/' + search.id " role="button" class="btn btn-info details-button">Details...</a>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var $root = new Vue({
el: '#app',
data:
{
searches: [
{ id: 123, keywords: ['sample', 'sample'], platforms: ['sample', 'sample'] },
{ id: 123, keywords: ['sample', 'sample'], platforms: ['sample', 'sample'] }
]
},
beforeCreate: function ()
{
var vm = this;
$.get("api/searches", function(data, status){
vm.$set(vm,'searches', data); //zet de waarde van searches gelijk aan de opgehaalde
console.log(vm.searches); //geeft juiste opgehaalde searches
});
}
});
console.log($root.searches); //geeft de initiële twee sample searches
</script>
I think you may want to try beforeMount instead of beforeCreate.
beforeCreate fires before anything in the component is initialied, according to the docs:
Called synchronously immediately after the instance has been initialized, before data observation and event/watcher setup.
I haven't tested it, but I would be willing to bet this is your issue. Since there are no watchers or data structures initialized, your call to vm.$set(vm,'searches', data) is being overwritten by the component data structure
Whereas beforeMount is called after data and events/watchers have been initialized:
Called right before the mounting begins: the render function is about to be called for the first time.
I would probably also just push to the existing array instead of replacing it as such as this (especially if you have populated the search array as in your example):
beforeMount: function () {
var vm = this;
$.get("api/searches", function(data, status){
vm.searches.push(...data); // assuming data is an array
console.log(vm.searches);
});
},
mounted: function(){
var vm = this;
console.log(vm.searches);
},
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.
I am trying to use a partial view to represent rows of a table in my project. I currently have
<table>
<thead>
<tr>
<th >
Column 1
</th>
<th >
Column 2
</th>
<th >
Column 3
</th>
</tr>
</thead>
<tbody>
#foreach(var item in Model.Items)
{
#Html.Action("ItemCalculatedView", new { Id = item.Id})
}
</tbody>
</table>
In my partial view I have this
#using (Ajax.BeginForm("SaveStuff", "Whatever",
new { id = #Model.Id }, new AjaxOptions()
{
HttpMethod = "Post",
OnSuccess = "Success"
}))
{
#Html.HiddenFor(m => m.Id)
<tr>
<td>
#Html.Label("Col1", Model.Col1)
</td>
<td>
#Html.TextBox("Number", Model.Number)
</td>
<td>
<input type="submit" id='submit-#Model.Id'/>
</td>
</tr>
}
How can I make this work?
You can put a form inside a table cell, but you can't have the form inside a tbody element, or spanning multiple columns. So there are three options:
Use a CSS layout instead of a table, or use divs with CSS display set to "table". (for example)
Put the entire form (TextBox and Submit) inside a td
Put another table inside the td element
I'd recommend #1 -- use a CSS layout to construct the table, since it's difficult to style table tags:
Main
<div class="table">
<div class="header-row">
<div class="header-cell">Column 1</th>
<div class="header-cell">Column 2</th>
<div class="header-cell">Column 3</th>
</div>
#foreach(var item in Model.Items)
{
#Html.Action("ItemCalculatedView", new { Id = item.Id})
}
</div>
Partial
#using (Ajax.BeginForm(
actionName: "SaveStuff",
controllerName: "Whatever",
routeValues: new { id = #Model.Id },
ajaxOptions: new AjaxOptions
{
HttpMethod = "Post",
OnSuccess = "Success"
},
htmlAttributes: new { #class = "row" }
))
{
<div class="cell">
#Html.HiddenFor(m => m.Id)
</div>
<div class="cell">
#Html.Label("Col1", Model.Col1)
</div>
<div class="cell">
#Html.TextBox("Number", Model.Number)
</div>
<div class="cell">
<input type="submit" id='submit-#Model.Id'/>
</div>
}
CSS
.table { display: table; }
.header-row, row { display: table-row; }
.header-cell, cell { display: table-cell; }
You have several issues here. First, as dbaseman mentions, you can't place forms within the structure of a table and have it be legal HTML. It may work, or it might not, and even if it does work, you can't guarantee it will continue to work.
I would instead wrap your table in the form, and then on the post figure out which button was pressed based on its value and/or index.
I would strongly advise against using css tables for tabular data. It's just not semantically correct.
Another possible solution is, instead of using the Ajax.BeginForm, instead use jQuery $.ajax and then you can select a row of data in javascript to post to the server.