v-model is not working in vue? - javascript

Actually , this is my problem in my real project doing with API . Let me first explain that , I used axios for call api data . That will get json array and each array has the same radio value so I append radio value to each array . Although I want to get changed radio value by v-model , but it is not working . I outputted selected value under radio element . Below I demonstrated like my project code .
var app2 = new Vue({
el: '#app-2',
data: {
list: null
},
created: function () {
var json = [{id:1,name:"B"},{id:2,name:"D"}]
this.list = json
this.list.forEach(function (v) {
v.options = [{ text: 'pacageA' , value: 1},{text: 'pagckaeB',value:2}]
v.selected = null
})
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="app-2">
<div v-for="l,ix in list">
<div v-for="o in l.options">
<input type="radio" v-model="l.selected" :name="'test'+ix" :value="o.value">
</div>
<h3>{{l.selected}}</h3>
</div>
</div>

Your problem is a reactivity one. In order for Vue to know about the new object properties you're adding to your list, you should use Vue.set, eg
Vue.set(v, 'options', [{ text: 'pacageA' , value: 1},{text: 'pagckaeB',value:2}])
Vue.set(v, 'selected', null)
Or, as mentioned below, "do all the modifications to json before assigning it to this.list".

Related

How to fix value update delay in vuejs after axios request?

I am retrieving a list of data from an api and need to fill the specific <select></select> tags, which is associated to a few radio button, with some of the data as <options></options>. The radio buttons waiting for an event (#change/#click) and executing and axios get request. Everthing works fine. I click on a radio button and retrieving the data as response (vue tools also showing the right data) but the <option></option> tags are not updating. Now when I click on another radio button, I am getting again the right data from the api BUT now the <option></option> tags are refreshing with the data from the previous response.
Template
<!-- CREATING 7 RADIO BUTTONS FOR THE CURRENT WEEK FROM MON-SUN -->
<div class="wrapper" v-for="item in inputDetails">
<input :id="'datetime[0]['+item.labelText+']'" type="radio" name="datetime[0][date]" v-model="formData.datetime[0].date" :value="item.inputValue" #change="getTimes" />
</div>
<!-- CREATING THE TIME PICKER -->
<select id="datetime[0][time]" name="datetime[0][time]" v-model="formData.datetime[0].time">
<option selected="selected"></option>
<option v-for="item in selectOptionTimes[0]" :value="item.value">{{ item.label }}</option>
</select>
<!--
2 MORE RADIO BUTTON SECTION AND TIME PICKER SECTIONS WITH DIFFERENT INDEXES
<input id="datetime[1][time]"...
-->
Script
data() {
return {
formData: {
datetime: [
{date: '', time: ''},
{date: '', time: ''},
{date: '', time: ''},
]
}
selectOptionTimes: [],
}
},
methods: {
getTimes: function (current) {
let instanceIndex = current.currentTarget.id.match(/(?<=\[)([0-9])(?=])/g)[0]; // getting the index of the current datetime section
axios.get('/api-url', {
params: {
location_id: this.formData.location_id,
date: current.currentTarget.value
}
}).then(response => {
this.selectOptionTimes[instanceIndex] = response.data;
});
}
}
Does someone know what the problem is here?
You cannot assign a value to an arbitrary index within an empty Array in this way. You must either completely replace the Array with values that hydrate that index, or you must use $set.
So, to recap:
BAD
this.selectOptionTimes[instanceIndex] = response.data
GOOD
this.$set(this.selectOptionTimes, instanceIndex, response.data)
Note though, that this has an unintended consequence. If you have an empty array, and call this.$set on an index greater than 0, the array will be filled with empty values up to your index.
What might make more sense is using an {} instead along with this.$set and looping over the Object.keys instead of the array directly.
Fiddle showing $set on index with an empty array
Fiddle showing Object usage instead

Using $set to grab jquery variable and push it to an vue array object

I'm pulling data from a API and using jQuery's getJson method to extract the data I'm then trying to assign the data to a vue array object by utilizing app.$set.
So far I've been able to extract the data and assign it to the vue array but I can only access one thing at a time.
<div id="app">
<div v-once id="movies">
{{movieCall()}}
</div>
<div v-for="(movie,index) of movies" class="card" style="width: 18rem;">
<!-- <img src="..." class="card-img-top" alt="..."> -->
<div class="card-body">
<div class="card-title">{{movie}}</div>
</div>
</div>
</div>
var app = new Vue({
el: "#app",
movies: [
],
},
methods:
$.getJSON("https://api.themoviedb.org/3/movie/now_playing?api_key=9d9f46b8451885697e5cf7d1927da80f", function (movie) {
for (let i = 0; i < 3; i++) {
app.$set(app.movies, i, movie.results[i].title);
}
for (var x = 0; x < app.movies.length; x++) {
console.log(app.movies[x])
}
})
},
I'm extracting the movie and setting the title to the movie array but I'm wanting to assign it instead to a movie{title} object. This is so when I go through my v-for loop I can refer to the movie object array as movie.title, movie.overview, etc. to print them all. e.g.
In other words, is there a way to do:
app.$set(app.movies.title, i, movie.results[i].title);
app.$set(app.movies.overview, i, movie.results[i].description);
etc.
and have my movie array set up as:
movie[
{title:}
{description:}
]
and finally loop through like:
<div v-for(movie, index) of movies>
<div class="titles">
{{movie.title}}
</div>
<div class="descriptions">
{{movie.description}}
</div>
</div>
If you want to access movies like:
<div v-for="(movie, index) of movies">
...
{{movie.title}}
...
{{movie.description}}
Then populate it as:
app.$set(app.movies, i, {title: movie.results[i].title, description: movie.results[i].description});
Or, if i is incrementing one by one, the equivalent:
app.movies.push({title: movie.results[i].title, overview: movie.results[i].description});
Your code needs a bit of an upgrade / correction before it's OK, so I prepared a snippet for you that does the same thing (with a mockup JSON response), so you can see that you don't need app or $set for this.
var app = new Vue({
el: "#app",
data: {
movies: []
},
methods: {
movieCall() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(json => {
json.forEach(movie => this.movies.push(movie))
// or just simply:
// this.movies = json
})
}
},
created() {
this.movieCall()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(movie,index) in movies">
<div>
<div>{{index + ". Title: " + movie.title}}</div>
<div>{{index + ". Body: " + movie.body}}</div>
</div>
</div>
</div>
I tried to keep this as close to your code as possible (but CSS classes were taken out).
Basically Vue is reactive (data property), so if you update the data property movies the template should immediately react to that and update the UI. That's the core idea of a JS frontend framework.
Accessing data in object syntax (like movies.title, with a dot), is another matter. In your project you set up a data property - movies, that's an array. An array can hold any type of elements - objects too.
The idea is that you download the objects and then read them (one by one) into the movies data property. Or, if you receive an array, then this.movies = response (make them equal, assigning the response to the movies array.
Then there's an other thing: Vue templates have their lifecycle, and created() is a hook, that can be used to execute functions when the template is created. If you want something to run once, you should utilize these lifecycle hooks. (Of course, if your app reloads this template, then it executes the hook many times. To avoid this (downloading something multiple times) you should look into state management, but that's a large topic in itself.)
Add data key to hold the data set
<script>
var app = new Vue({
el: "#app",
data: {
movies: [],
},
methods:
$.getJSON("https://api.themoviedb.org/3/movie/now_playing?api_key=9d9f46b8451885697e5cf7d1927da80f", function (movies) {
for (var x = 0; x < movies.results.length; x++) {
//console.log("\nTitle"+movies.results[x].title);
//app.movies.$set(x, movie.results[x].title);
app.movies.push(movies.results[x].title);
console.log(JSON.stringify(app.movies))
}
})
});
</script>
And try with this command
app.movies.push(movie.results[i].title);
Here is a working example or sample which i created : https://plnkr.co/edit/EnepqQqXzEquJlxqjzn6?p=preview
Ref1: https://v2.vuejs.org/v2/guide/list.html

Forcing v-validate to update rules (with Vue)

I'm using v-validate with Vue. I'm trying to figure out how to force v-validate to update rules. For example, I have something like this:
<template>
<div v-for="field in fields">
<input :name="field.name" v-validate="field.rules">
</div>
</template>
<script>
export default {
data() {
fields: [
{
name: "city",
rules: {
included: []
}
}
]
}
}
</script>
As you can see, my "included" array is empty on page load. I get the array from an AJAX request, and then I update my data:
this.fields[0].rules.included = cities
But v-validate doesn't seem to acknowledge the newly-added array. It only works if I hardcode the cities into my data. How can I force v-validate to respond to the updated rules?
Vue.js is unable to track updates on nested reference types.
Try:
let fields = [...this.fields]
fields[0].rules = cities
this.fields = fields
Use Vue.set to track changes : https://v2.vuejs.org/v2/guide/reactivity.html
Vue.set(this.fields[0], 'rules', cities);

Vue.js 2 - retrieve form elements from server and render CRUD

I'm Vue.js newbie and my task is:
make an ajax call (GET) to server, using RESTful API (Laravel on background)
retrieve a (JSON) list of Form CRUD items in array (like checkbox, input text, textarea...) with their properties (value, checked, custom classes...)
render CRUD form with these form items maybe using Vue's loop
I'm wondering if it could be rendered using components somehow. But I don't know the correct way.
Frankly, I exactly don't know how to solve this problem with Vue.js - rendering items from array and each item has it's own markup and properties (checkbox has it's own, textbox, select, textarea...).
I'm building a web application based on CRUD operations and I'm trying to write universal components. The easiest way is to do a special component with hard-written sub-components for each subpage, but I don't like this way if not needed.
Thank you!
EDIT: I don't have much code yet, but this is where I am...
<script>
// ./components/CrutList.vue
export default {
mounted() {},
data() {
return {
items: []
}
},
props: ['resource'],
methods: {
getItems() {
var resource = this.$resource('api/'+this.resource+'{/id}');
resource.get({}).then(function(items){
if(items.body.status == 'success'){
this.items = items.body.items;
}
}).bind(this);
},
deleteItem(item) {
// perform CRUD operation DELETE
alert('delete action');
}
}
}
</script>
My idea is using CrudList component to CRUD listing...
<crud-list resource="orders">
In laravel I do something like this:
return response()->json([
'status' => 'success',
'items' => [
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.1",
'name' => 'checkbox1'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.2",
'name' => 'checkbox2'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'name' => 'checkbox3'
]
],
],
]);
...it's very simplified, but it's just example of what I'm doing.
Now the problem is:
take the 'itemComponent' part from the returned array item (this is in a loop),
if it's a checkbox, take (for example) Checkbox.vue component, fill it with properties ('props' part of the array item)
I read about slots, but it's not what I'm looking for. Is there something I can use for dynamic components?
Check out this jsFiddle working example for dynamic forms:
https://jsfiddle.net/mani04/kr8w4n73/1/
You can do it easily by using a lot of v-ifs for each and every form element type you might get from server. It is a bit cumbersome but I can't find any other way.
In the above example, I have the form structure as follows:
var formItems = [{
input_type: "text",
input_label: "Login",
values: {
value: "your_name#example.com"
}
},
{...},
{...}];
Once you have that data, then it is a matter of iterating through formItems, checking input_type and activating the relevant form control.
Here is how my dynamic form template looks like, for the above input:
<div v-for="formItem in formValues">
<div v-if="formItem.input_type == 'text'">
<input type="text" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'password'">
<input type="password" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'checkbox'">
<input type="checkbox" v-model="formItem.values.checked">
{{formItem.values.label}}
</div>
</div>
My jsFiddle example uses form-horizontal from bootstrap, and I am also able to display the labels well. If I put that in the example above, it will get cluttered and will not let you see how it works.
Hope it helps! You can change the formItems data structure to meet your needs, and modify the template accordingly.

AngularJS Push in Collection, Show from Collection, $watch changes

I use object ITEMS to hold data
$scope.items = [
{value:'title1', text: 'head1',
value:'title2', text: 'head2',
value:'title3', text: 'head3' }
];
When I clicked 'Add option' button I need show 'value' and 'text' in HTML page:
$scope.items.push(
{
value: 'value1',
text: 'text1'
}
);
I can show object length, but I can't show added option.
And $watch ($watchCollection) doesn't work too.
In this example I don't get values from inputs.
enter link description here
Your $scope.items array is improperly declared. You need braces around each separate item in the array, like this:
$scope.items = [
{value:'title1', text: 'head1'},
{value:'title2', text: 'head2'},
{value:'title3', text: 'head3'}
];
Your directive is all kinds of messed up. You don't even need to create a new directive if all you want to do is display the items in a list. You can just do this:
<select ng-model="selectedItem" ng-options="item.text for item in items"></select>
Your textboxes are ok, except for the typo in the ng-model="addoText". Your labels below should be bound to the same variables as the textboxes.
key: {{addVal}} <br>and value: {{addText}}
That will update the labels as you type in the textboxes. If you don't want to update the labels until you add a new item, then bind them to some new variables, like this:
key: {{newVal}} <br>and value: {{newText}}
Finally, your add() function should look like this:
$scope.add = function () {
$scope.items.push(
{
value: $scope.addVal,
text: $scope.addText
}
);
$scope.newVal = $scope.addVal;
$scope.newText = $scope.addText;
};
This pushes the new item to the array, and sets the bindings on your labels to the new values. You don't need to $watch anything.
Here's a Plunker.
There is an issue with how your items array looks at the moment.
I think your $scope.items should look like:
$scope.items = [
{
value: "value1",
text: "text1"
},
{
value: "value2",
text: "text2"
}
]
rather than all in one object, as when you push you'll create a new object.
With your question, calling items.value, will result in an undefined.
You need to call an object in $scope.items. Calling items[$scope.items.length-1] will get the most recent object added, and such items[$scope.items.length-1].value and items[$scope.items.length-1].text the values in that object

Categories

Resources