ajax call in beforeCreate does not update data - ajax

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);
},

Related

(Vue + Laravel) v-if / v-else inside <td> throws no matching end tag error

I apologize in advance if my question is silly-- Vue newbie here but very eager to learn!
In order to create an interface to manage user privileges in a web app, I've made a component in which I want to create a table with nested v-fors.
For each row, I want 5 cells (): the first one includes text depending on the current iteration of object permisos and the other 4 should be created from the object tipos_permisos (which is an object with 'fixed' values).
The problem:
When I try to compile, I get multiple errors claiming that some tags have no matching end tag. I assume it is due to the v-for nested inside another v-for and/or the v-if inside the innermost v-for... or something like this. The claim is that has no matching tag or that the element uses v-else without corresponding v-if :/
I've tried writing out the last 4 cells manually (without using the tipos_permisos object) but even then, I get errors. In this case, claiming that , and have no matching end tag.
The desired result:
Please note that for some of the resources listed, some of the privileges might not apply (i.e., the log viewer is read-only always so it doesn't have the C (create), U (update) or D (delete) privileges, hence the conditional to show either a checkbox or an icon)
My component:
<template>
<form :action="usr.url.savepermisos" method="post" class="">
<div class="card">
<div class="card-header bg-gd-primary">
<h3 class="card-title">Privilegios de {{ usr.nombre }}</h3>
</div>
<div class="card-content">
<p class="mb-3">
El usuario
<span class="font-w700">{{ usr.nombre }}</span>
tiene los siguientes privilegios:
</p>
<table class="table">
<thead>
<tr>
<th>Recurso</th>
<th>Alta / Creación</th>
<th>Visualización</th>
<th>Edición</th>
<th>Eliminación</th>
</tr>
</thead>
<tbody>
<tr v-for="(recurso, idxrecurso) in permisos">
<td :data-order="recurso.id">{{ recurso.etiqueta }}</td>
<td class="align-middle" v-for="(color,tp) in tipos_permisos">
<label :class="'checkbox checkbox-' + color + ' mycheckbox'" v-if="(typeof recurso[tp] !== "undefined")">
<input type="checkbox" class="checkbox-input check_permiso" />
<span class="checkbox-indicator"></span>
</label>
<i class="fas fa-minus-square fa-lg text-muted" data-toggle="tooltip" title="N/A" v-else></i>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</template>
<script>
export default {
data() {
return {
tipos_permisos: {
'C': 'success',
'R': 'info',
'U': 'warning',
'D': 'danger'
}
}
},
props: [
'usr',
'permisos',
'perm_log'
]
}
</script>
If something is unclear please let me know so that I can provide further info.
There is a missing "=" after v-for:
<td class="align-middle" v-for"(color,tp) in tipos_permisos">
I didn't understand this part:
v-if="(typeof recurso[tp] !== "undefined")"
If undefined in your code is a string, your condition should be
v-if="recurso[tp] !== 'undefined'"
If it's a real undefined, it should be like this:
v-if="recurso[tp] !== undefiened"

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

Knockout js Update viewmodel after ajax post

I am using knockout js and the knockout mapping plugin.
My problem is after calling the ajax post my view (ui) is not updating.
Only if I reload the page the data will be updated.
<tbody data-bind="foreach: WorkData">
<td data-bind="text: id"></td>
<td data-bind="text: user_name"></td>
<button class="btn btn-xs btn-success" data-bind="click: $parent.postTmpData" role="button">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</button>
</tbody>
<script>
function ViewModel() {
var self = this;
var data = <?php echo json_encode($this->data); ?> ;
self.WorkData = ko.mapping.fromJS(data);
self.postTmpData = function(entry) {
$.post("<?php echo Config::get('URL'); ?>/work/confirmWorkPost/", entry, function(returnedData) {
ko.mapping.fromJS(returnedData, self);
})
}
}
ko.applyBindings(new ViewModel());
</script>
Is returnedData what you want to replace WorkData with? It's only possible if they are in the same structure. In that case, try this:
$.post("<?php echo Config::get('URL'); ?>/work/confirmWorkPost/", entry, function(returnedData) {
ko.mapping.fromJS({'WorkData': returnedData}, self);
})
This was the solution for me:
ko.mapping.fromJS(JSON.parse(returnedData), self.WorkData);

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.

How to retrieve multiple records from Jquery to my RazorView page

I have a button "btnGetAddress" on my razor page .On clik of this button,I am calling a Jquery to get my addressItmes object to be displayed on to my View page.
On clicking "btnGetAddress" I am able to hit my "JsonResult GetAddresses()" and retrieve records within my Jquery (success: function (data)).and this data has multiple address records. But I do not know how to take this data to my view .Please help me to get my data to be displayed on to my View
When my page get loaded,the user will see only the "btnGetAddress" button .When the user click on the btnGetAddress, it will call the Jquery Click function to fetch all address records from database and display each set of records on the page
$("#btnGetAddress").click(function () {
debugger;
var selected = $("#ddlType").val();
if (selected == "")
{ selected = 0; }
var dataToSend = {
SelectedTypeId: selected
};
$.ajax({
type: "GET",
url: '#Url.Action("GetAddresses", "Content")',
data: { SelectedTypeId: selected },
success: function (data) {
debugger;
},
error: function (error) {
var verr = error;
alert(verr);
}
});
pasted below is my JsonResult GetAddresses() which gets called to retrieve addressItems
public JsonResult GetAddresses()
{
model.AddressItems = AddressService.RetrieveAllAddress();
// My AddressItems is of type IEnumerable<AddressItems>
return Json(model.AddressItems, JsonRequestBehavior.AllowGet);
}
Here is my razor View Page where the address records are to be displayed.
........................
<input type="submit" id="btnGetAddress" name="btnSubmit" value="Show Addresses" />
if (!UtilityHelper.IsNullOrEmpty(Model.AddressItems))
{
foreach (var AddressRecord in Model.AddressItems)
{
<fieldset >
<legend style="padding-top: 10px; font-size: small;">Address Queue(#Model.NumRecords)
</legend>
<table>
<tr>
<td>
<span>Index</span>
</td>
<td>
</td>
<td>
<input type="submit" id="btnDelete" name="btnSubmit" value="X" />
<br />
</td>
</tr>
<tr>
<td>
<span>Address1</span>
<br />
</td>
<td>
#Html.EditorFor(model => AddressRecord.Address )
#Html.ValidationMessageFor(model => AddressRecord.Address)
</td>
</tr>
<tr>
<td>
<span>Description</span>
<br />
</td>
<td>
#Html.EditorFor(model => AddressRecord.Description)
#Html.ValidationMessageFor(model => AddressRecord.Description)
</td>
</tr>
<tr>
<td>
<input type="submit" id="btnSave" name="btnSubmit" value="Save" />
</td>
<td>
<input type="submit" id="btnDelete" name="btnSubmit" value="Delete" />
</td>
</tr>
</table>
</fieldset>
}
}
<fieldset>
Or is there any better way to achieve my objective?
Since you are getting the data via ajax you should use a jquery template engine. Basically get the data the way you are and on success you do something like
<script language="javascript" type="text/javascript">
$(function () {
$.getJSON("/getprojects", "", function (data) {
$("#projectsTemplate").tmpl(data).appendTo("#projectsList");
});
});
</script>
<script id="projectsTemplate" type="text/html">
<section>
<header><h2>Projects</h2></header>
<table id="projects">
<th>Name</th>
{{tmpl(items) "#projectRowTemplate"}}
</table>
</section>
</script>
<script id="projectRowTemplate" type="x-jquery-tmpl">
<tr>
<td>${name}</td>
</tr>
</script>
<div id="projectsList"></div>
Now each template engine is different but the above gives you an idea of what you can do
If you want to return JSON object in your controller, you are going have to turn your view into a string and return it as part of the message. If you google there are some methods out there that can do this.
However, I really think that's the hard way, why not take the data you get from the JSON in the controller and put it in a MODEL and then return your VIEW with the model data passed in. I think that's the easier way.

Resources