I have a paginated record set from a http response and want to further implement client side pagination on return paginated record set, thus I have the following component markup
<td v-for="item in items">....</td> // only print 5 at a time
and in the default....
{
data: { return {
itemsData:[] // populated from RESTful data in increments of 20
, offset: 0 // for internal pagination
} },
computed: {
items: function(){
return this.itemsData.slice(this.offset, 5); // re-populate over time as offset changes
}
},
methods: {
getItems: function() {
this.$http.get('/api/items/?page=' + this.page).then(response=>
{
this.itemsData = response.data.data; // json array and I al get back meta data
// for which i use in a mixin to calculate offset and page etc.
// for both client side and server side pagination
}) // fetches records 20 at a time
}
}
.........
If itesmData is populated and then offset is dynamically changed. Shouldn't the component's template re-rendered with a new items collection?
Or should I be using a method instead? e.g.
<td v-for="item in paginated(itemData)">....</td>
{
....
methods: {
paginated: function(items){
var arr=[];
for( var i = this.offset; i < this.offset + 5; i++)
arr.push(item[i]);
return arr;
}
}
How would the template be updated with the new array? Would I need to implement a watcher? on the computed data? or would the offset do?
UPDATE:
I tried to implement the pagination via competed and while I get the template to render the first 5..... in trying to re-render after updating offset does not fire.... arr seems to return empty even thought i am on the second page and offset is yet to reach itemsData.length
Can you iterate through a data array property OUTSIDE of the template? i.e. loop through this.itemsData[i] or this.$data.itemsData[i]???
You need to make following changes in your code:
computed: {
items: function(){
return this.itemsData.slice(this.offset, this.offset + 5); // re-populate over time as offset changes
}
}
As you can see from documentation, slice takes two argument start and end, it will return a portion of an array into a new array object selected from start to end (end not included).
Related
I have a map with features on it that are also listed in a v-data-table. When the user clicks a row, the feature is highlighted on the map. When the user clicks a map feature, the corresponding grid row is selected. So I am programmatically setting the selected row like this:
selectRow(id) {
this.selected = [this.getRowFromId(id)]
},
getRowFromId(id) {
for (let site of this.sites) {
if (site.id === id) return site;
}
return []
},
Works fine with one UX problem: The table is not scrolled to the selected row.
I am using a vertically scrolling data-table with all rows in the grid rather than pagination.
Any ideas on how to programmatically scroll the data table?
Here is my solution. I found most of what I was looking for in this post: Plain JavaScript - ScrollIntoView inside Div
I had to do a couple of v-data-grid specific things to make it work. I am using a watcher in my context but you could to this anywhere you needed to:
watch: {
selectedId: function(newVal) { // watch it
// Select the row
this.selectRow(newVal)
// Scroll the item into view
// First we need to let the component finish selecting the row in the background
setTimeout( () => {
// We get the selected row (we assume only one or the first row)
const row = document.getElementsByClassName("v-data-table__selected")[0]
// Then we get the parent. We need to give the -v-data-table a ref
// and we actually take the child of the table element which
// has the scrollbar in my case.
const parent = this.$refs.detailGrid.$el.firstChild
// Finally call the scroll function
this.scrollParentToChild(parent, row )
}, 100)
}
},
methods: {
scrollParentToChild(parent, child) {
// Where is the parent on page
var parentRect = parent.getBoundingClientRect();
// What can you see?
var parentViewableArea = {
height: parent.clientHeight,
width: parent.clientWidth
};
// Where is the child
var childRect = child.getBoundingClientRect();
// Is the child viewable?
var isViewable = (childRect.top >= parentRect.top) && (childRect.top <= parentRect.top + parentViewableArea.height);
// if you can't see the child try to scroll parent
if (!isViewable) {
// scroll by offset relative to parent
parent.scrollTop = (childRect.top + parent.scrollTop) - parentRect.top - childRect.height
}
},
}
I have a computed property function called: Total, this essentially calculates the name + value pairs of an array called prices, it's for a quotation form whereby a running total is added up as a user progresses through a multi-step form, values are then pushed into an array to keep things clean.
This computed property allows me to echo the total on the page which is dynamically updated without any additional code, I can simply add total using handlebars to wherever I want on my page like so: {{ total }}
The problem I'm now facing is that I also want the value of 'total' to be included in a separate array, or at least added to an array and I can't seem to get it right.
The working code which gets the values of my prices array which is by default empty is as follows:
computed: {
total: function(){
var total = [];
Object.entries(this.prices).forEach(([name, value]) => {
total.push(value)
});
return total.reduce(function(total, num){
return total + num
}, 0);
}
}
I want to do add something like this to my computed property:
this.quote.totalPrice = total
I've tried:
computed: {
total: function(){
var total = [];
Object.entries(this.prices).forEach(([name, value]) => {
total.push(value)
});
return total.reduce(function(total, num){
return total + num
this.quote.totalPrice = total
}, 0);
}
}
I'm not getting anything with this?
You can use a watcher to keep track of changes to your computed property and update any variables accordingly:
watch: {
total(newTotal) {
this.quote.totalPrice = newTotal
}
}
data: {
addItemArray: [],
}
I have initialize one array inside data and then inside methods i am added some code:
var self = this;
{
self.cacheDom.$submitItem.click(function () {
self.addItemArray.push({
‘id’: $(’#accountSearch’).data(‘id’).trim(),
‘name’: $(’#accountSearch’).val().trim(),
‘type’: $(’#accountDropdown option:selected’ ).text().trim()
});
$(’.addItemMenu’).hide();
});
}
i pushing the data into addItemArray. Then another one click function inside methods that time i need the addItemArray items. But i getting always a empty array.
Thanks
I had an API call to the backend and based on the returned data, I set the reactive data dynamically:
let data = {
quantity: [],
tickets: []
}
api.default.fetch()
.then(function (tickets) {
data.tickets = tickets
tickets.forEach(ticket => {
data.quantity[ticket.id] = 0
})
})
Based on this flow, how can I set watcher for all reactive elements in quantity array dynamically as well?
You can create a computed property, where you can stringify the quantity array, and then set a watcher on this computed property. Code will look something like following:
computed: {
quantityString: function () {
return JSON.stringify(this.quantity)
}
}
watch: {
// whenever question changes, this function will run
quantityString: function (newQuantity) {
var newQuantity = JSON.parse(newQuantity)
//Your relevant code
}
}
Using the [] operator to change a value in an array won't let vue detect the change, use splice instead.
My function that's supposed to iteratively insert JSON into each element of a particular level of nested JSON using parameters from itself is duplicating the first object returned and inserting it into each nested parameter. See screenshots and code below to get a feel for what I'm doing.
Basically I have a nested JSON object within the state, and need to iteratively append to a nested property of each 'row' of the existing object using an action that returns JSON from an API then a mutation that updates the state.
The problem lies within the mutation I think. I've deduced my function is duplicating the first JSON object returned from the API. See Action and Mutation functions below, along with API function and structure of JSON.
http://api.capecodcommission.org/docs/index.html#sumofannualcapitaltotalsfortreatment
Function within a particular component run using v-for:
methods: {
costTotal () {
return this.updateFinTotals(this.treatment.treatmentId,this.costType.treatTotal,this.costType.financeOption,this.treatment.relativeStartYear,this.costType.finDur,this.costType.prinFor)
}
}
Running function, viewing JSON:
<td class="text-center">
{{ costTotal }}
{{ costType.annualized.sumOfAnnualCapitalTotals }}
</td>
V-for Loop:
<tbody>
<tr v-for="(index, costType) in treatment.costTypes | filterBy 'true' in 'financeable'" is="cost-type-table-row-finance" :cost-type="costType"></tr>
</tbody>
API function:
getSumOfAnnualCapitalTotals (treatmentId, costToFinance, financeOption, relativeStartYear, financeDuration, principalForgivenessRate) {
let data = {
treatmentId: treatmentId,
costToFinance: costToFinance,
financeOption: financeOption,
relativeStartYear: relativeStartYear,
financeDuration: financeDuration,
principalForgivenessRate: principalForgivenessRate
}
return Vue.http.post(API_ROOT + 'sumOfAnnualCapitalTotalsForTreatment', data)
}
Action: Pulls JSON from API, dispatches mutation function.
export const updateFinTotals = function ({ dispatch, state }, treatmentId, costToFinance, financeOption, relativeStartYear, financeDuration, principalForgivenessRate) {
api.getSumOfAnnualCapitalTotals( treatmentId, costToFinance, financeOption, relativeStartYear, financeDuration, principalForgivenessRate ).then(function (response) {
dispatch('UPDATE_ANNUALIZED', response.data)
}, function (response) {
console.log(response)
})
}
Mutation: Updates state with JSON.
UPDATE_ANNUALIZED (state, annualized) {
for (var i = 0; i < state.scenario.treatments.length; i++) {
for (var j = 0; j < state.scenario.treatments[i].costTypes.length; j++) {
state.scenario.treatments[i].costTypes[j]["annualized"] = annualized
}
}
}
EDIT: PICS BELOW
Component: http://i.imgur.com/lcS5Fgo.jpg
JSON Structure: http://i.imgur.com/AsANZOp.jpg
Well, your API returns one value (for one cost-type of one treatment)
Example from linked API docs:
{"sumOfAnnualCapitalTotals":150374.9849625}
but you assign it to all cost-types of all treatments with the for loop in the mutation:
for (var i = 0; i < state.scenario.treatments.length; i++) {
for (var j = 0; j < state.scenario.treatments[i].costTypes.length; j++) {
state.scenario.treatments[i].costTypes[j]["annualized"] = annualized
}
}
what you should be doing is pass the treatmentID and cost type (which, I sherlock-holmed, is called costToFinance) from the action to the mutation, then filter the state for the matching object, and update only that:
UPDATE_ANNUALIZED (state, treadmentId, costToFinance, annualized) {
const treatment = state.treatments.find((t) => t.treatmentId === treatmentId)
const currCostType = treatment.costTypes.find((costType) => costType.Id === costToFinance)
currCostType.annualized = annualized
}
The problem is that your JSON doesn't seem to have any ID for the costTypes to find it by, but that's for you to figure out.