Watch a select quantity selector with Vue 3 - javascript

I'm trying to make a quick shopping cart with on existing project.
My list items is already is generated by php and I get work with html elements like that :
const billets = document.querySelectorAll(".card-billet");
var products = [];
billets.forEach(billet => {
products.push({
title: billet.querySelector('.card-billet-title').textContent,
price: billet.dataset.price,
qty: billet.querySelector('select[name="billet_quantity"]').value
});
});
const App = {
data() {
return {
items: products
}
},
watch: {
items: function () {
console.log("watched");
},
},
computed: {
total: function () {
console.log(this.items)
let total = 0.00;
this.items.forEach(item => {
total += (item.price * item.qty);
});
return total;
}
}
}
Vue.createApp(App).mount('#checkoutApp')
This works but only on page load but I'm trying to change the total when my select quantity changeno.
I'm a bit lost to achieve this, should I use watch but on what ? Or anything else ?

Finally I found how to achieve this, the problem was that my array was out of the vue instance so can't be updated.
I simplified the code like this :
const App = {
data() {
return {
items: []
}
},
methods: {
onChange: function (e) {
// console.log(this.items)
this.items = [];
document.querySelectorAll(".card-billet").forEach(billet => {
this.items.push({
title: billet.querySelector('.card-billet-title').textContent,
price: billet.dataset.price,
qty: billet.querySelector('.card-billet-qty-selector').value
});
});
}
},
computed: {
total: function () {
// console.log(this.items)
let total = 0.00;
this.items.forEach(item => {
total += (item.price * item.qty);
});
return total;
}
}
}
Vue.createApp(App).mount('#checkoutApp')

Related

I'm getting the empty item instead of a conditionally created item from a map function

In a Reducer of ngRx I'm trying to create a single item from an item matching an if condition, but getting the empty item instead. Please, help!
Here is the Reducer code:
on(rawSignalsActions.changeRangeSchema, (state, { rangeSchemaName }) => ({
...state,
engagementSignal: state.rangeSchemas.map(
item => {
if(item.mapping.rangeSchemaName === rangeSchemaName){
let engagementSignal: EngagementSignal=
{
id:0,
name:'',
metaSignalName:'',
rangeSchemaName:'',
type:2,
mappedGraphSignals:[],
signalInputs:[]
};
engagementSignal.id = item.mapping.id;
engagementSignal.name = item.mapping.rangeSchemaName;
engagementSignal.metaSignalName = item.mapping.metaSignalName;
engagementSignal.rangeSchemaName = item.mapping.rangeSchemaName;
engagementSignal.signalCounts = item.signalCounts;
engagementSignal.type = item.mapping.type;
engagementSignal.mappedGraphSignals = item.abstractSignals.map(
signal => {
let mappedGraphSignal: MappedGraphSignal = {
id:0,
name:'',
totalValues:0,
uniqueUsers:0,
mappedAttitudes:[],
signalRange:[]
};
mappedGraphSignal.id = signal.abstractSignal.id;
mappedGraphSignal.name = signal.abstractSignal.name;
mappedGraphSignal.totalValues = 1234; //dummy values for now
mappedGraphSignal.uniqueUsers = 1234;
mappedGraphSignal.mappedAttitudes = signal.signalAttitudes;
if (signal.numericMappings) {
mappedGraphSignal.signalRange = signal.numericMappings;
} else {
mappedGraphSignal.signalRange = signal.textMappings;
}
return mappedGraphSignal;
}
);
//dummy values for now
engagementSignal.signalInputs = [
{
value: '0',
count: 2376
},
{
value: 'no',
count: 3423
},
{
value: '1',
count: 1264
},
{
value: 'yes',
count: 5423
}
];
return engagementSignal;
}
}
)[0],
linkedRangeSchema: something
})),
I want to get a single item object instead of an array, discarding the rest of array.
When I debug the App, after passing the map function, I got engagementSignal value as:
By applying the filter on array, followed by map function solved the problem!
Here is the working code snippet:
on(rawSignalsActions.changeRangeSchema, (state, { rangeSchemaName }) => ({
...state,
engagementSignal: state.rangeSchemas.filter(item =>item.mapping.rangeSchemaName === rangeSchemaName).map(
item => {
let engagementSignal: EngagementSignal=
{
id:0,
name:'',
metaSignalName:'',
rangeSchemaName:'',
type:2,
mappedGraphSignals:[],
signalInputs:[]
};
engagementSignal.id = item.mapping.id;
engagementSignal.name = item.mapping.rangeSchemaName;
engagementSignal.metaSignalName = item.mapping.metaSignalName;
engagementSignal.rangeSchemaName = item.mapping.rangeSchemaName;
engagementSignal.signalCounts = item.signalCounts;
engagementSignal.type = item.mapping.type;
engagementSignal.mappedGraphSignals = item.abstractSignals.map(
signal => {
let mappedGraphSignal: MappedGraphSignal = {
id:0,
name:'',
totalValues:0,
uniqueUsers:0,
mappedAttitudes:[],
signalRange:[]
};
mappedGraphSignal.id = signal.abstractSignal.id;
mappedGraphSignal.name = signal.abstractSignal.name;
mappedGraphSignal.totalValues = 1234; //dummy values for now
mappedGraphSignal.uniqueUsers = 1234;
mappedGraphSignal.mappedAttitudes = signal.signalAttitudes;
if (signal.numericMappings) {
mappedGraphSignal.signalRange = signal.numericMappings;
} else {
mappedGraphSignal.signalRange = signal.textMappings;
}
return mappedGraphSignal;
}
);
engagementSignal.signalInputs = [
{
value: '0',
count: 2376
},
{
value: 'no',
count: 3423
},
{
value: '1',
count: 1264
},
{
value: 'yes',
count: 5423
}
];
return engagementSignal;
}
)[0],
linkedRangeSchema: something
})),

