I've got an input field that looks like the following :
<tr v-for="(item, index) in collection">
...
<input
type="checkbox"
v-model="item.activated"
#change="toggleSwitch(item.resource_url, 'activated', item)">
>
...
</tr>
The collection is an array containing several keys, activated is one of them. activated is equal to 1 or 0 as the data is coming from a mysql database. The problem is that the input field is always set to true in this case, even if the activated is equal to 1 or 0.
Now, I tried writing the v-model like so to fix the issue :
v-model="!!+item.activated"
as by adding !!+ I'd convert the integer value to a boolean and use that. That fixes the issue, but creates another. The other issue I get by doing so is when I try to change my checked input I get an error :
[Vue warn]: Cannot set reactive property on undefined, null, or primitive value: false
admin.js:120238 TypeError: Cannot use 'in' operator to search for 'activated' in false
The toggleSwitch method looks like this :
toggleSwitch: function toggleSwitch(url, col, row) {
var _this8 = this;
axios.post(url, row).then(function (response) {
_this8.$notify({ type: 'success' });
}, function (error) {
row[col] = !row[col];
_this8.$notify({ type: 'error' });
});
},
I'm new to Vue.js, any idea how to debug this and where could my problem be coming from? I'll gladly give any additional info.
Edit :
Here's my component
Vue.component('profile-edit-profile-form', {
mixins: [AppForm],
data: function() {
return {
form: {
...
activated: false ,
...
}
}
}
});
If you use AJAX to populate your collection, then you should convert your 0 and 1 strings to booleans in your AJAX callback before injecting them into your component. Or even better you could convert them directly from your controller, by the way you directly get true|false
data.forEach(function(entry) {
if(entry.hasOwnProperty("activated"))
entry.activated = !!+entry.activated
});
my recommendation is:
Database column "activated" tinyint(1)
in laravel model use $cast array to cast "activated" to "boolean"
in vue use native type boolean for form.activated with true and false
Laravel Model:
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'minimum' => 'float',
'maximum' => 'float',
'step' => 'float',
'minItems' => 'integer',
'maxItems' => 'integer',
'uniqueItems' => 'boolean',
];
Vue:
<b-form-radio-group id="uniqueItems" v-model="formData.uniqueItems" :options="optionsBoolean" name="uniqueItems" :/>
optionsBoolean (){
return [
{ text: 'Yes'), value: true },
{ text: 'No'), value: false }
]
}
Related
This question already has answers here:
Access vue instance/data inside filter method
(3 answers)
Closed 2 years ago.
I'm creating a simple Vuejs div component (to show a specific value) which needs to receive: a lists, a placeholder and a value as props. What I'm trying to do is displaying the value with the data from my database, if the user picks a new value from the lists, it should take that new value and display it. However, if the user never picks a new value and the data from the database is empty, it should display the placeholder.
So I have used filters to achieve this. However, it outputs an error: "Cannot read property 'lists' of undefined", which comes from the filters (I know because it outputs no error if I comment out the filters). When I changed the filter to this:
filters: {
placeholderFilter () {
return this.placeholderText || this.placeholder
}
}
It says:""Cannot read property 'placeholderText' of undefined"". So I was wondering if the filters properties executed before the data and props properties. What is the execution order of them? I have attached some of the relevant code down below. Anyway, If you could come up with a better way to achieve this. I would appreciate it!
Here is my component:
<template>
<div>{{ placeholderText | placeholderFilter }}</div>
<li #click="pickItem(index)" v-for="(list,index) in lists" :key="index">{{ list }}</li>
</template>
<script>
export default {
props: {
lists: {
type: Array,
required: true
},
value: {
type: [String, Number],
default: ''
},
placeholder: {
type: String,
default: ''
}
},
data () {
return {
selected: -1,
placeholderText: this.value || this.placeholder
}
},
methods: {
pickItem (index) {
this.selected = index
}
},
filters: {
placeholderFilter () {
return this.lists[this.selected] || this.placeholderText || this.placeholder
}
}
}
</script>
And this is where I use it:
<my-component
placeholder="Please type something"
value="Data from database"
lists="['option1','option2','option3']"
>
</my-component>
Filters aren't bound to the component instance, so they simply don't have access to it through the this keyword. They are meant to always be passed a parameter and to return a transformed version of that parameter. So in other words, they're just methods. They were removed in Vue 3 entirely probably for that reason.
And yeah, what you're looking for here is a computed!
I attempted to create a model in sequelize (say has 3 attributes, attrA, B, and C) with some custom validation logic. This tutorial helped me get most of it set up:
const Model = Sequelize.define('model', {
attrA: { type: Sequelize.STRING },
attrB: { type: Sequelize.STRING },
attrC: { type: Sequelize.STRING },
}, {
validate: {
someValidationLogic() {
// Do something with attrA,B,C
// if (this.attrA ... this.attrB ... this.attrC) throw new Error..
}
}
})
In the application logic however, only say, 2 out of the 3 attributes (A and B) need to be updated:
Model.update(
{
attrA: 'foo',
attrB: 'bar'
}, {
where: {
id: 1,
},
returning: true,
})
This results in that when the custom validation logic being called, in the this object accessed in the function, only attrA and attrB are defined in this, and attrC remained undefined. This causes the validation logic to fail because attrC cannot be read. Is there any way I can get the object visible from someValidationLogic() to have all attributes populated? Or should this "validation" shouldn't have been validation logic at all and should've been done on the application level?
Your validation logic could take in account the possibility of attrC not being defined :
validate: {
someValidationLogic() {
if (this.attrA !== 'undefined' && this.attrA === 'forbidden value' ) {
// throw new Error
}
}
}
But if your validation includes checking the provided values against current database values, then you would better handle this in the application layer : first recover the current database record, manipulate it as needed, then save it to database.
Suppose I have an array feedsArray, the example value may look like this:
this.feedsArray = [
{
id: 1,
type: 'Comment',
value: 'How are you today ?'
},
{
id: 2,
type: 'Meet',
name: 'Daily sync up'
}
]
Suppose I have registered two components: Comment and Meet, Each component has a prop setting as the following:
props: {
feed: Object
}
and the main component has the following definition:
<component v-for="feed in feedsArray" :feed="feed" :key="feed.id" :is="feed.type"></component>
As you can see, it uses is property to select different component. My question is, how to detect feed object change in the child component ? Like when I set
this.feedsArray[0] = {
id: 1,
type: 'Comment',
value: 'I am not ok'
}
How can the Comment component detect the changes ? I tried to add a watcher definition in the child component like the following:
watch: {
feed: {
handler (val) {
console.log('this feed is changed')
},
deep: true
}
},
But it doesn't work here. Anyone know how to solve this ?
Do not assign directly to an array using index - use splice() instead, otherwise JavaScript can not detect that you have changed the array.
If you want to change only the value of an already existing key of an object - then simply update it e.g. this.feeds[0].value = 'I am not okay any more';
This works for existing keys only - otherwise you have to use this.$set(this.feeds[0], 'value', 'I am not okay any more');
I'm currently in the process of building a CMS using Laravel and Vue JS which build forms dynamically based on an array of data created in the Laravel model. Here is an example:
class TheBuilding extends Model
{
protected $fillable = [...];
public function formFields(){
$fields = [
[
'label' => 'Title',
'name' => 'title',
'component' => 'input_text'
],
[
'label' => 'Content',
'name' => 'content',
'component' => 'input_textarea'
],
[
'label' => 'Main Image',
'name' => 'main_image',
'component' => 'input_single_upload'
],
[
'label' => 'Gallery',
'name' => 'gallery',
'component' => 'input_multiple_upload',
'meta' => [
[
'type' => 'text',
'name' => 'caption',
'label' => 'Caption'
]
]
],
];
return $fields;
}
}
Basically this array gets passed into Vue JS and parsed to dynamically display Vue JS form components accordingly. This has been working great but I've come across an interesting issue with the Gallery multiple upload component which needs the ability to assign captions to images.
To fast forward a bit, I'm at the point where I have an array of uploaded files which get iterated through and displayed on the page, and then I have the input textfield for the caption underneath.
Here's my component (edited to show the relevant bits):
<template>
<div class="row">
<div v-for="(file, i) in files">
<img :src="file.file" >
<div v-for="meta in file.meta">
<input v-if="meta.type == 'text'" type="text" v-model="meta.value">
</div>
</div>
</div>
</template>
<script>
export default{
computed:{
files(){
let uploads = [];
/*this.uploaded is just an array of filenames*/
this.uploaded.forEach((file, i) => {
let createdMeta = [
{
name:"caption",
type:"text",
value:''
}
];
uploads.push({file,meta:createdMeta});
});
return uploads;
}
},
props:{ ... },
mounted(){
//CODE THAT HANDLES DROPZONE UPLOAD
},
name: 'InputMultipleUpload',
data(){
return {
showUploadProgress:true,
}
}
}
</script>
The bit I'm focusing on is:
let createdMeta = [{
name:"caption",
type:"text",
value:''
}];
You'll notice here that I've created that array statically. If I do that, when I type in a caption textbox everything works fine and the caption value gets updated dynamically by v-model as expected. Essentially, I get the desired result and everything is good.
However, if I try and set this this dynamically from the created model ie:
let createdMeta = formFields;
where formFields is the reference to the model array, when I then type in the textbox it updates all other textboxes and values in the files array created. V-Model no longer seems to relate to the specific textbox.
So I guess the question I'm asking is:
a) Why is it behaving that way when I passed in the referenced array
b) Why does it work fine if I just manually create that array?
c) How can I get A to behave like B?
Thanks everyone, happy to clarify anything. I assume i'm missing a piece in the reactivity puzzle.
Cheers,
Lew
You should be using data not computed.
Try something like this:
<script>
export default {
props: {...},
mounted () {
//CODE THAT HANDLES DROPZONE UPLOAD
// 2. handlers should call `onUpload`
},
name: 'InputMultipleUpload',
data () {
return {
files: [], // 1. declaring `files` here makes it reactive
showUploadProgress: true,
}
},
methods: {
onUpload (file) {
const meta = createdMeta = [{
name: "caption",
type: "text",
value: ''
}]
this.files.push({file, meta}); // 3. push the new data onto the stack and the component will update
}
}
}
</script>
With this:
let createdMeta = formFields;
uploads.push({file,meta:createdMeta});
in a loop you actually just pass the reference to the same formFields array object and then binds all your inputs to it.
This is likely to work as it should, if you pass a new array copy for each input. You can do it easy this way:
let createdMeta = formFields.slice();
let createdMeta = JSON.parse(JSON.stringify(formFields))
Thanks to Rick in Slack, and Dave Steward too, I have refactoring to do but for the purpose of this thread here is the solution
I'm writing my service to update a row using sequelize for PostGres. When I try out my query using a PSequel it works fine:
UPDATE "test_table" SET "test_col"=NULL WHERE "id"= '2'
But using sequelize it throws a 500 error:
db.TestTable.update({ testCol: NULL }, { where: { id: id } })
.then((count) => {
if (count) {
return count;
}
});
My model does allowNull which I believe is what allows null values to be the default as well as set:
testCol: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
field: 'test_col'
},
Any other value but NULL works as expected. Is there a different method for setting null values?
From the looks of it, I think your issue is that you are using SQL's syntax for a null value ('NULL') where you should be using JS syntax ('null').
db.TestTable.update({ testCol: null }, { where: { id: id } })
.then((count) => {
if (count) {
return count;
}
});
should work.
Have you checked a more detailed error message in logs? I'd suggest you to add a promise catching error and then update your question.
For now, my guess is that you created your connection with omitNull: true. Call an update function with just one null property probably is the reason of error 500 because it'll generate a incomplete UPDATE command (without SET).
Try to set omitNull: false or, if you cannot do this test, try to update this way:
db.TestTable.testCol = null;
db.TestTable.save(['testCol']);
More info here.