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

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.

Related

Watch a select quantity selector with Vue 3

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')

Vue js: mapping array from API response data to checkbox list and back

I'm using Vue js to display and edit details of a person. The person being edited has a list of favourite colours that I want to display as a list of checkboxes. When I change the colours selected, and click the 'Update' button, the person object should be updated accordingly, so I can pass back to the api to update.
I've got as far as displaying the Person object's colours correctly against their respective checkboxes. But I'm struggling with passing the changes to the colour selection, back to the Person object. Below is my checkbox list and details of how I've tried to implement this. Is there a better way of doing this?
I've tried using 'b-form-checkbox-group'. Below is my code.
Please note - The list of available colours is dynamic, but I've temporarily hardcoded a list of colours ('colourData') till I get this working.
Also, in the 'UpdatePerson' method, I've commented out my attempts to get the selected colours mapped back to the Person object.
<template>
<form #submit.prevent="updatePerson">
<b-form-group label="Favourite colours:">
<b-form-checkbox-group id="favColours"
v-model="colourSelection"
:options="colourOptions"
value-field="item"
text-field="name">
</b-form-checkbox-group>
</b-form-group>
<div class="container-fluid">
<b-btn type="submit" variant="success">Save Record</b-btn>
</div>
</form>
</template>
<script>
import service from '#/api-services/colours.service'
export default {
name: 'EditPersonData',
data() {
return {
personData: {
personId: '',
firstName: '',
lastName: '',
colours:[]
},
colourData: [
{ colourId: '1', isEnabled: '1', name: 'Red' },
{ colourId: '2', isEnabled: '1', name: 'Green' },
{ colourId: '3', isEnabled: '1', name: 'Blue' },
],
selectedColours: [],
colourSelection: []
};
},
computed: {
colourOptions: function () {
return this.colourData.map(v => {
let options = {};
options.item = v.name;
options.name = v.name;
return options;
})
}
},
created() {
service.getById(this.$route.params.id).then((response) => {
this.personData = response.data;
this.colourSelection = response.data.colours.map(function (v) { return v.name; });
this.selectedColours = response.data.colours;
}).catch((error) => {
console.log(error.response.data);
});
},
methods: {
async updatePerson() {
//const cs = this.colourSelection;
//const cd = this.colourData.filter(function (elem) {
// if (cs.indexOf(elem.name) != -1) { return elem;}
//});
//this.personData.colours = [];
//this.personData.colours = cd;
service.update(this.$route.params.id, this.personData).then(() => {
this.personData = {};
}).catch((error) => {
console.log(error.response.data);
});
},
}
}
</script>
Any help wold be much appreciated.
Thanks
I got this working by making the below changes to the commented part in the 'updatePerson()' method:
methods: {
async updatePerson() {
const cs = this.colourSelection;
const cd = this.colourData.filter(function (elem) {
if (cs.some(item => item === elem.name)) { return elem; }
});
this.personData.colours = [];
this.personData.colours = cd;
service.update(this.$route.params.id, this.personData).then(() => {
this.personData = {};
}).catch((error) => {
console.log(error.response.data);
});
}
}

How to update async await function when a variable change?

genderPie()
let filter = {};
async function genderPie() {
const d = await getData();
const g = await d.reduce((a, o) => (o.GEN && a.push(o.GEN), a), []);
const gender = Object.keys(g).length;
const m = await d.reduce((a, o) => (o.GEN == 1 && a.push(o.GEN), a), []);
const male = Object.keys(m).length;
const f = await d.reduce((a, o) => (o.GEN == 2 && a.push(o.GEN), a), []);
const female = Object.keys(f).length;
var data = [{
name: 'male',
y: male,
id: 1
}, {
name: 'female',
y: female,
id: 2
}];
chart = new Highcharts.Chart({
plotOptions: {
pie: {
innerSize: '80%',
dataLabels: {
connectorWidth: 0
}
}
},
series: [{
"data": data,
type: 'pie',
animation: false,
point: {
events: {
click: function(event) {
filter.GEN = '' + this.id + '';
}
}
}
}],
"chart": {
"renderTo": "gender"
},
});
}
async function getData() {
buildFilter = (filter) => {
let query = {};
for (let keys in filter) {
if (filter[keys].constructor === Array && filter[keys].length > 0) {
query[keys] = filter[keys];
}
}
return query;
}
//FILTER DATA
//Returns the filtered data
filterData = (dataset, query) => {
const filteredData = dataset.filter((item) => {
for (let key in query) {
if (item[key] === undefined || !query[key].includes(item[key])) {
return false;
}
}
return true;
});
return filteredData;
};
//FETCH JSON
const dataset = [{
"GEN": "2"
}, {
"GEN": "1"
}, {
"GEN": "1"
}, {
"GEN": "2"
},
{
"GEN": "2"
}, {
"GEN": "2"
}, {
"GEN": "2"
}, {
"GEN": "1"
}
]
//BUILD THE FILTER
const query = buildFilter(filter);
const result = filterData(dataset, query);
console.log(result)
return result
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="gender"></div>
does anyone can explain me how to handle the following?
I have two functions that filter data and than I build a chart with Hichart
Each time a user click for example a slice of a pie chart an event is fired and an object is populated.
That object allows me to filter the dataset and redraw the chart
The last thing I'm missing is about to update the filtering functions based on the object to be populated
first I'll do this
async function getData() {
buildFilter = (filter) => {
let query = {};
for (let keys in filter) {
if (filter[keys].constructor === Array && filter[keys].length > 0) {
query[keys] = filter[keys];
}
}
return query;
}
then
filterData = (data, query) => {
const filteredData = data.filter( (item) => {
for (let key in query) {
if (item[key] === undefined || !query[key].includes(item[key])) {
return false;
}
}
return true;
});
return filteredData;
};
const query = buildFilter(filter);
const result = filterData(data, query);
my object is
let filter = {}
when a user click the slice myobject become for example
let filter = {
gen: "1"
}
Take a look at this StackBlitz project.
In getData(), I simplified your filter to this one:
return data.filter(item => {
for (const property of Object.keys(filter)) {
if (item[property] !== filter[property]) {
return false;
}
}
return true;
});
and when a slice is clicked, I call genderPie() again, after updating the filter.
You might want to separate the data request from the filtering, so that the data is downloaded only once, not every time a filter is changed.

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

Categories