Svelte Each loop is not updated while object changed dynamically - javascript

When i'm trying to change data dynamically of the each loop variable is not refreshing the DOM elements. How I can redraw the each loop values
<script>
// default messages object
let messages =[{
"name":"John",
"message":"Hi",
"checked": false
},
{
"name":"Anthony",
"message":"welcome",
"checked": true
},
{
"name":"David",
"message":"thank you",
"checked": false
}]
//click function to change the values dynamically
function test(){
messages.map(ob=>{
ob.checked = true;
})
console.log(messages)
}
</script>
template code
<div>
{#each messages as m}
<div>{m.checked}
<input type="checkbox" bind:checked={m.checked}>
{m.name}
</div>
{/each}
<br>
<button on:click={()=>{test()}}> check all values</button>
</div>
link for snippet: https://svelte.dev/repl/887e7e2de4114ec1bb3625c264b9a62c?version=3.24.1
Any help would be appreciated! Thank you :)

You can't update an array/object just by changing its attribute in svelte.
A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment.
Updating Arrays and Objects

map returns a new array, in your case you got a new array from undefined, you need assign the result and correctly return the new object
function test(){
messages = messages.map(obj => ({
...obj,
checked: true
}));
}

Related

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

Submit Javascript Object with Array of Object inside to the API

Good day everyone,
I need a help on how to submit an object with array of object inside and the array of object should be from a checkboxes I've made.
This is my sample checkbox
<input type="checkbox" name="user.preferences" value="Hard" />
<input type="checkbox" name="user.preferences" value="Soft" />
<input type="checkbox" name="user.preferences" value="Small" />
My data javascript looks like this:
user:{
preferences: []
}
When I alert theuser using JSON.stringify, I can able to see a result something like this.
{"preferences": ["Soft","Small"]}
But the problem is, the api that I'm using needs a format like this:
{
"preferences": [
{
"preference": "Hard"
},
{
"preference": "Soft"
},
// so on and so forth
]
}
Please somebody help me. Thank you
You should .map each string in preferences to an object with that preference as its key/value:
const user = {
"preferences": ["Soft", "Small"]
};
user.preferences = user.preferences
.map(preference => ({ preference }));
console.log(user);

Polymer 3: How to implement two way data binding for radio input

