Statically passing arrays to a Vue component - vue.js

I need to statically pass an array to my Vue component, called ajax-table. I can't seem to find a way to do it, so I came up with this:
<ajax-table
header-names="Code, Name, Description, Type"
field-names="code, name, description, major_type">
</ajax-table>
Inside the component, I do this:
export default {
props: [
'headerNames',
'fieldNames'
],
data: function () {
return {
columnHeaders: [],
columnFields: []
}
},
created() {
this.columnHeaders = this.headerNames.split(",").map(x => x.trim());
this.columnFields = this.fieldNames.split(",").map(x => x.trim());
}
}
Now, columnHeaders and columnFields contain the header-names and field-names that I passed statically to the component.
My question:
Is there a better way to do this?

You should be able to directly pass the array to props using v-bind: directive or : for short:
<ajax-table
:header-names="['Code', 'Name', 'Description', 'Type']"
:field-names="['code', 'name', 'description', 'major_type']">
</ajax-table>
Now props headerNames and fieldNames are arrays, which you can use in the component.

Related

Use v-model in a v-for loop

I have a component with the following data -
data() {
return {
name: '',
age: '',
}
}
I then define a method like the following -
formData() {
const data = [
{label: 'Name', model: this.name},
{label: 'Age', model: this.age},
]
return data
}
In my template, I am writing a v-for loop accessing the formData() to render the HTML. (I'm doing it this way because there will be ~50 form fields like name and age and the HTML is the same for every form field).
<md-table>
<md-table-row v-for="d in formData()" :key="d.label">
<md-table-cell>{{d.label}}</md-table-cell>
<md-table-cell>
<md-field>
<md-input v-model="d.model"></md-input>
</md-field>
</md-table-cell>
</md-table-row>
</md-table>
This template renders fine. However, the models are not bound, because the values of name and age does not change in the data if the user enters in the input fields.
I am pretty sure this is because when declaring formData(), I am actually passing the values of the data in the model. Is there a way I can actually pass the model, so that the template v-models bind?
Use props:
In the child component define:
props: ['name', 'age']
and remove the data element.

How can i splice a object from a "props" array and assign it to a predefined "data" attribute with it being accessible and reactive?

My Component:
props: {
answerOptions: Array
},
data: function() {
return {
selectedOption: null,
//selectedOption: {}, also not working
};
},
methods: {
select(option, i){
//this.selectedOption = this.answerOptions.splice(i, 1); not working
//Object.assign(this.selectedOption, this.answerOptions.splice(i, 1)); also not working
this.selectedOption = Object.assign({}, this.answerOptions.splice(i, 1)); //still not
working!
console.log(this.selectedOption) //prints a observable object with alle the correct values
console.log(this.selectedOption.anyAttribute) //prints undefined
}
edit:
thats where the select function is called:
<div
ref="option-elems"
v-on:click="select(option, i)"
v-for="option, i in answerOptions"
>
The answerOption array gets rendered via a for loop just fine. When the select() Method is called on one of the answerOption objects it removes it from the array and updates that rendered list in the ui correctly. I can even conditionally render (v-if="selectedOption") with the selectedOption Object. But for the love of god, im am not able to access any of its Attributes like so: {{ selectedOption.anyAttribute }}
What am i doing wrong here?
You need to get the object inside the array that is called "selectedOption". When you define this, you are still having the object wrapped in an array, so simply get its first item:
this.selectedOption = answers.splice(i, 1)[0];
Demo Here

Is there an easier way of updating nested arrays in react-native (react-redux)?

I am trying to update an array inside of my array (nested array) with react-redux. I found a solution to how to do this but is there any easier way of doing this rather than passing multiple parameter to the action.
[types.HISTORY_UPDATE](state, action){
return {
...state,
budgets: [
...state.budgets.slice(0,action.id),
{
key: action.key,
id: action.idd,
name: action.name,
budgetType: action.budgetType,
startDate: action.startDate,
currency: action.currency,
amount: action.amount,
amountLeft: action.amountLeft,
rollOver: action.rollOver,
color: action.color,
iconName: action.iconName,
history: [
...state.budgets[action.id].history.slice(0,action.histId),
{
note: action.note,
amount: action.amount,
type: action.type,
date: action.date,
fullDate: action.fullDate,
hours: action.hours,
min: action.min,
month: action.month,
year: action.year
},
...state.budgets[action.id].history.slice(action.histId+1)
]
},
...state.budgets.slice(action.id+1)
]
}
},
and the action goes like this
export function updateHistory(id,key,idd,name,budgetType,startDate,currency,amount,amountLeft,rollOver,color,iconName,histId,........){
I don't want to spend time with passing multiple parameter like this while using react-redux and also while I tried to run my application on my phone sometimes it really slows the application. Is it because of the example above?
I would be really appreciated If you guys come up with a solution.
I typically do not store arrays in redux, since updating a single element really is a burden as you noticed. If the objects you have inside your array all have a unique id, you can easily convert that array to an object of objects. As key for each object you take that id.
const convertToObject = (array) => {
let items = {};
array.map((item) => {
items[item.id] = item;
});
return items;
};
In your action you simply just pass the item you want to update as payload, and you can update the redux store very easily. In this example below I am just updating the budgets object, but the same logic applies when you assign a history object to each budget.
[types.BUDGET_UPDATE](state, action){
const item = action.payload;
return {
...state,
budgets: {
...state.budgets,
[item.id]: item
}
}
}
And if you want an array somewhere in your component code, you just convert the redux store object back to an array:
const array = Object.values(someReduxStoreObject);

Component's Array index data not updating

I need to update some Array data in my VueJS component which is rendering as a list in the template via v-for.
When I update the whole Array, I see that the list updates in the DOM. But, if I update only an index, the list does not update.
Here are the two relevant methods:
methods: {
loadOnlyOneIndex: function() {
this.data[0] = {
title: 'Hello error',
slug: 'hello',
desc: 'will not work'
};
},
loadEverything: function() {
this.data = [{
title: 'Hello new title',
slug: 'hello',
desc: 'this will work'
}, {
title: 'Hello new title 2 !',
slug: 'hello2',
desc: 'really work'
}];
}
}
Here is a fiddle.
From the documentation:
Due to limitations in JavaScript, Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
To get Vue to react to the change of an array's index, use the Vue.set() method.
In your case, you should use Vue.set(this.data, 0, newValue) in your loadOnlyOneIndex method.
Here's a working fiddle.
Every Vue instance also has an alias to the Vue.set method via vm.$set (where vm is the Vue instance).
So, you could also use this.$set(this.data, 0, newValue) in your loadOnlyOneIndex method.
This is helpful when using Single File Components, or anywhere where you don't have a direct reference to the Vue object.

Sharing data from a VueJS component

I have a VueJS address lookup component.
Vue.component('address-lookup',
{
template: '#address-lookup-template',
data: function()
{
return {
address: {'name': '', 'town:': '', 'postcode': ''},
errors: {'name': false, 'town': false, 'postcode': false},
states: {'busy': false, 'found': false},
result: {}
}
},
methods:
{
findAddress: function(event)
{
if( typeof event === 'object' && typeof event.target === 'object' )
{
event.target.blur();
}
$.ajax(
{
context: this,
url: '/lookup',
data:
{
'name': this.address.name,
'town': this.address.town,
'postcode': this.address.postcode
},
success: function(data)
{
this.states.busy = false;
this.states.found = true;
this.address.name = data.name;
this.result = data;
}
});
},
reset: function()
{
this.states.found = false;
this.result = {};
}
}
});
Inside my template I've then bound the result like so:
<p>{{ result.formatted_address }}</p>
There is some extra data returned within the result (like a twitter handle) that isn't part of the address lookup template, and occurs on a separate part of the form. For reasons relating to how my form is structured I can't include these inputs within the same template.
I found a way to bind those inputs, although it felt somewhat 'hacky'.
<input type="text" name="twitter" v-model="$refs.lookupResult._data.result.twitter">
That all works fine.
My problem is that the form is included as part of a larger template sometimes in the context of creating a new record, sometimes in the context of editing. When editing a record, the lookup component is removed (using an if server-side, so the template is no longer loaded at all) and when that happens I get this error.
$refs.lookupResult._data.result.twitter": TypeError: Cannot read property '_data' of undefined
This makes sense. lookupResult is defined when I include the template, and when editing I am removing this line:
<address-lookup v-ref:lookup-result></address-lookup>
I've worked around it by including a version of each extra input without the v-model attribute, again using a server-side if. But there are quite a few of these and it's getting a bit messy.
Is there a cleaner approach I could be using to better achieve this?
So I don't know the hierarchy of your layout, it isn't indicated above, but assuming that address-lookup component is a child of your parent, and you in fact need the results of address lookup in that parent, eg:
<parent-component> <!-- where you need the data -->
<address-lookup></address-lookup> <!-- where you lookup the data -->
</parent-component>
then you can simply pass the data props, either top-down only (default) or bidirectionally by defining 'address' for example on your parent's vue data hook:
// parent's data() function
data = function () {
return {
address: {}
}
}
// parent template, passed address with .sync modifier (to make it bi-directional)
<parent-component>
<address-lookup :address.sync='address'></address-lookup>
</parent-component>
// have the props accepted in the address look up component
var addressComponent = Vue.extend({
props: ['address']
})
Now in your $.ajax success function, simply set the props you need on this.address. Of course you can do this with all the props you need: errors, results, state etc. Even better, if you can nest them into a single key on the parent, you can pass the single key for the object containing all four elements instead of all four separately.