Why index-of not working correctly in vuejs?

I make a custom component in Vue.js .In My component, I have a list which has a delete button.On click of a button, it deletes the row.If I click any row it deletes the last row because the index is always -1 why?
here is my code
https://plnkr.co/edit/hVQKk3Wl9DF3aNx0hs88?p=preview
methods: {
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
this.items.splice(index, 1);
}
}
below Whole code
var MyComponent = Vue.extend({
template:'#todo-template',
props:['items'],
computed: {
upperCase: function () {
return this.items.map(function (item) {
return {name: item.name.toUpperCase(),complete:item.complete};
})
}
},
methods: {
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
this.items.splice(index, 1);
}
}
})
Vue.component('my-component', MyComponent)
var app = new Vue({
el: '#App',
data: {
message: '',
items: [{
name: "test1",
complete:true
}, {
name: "test2",
complete:true
}, {
name: "test3",
complete:true
}]
},
methods: {
addTodo: function () {
this.items.push({
name:this.message,
complete:true
});
this.message ='';
},
},
computed: {
totalCount:function () {
return this.items.length;
}
}
});
Instead of passing the whole object you should pass the index of the item.
Change the for loop to
<li v-for="(item, index) in upperCase" v-bind:class="{'completed': item.complete}">
{{item.name}}
<button #click="deleteTodo(index)">X</button>
<button #click="deleteTodo(index)">Toggle</button>
</li>
and the delete function to
deleteTodo:function (itemIndex) {
this.items.splice(itemIndex, 1);
}
Updated Code: Link
Your code is assuming that indexOf will return a valid index
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
this.items.splice(index, 1);
}
If it's returning -1, it means that it's not finding the item in the list. Quite likely this.items is not what you think it is.
A bit of defensive code will help you solve this:
deleteTodo:function (item) {
console.log(item)
var index = this.items.indexOf(item);
if (index === -1)
console.error("Could not find item "+item in list: ",this.items);
else
this.items.splice(index, 1);
}
This will show you what this.items is in your console output

How can i append table data value in ng-repeat using angularjs

I have two table. dynamicaly data will come for table one after I click the dynamic table data button. after I click the done button I want to
append the data in $scope.notiData.
now after I click done button $scope.notiData data is gone. every time data is coming from tableTwo what is there in presently. so how can i use concat Iin $scope.notiData.please help.
http://jsfiddle.net/A6bt3/127/
Js
var app = angular.module('myApp', []);
function checkBoxCtrl($scope) {
$scope.notiData = [];
$scope.tableOne = [{
firstname: 'robert',
value: 'a'
}, {
firstname: 'raman',
value: 'b'
}, {
firstname: 'kavi',
value: 'c'
}, {
firstname: 'rorank',
value: 'd'
}
];
$scope.tableOne1 = [{
firstname: 'robvzxcvert',
value: 'a'
}, {
firstname: 'ramsdgan',
value: 'b'
}, {
firstname: 'kasdgsdgvi',
value: 'c'
}, {
firstname: 'rordggank',
value: 'd'
}
];
$scope.tableTwo = [];//the table to be submitted
function removeitems(tableRef) { //revmove items from tableRef
var i;
for (i = tableRef.length - 1; i >= 0; i -= 1) {
if (tableRef[i].checked) {
tableRef.splice(i, 1);
}
}
}
$scope.btnRight = function () {
//Loop through tableone
$scope.tableOne.forEach(function (item, i) {
// if item is checked add to tabletwo
if (item.checked) {
$scope.tableTwo.push(item);
}
})
removeitems($scope.tableOne);
}
$scope.btnAllRight = function () {
$scope.tableOne.forEach(function (item, i) {
item.checked = true;
$scope.tableTwo.push(item);
})
removeitems($scope.tableOne);
}
$scope.btnLeft = function () {
$scope.tableTwo.forEach(function (item, i) {
if (item.checked) {
$scope.tableOne.push(item);
}
})
removeitems($scope.tableTwo);
}
$scope.btnAllLeft = function () {
$scope.tableTwo.forEach(function (item, i) {
item.checked = true;
$scope.tableOne.push(item);
})
removeitems($scope.tableTwo);
}
$scope.done = function () {
angular.extend($scope.notiData, $scope.tableTwo);
$scope.tableTwo = [];
}
$scope.removeRow = function (item) {
var index = $scope.notiData.indexOf(item);
$scope.notiData.splice(index, 1);
}
$scope.dynamicTable= function () {
$scope.tableOne1.forEach(function (item, i) {
item.checked = true;
$scope.tableOne.push(item);
})
}
};
I want to append the data in $scope.notiData. now after I click done button $scope.notiData data is gone
The angular.extend replaces existing entries. To append, use array.concat:
$scope.done = function () {
//angular.extend($scope.notiData, $scope.tableTwo);
$scope.notiData = $scope.notiData.concat($scope.tableTwo);
$scope.tableTwo = [];
}
Also to avoid duplicate in ng-repeat use track by errors, use angular.copy to push items:
$scope.dynamicTable= function () {
$scope.tableOne1.forEach(function (item, i) {
item.checked = true;
//$scope.tableOne.push(item);
$scope.tableOne.push(angular.copy(item));
});
};
The ng-repeat directive tracks array objects by reference. Pushing duplicate objects will result in tracking errors. The angular.copy will create a new unique object reference for the item and will avoid the tracking errors.
The DEMO on JSFiddle.

How do I display the sum of multiple properties in Ractive

I am using RactiveJS for my templates. I have some nested data like this:
var view = new Ractive({
data: {
items: [
{ size: 1 }
{ size: 3 }
{ size: 4 }
]
}
});
How can I display the sum of item sizes in my template? This depends on the size of each individual item but also on the items array (e.g. items are added/removed).
Here is a fiddle which achieves what you want by using Ractive's computed properties. Would you consider this denormalization of data?
computed: {
sum: function () {
var items = this.get( 'items' );
return items.reduce(function(prev, current) {
return prev + current.size;
}, 0)
}
You can track the sum using an observer. This has the advantage of not having to reiterate the entire array each time a value changes. (see http://jsfiddle.net/tL8ofLtj/):
oninit: function () {
this.observe('items.*.size', function (newValue, oldValue) {
this.add('sum', (newValue||0) - (oldValue||0) );
});
}
I found one solution, but it's not optimal because it denormalizes data in the view.
var view = new Ractive({
data: {
items: [
{ size: 1 }
{ size: 3 }
{ size: 4 }
],
sum: 0
},
oninit: function () {
this.observe('items items.*.size', function () {
this.set('sum', _.reduce(this.get('items'), function (memo, item) {
return memo + item.size;
}, 0));
});
}
});
And then in the template I can just use {{sum}}

Filter in knockout displaying empty Select List

I am doing some filtering with Knockout. I have written this code Please have a look.
$(function() {
var viewmodel = (function () {
var filter = ko.observable("");
var productsList = ko.observableArray([
{
ProductName: "Sunsilk",
ProductCategory:"Shampo"
},
{
ProductName: "Badminton",
ProductCategory: "Sports"
},
{
ProductName: "Chicken",
ProductCategory: "Meat"
},
{
ProductName: "Head and Shoulder",
ProductCategory: "Shampo"
},
{
ProductName: "Book",
ProductCategory: "Education"
},
{
ProductName: "Pen",
ProductCategory: "Education"
}
]);
return {
productsList: productsList,
filter: filter,
};
}());
viewmodel.filteredItems = ko.computed(function () {
var filter = this.filter().toLowerCase();
if (!filter) {
return this.productsList();
} else {
return ko.utils.arrayFilter(this.productsList, function (item) {
return ko.utils.stringStartsWith(this.item.ProductCategory.toLowerCase(), filter);
});
}
}, viewmodel);
ko.applyBindings(viewmodel);
});
and below is the HTML
<h4> << Decision based on filter >> </h4>
<p><span>Filter: </span><input data-bind="value:filter" type="text" name="filterbox"/> <button name="filter">Filter</button></p>
<select data-bind="options:filteredItems,optionsText:'ProductName'" multiple="multiple" size="3"></select>
The filter doesn't work . I am stuck can anyone help me out please. If the given filter value is null or empty all of the products are returned which is okay. But when I write the specific category for the products none of the products are returned.
Fiddle here
There is small mistake in code that under computed "if" condition always returning true whenever you enter any value and i have also changed the filtering logic.
viewmodel.filteredItems = ko.computed(function () {
var filter = viewmodel.filter().toLowerCase();
if (!filter && filter === "") {
return viewmodel.productsList();
} else {
return ko.utils.arrayFilter(viewmodel.productsList(), function (item) {
if (item.ProductCategory) {
return item.ProductCategory.toLowerCase().indexOf(filter) !== -1;//will return true if ProductCategory contains the filter string
}
});
}
}, viewmodel);
Fiddle Demo
try this code
viewmodel.filteredItems = ko.computed(function () {
var filter = viewmodel.filter().toLowerCase();
if (!filter) {
return viewmodel.productsList();
} else {
return ko.utils.arrayFilter(viewmodel.productsList, function (i,item) {
if(item.ProductCategory)
{
return ko.utils.stringStartsWith(item.ProductCategory.toString().toLowerCase(), filter);
}
});
}
}, viewmodel);

Categories