I'm trying to understand/implement two way attribute binding in a Polymer 3 web component. I've got the following code:
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
class CustomInputComponent extends PolymerElement {
static get template() {
return html`
<div>
<template is="dom-repeat" items="{{ratings}}">
<input type="radio"
name="group"
id="item_{{item.id}}"
value="{{item.checked}}"
checked$="{{item.checked}}">
</template>
</div>`;
}
static get properties() {
return {
ratings: {
type: Array,
value: [
{ id: 1, checked: true },
{ id: 2, checked: false }
]
}
};
}
}
window.customElements.define('custom-input-component', CustomInputComponent);
As you can see, I have defined a Array property containing a default list of values. This property is a model from which I want to render a radio group. The initial state looks good. But when I click on the unchecked input, the DOM elements don't update correctly.
I'd bet I'm missing something really simple...
The main things are:
You are binding to the checked attribute ($=), however I don't think radio inputs dynamically update their checked attribute. AFAICT, the checked property is what changes when the input gets selected.
Also, native <input type="radio"> inputs will only fire their change and input events when they are selected, not when they are de-selected. Polymer relies on events to trigger property effects like data bindings; this means that an upwards data binding (from the input to your custom element) will only get processed when the checked property on an input changes from false to true. Effectively, once ratings[n].checked becomes true, it will never be made false because Polymer has no way to know that this has occurred.
Incidentally, to perform two-way binding on a native HTML element, you would also need to include an annotation for the event that the radio input fires when it is selected. So if you did want to capture the changes on selection, it'd be something like checked="{{item.checked::change}}".
A couple of options:
Use paper-radio-buttons inside a paper-radio-group instead of native <input>s. These elements behave well for two-way data binding.
Listen for the change when a new item gets checked, and manually update ratings[n].checked to false for the previously selected item.
A couple more things about your code
(I don't think this is anything to do with your current problem, but it will help avoid future problems) when initializing a default value for an object or array property in a Polymer element, remember to use a function so that each element instance gets its own unique array or object. E.g.:
ratings: {
type: Array,
value: function(){
return [
{ id: 1, checked: true },
{ id: 2, checked: false }
];
}
}
Normally I think, you wouldn't want to change the values of your radio inputs. Conventionally, when the <form> containing a radio input group is submitted, the value on the radio input that is currently checked gets included with the form data, and the other radio input values are ignored. Here's an example on W3Schools. So instead of value="{{item.checked}}", something like value="[[item.data]]".
So the whole thing might be something like
class CustomInputComponent extends PolymerElement {
static get properties () {
return {
ratings: {
type: Array,
value: function(){
return [
{ id: 1, checked: true, value: 'pie' },
{ id: 2, checked: false, value: 'fries' },
{ id: 3, checked: false, value: 'los dos' }
];
}
},
selected: {
// type: Number or Object, idk
// Keep track of the selected <input>
}
};
}
static get template() {
return html`
<p>do you want pie or fries?</p>
<div id="mydiv">
<template is="dom-repeat" items="{{ratings}}">
<input
type="radio"
name="testgroup"
id="[[item.id]]"
value="[[item.value]]"
checked="{{item.checked::input}}"
on-change="radioChanged"
>[[item.value]]
</input>
</template>
</div>
`;
}
radioChanged(event){
// update ratings[n].checked for selected input
// set selected input to currently selected input
}
}

v-model is not working in vue?

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".

updating an item in a knockout bound list

I have a list of questions and each question has a list of answers and I am using knockout to display each question 1 at a time. What I am doing is setting up my model with the full list and then making a currentQuestion property observable and after each question is answered, I increment this to the next question. Problem is that I have to change some data on the question when the user hovers it but cant figure out how to make the answers observable.
I've put together a jsfiddle and what I want to do is change the answer text to 'modified' when the user clicks the answer.
How do I make the AnswerText observable so that when the click handler changes its value this is reflected in the UI.
Any ideas where I am going wrong would be appreciated.
jsfiddle code if below:
<div class="top">
<div data-bind="foreach: currentQuestion().Answers">
<div data-bind="click: $root.answerClicked">
<div data-bind="text: AnswerText"></div>
</div>
</div>
</div>
function MyVM() {
var self = this;
this.session = {
Questions: [
{
QuestionText: "Q1",
Answers: [
{
AnswerText: "Q1A1"
},
{
AnswerText: "Q1A2"
}
]
},
{
QuestionText: "Q2",
Answers: [
{
AnswerText: "Q2A1"
},
{
AnswerText: "Q2A2"
}
]
}
]
};
this.currentQuestion = ko.observable();
this.currentQuestion(self.session.Questions[1]);
this.answerClicked = function (selectedAnswer, event) {
alert('hello');
selectedAnswer.AnswerText = 'modified1';
selectedAnswer.AnswerText('modified');
};
}
var model = new MyVM();
ko.applyBindings(model);
Currently you're binding the UI to a static string. In order to make the UI reflect changes the string must be wrapped in an observable as you stated so it sounds like you were on the right track. All you need to do is use an observable in each answer object.
Answers: [
{
AnswerText: ko.observable("Q2A1")
},
{
AnswerText: ko.observable("Q2A2")
}
]
Then in the click function you'll want to get rid of the first assignment operator where it would be replacing the observable and use only the second line where a value is assigned to the observable instead.
this.answerClicked = function (selectedAnswer, event) {
alert('hello');
//selectedAnswer.AnswerText = 'modified1';
selectedAnswer.AnswerText('modified');
};

Categories

Resources