Cannot make data to be bind-able inside of the widget - durandal

once i change value of input, my main model ($data.name) is not updated.
Thanks in advance
some view
<td data-bind="editableLabel: { name: $data.name }"></td>
widget's view
<span data-bind="text: settings.name, click: edit, visible: !settings.inEditMode"></span>
<div class="input-group" data-bind="visible: settings.inEditMode">
<input class="form-control" data-bind="value: settings.name" />
<span class="input-group-btn">
<button class="btn btn-success" type="button" data-bind="click: save"><span class="glyphicon glyphicon-ok"></span></button>
<button class="btn btn-danger" type="button" data-bind="click: reset"><span class="glyphicon glyphicon-remove"></span></button>
</span>
</div>
widget's viewmodel
define(['context/context'], function (context) {
var ctor = function () { };
ctor.prototype.activate = function (settings) {
this.settings = settings;
this.settings.inEditMode = false;
};
ctor.prototype.edit = function () {
this.settings.inEditMode = true;
};
ctor.prototype.save = function () {
this.settings.inEditMode = false;
context.save();
};
ctor.prototype.reset = function () {
this.settings.inEditMode = false;
};
return ctor;
});

In the following line of code you are passing a reference to a property of the current VM (nameproperty) to the widget:
<td data-bind="editableLabel: { name: $data.name }"></td>
I suspect that the property name that you are passing to the widget is not a observable. If it isn't, then you are just passing a reference to a property which contains only a value and therefore it will not trigger any change event if it gets updated.

Related

V-model inside v-for is filling all the inputs

In few words I have multiple Todolists, once I try to post a new Todo inside the Todolist all the inputs of other Todolists gets fill in with the same words. How can I solve this?
Edit: I can post a new todo without problems and it will appear on the relative todolist (see addTodo method). The ONLY problem is that the v-model is for all the todolists on the field, not only for the one I'm writing in, so as soon as I start to write inside an input I got all the inputs filled.
<div class="todolist" v-for="todolist in todolists" :key="todolist.id">
<div class="td-title">
<h5 class="text-center m-0 py-2 text-light">
{{ todolist.title }}
</h5>
</div>
<form
#submit.prevent="addTodo(todolist.id)"
class="td-inputs d-flex align-items-center justify-content-center"
>
// this is the v-model
<input v-model="todo.title" class="px-2" type="text" />
<button type="submit">
<i class="fas fa-plus fa-2x"></i>
</button>
</form>
<div>
<div v-for="todo in todolist.todos" :key="todo.id" class="todo">
{{ todo.title }}
</div>
<div>
<i class="fas fa-times" #click="cancel(todolist.id)"></i>
</div>
</div>
</div>
data
data() {
return {
todolists: [],
loading: true,
todo: {
title: "",
todolist_id: "",
user_id: ""
},
};
},
Methods
getTodoLists() {
axios
.get("http://127.0.0.1:8000/api/todolists")
.then((res) => {
this.todolists = res.data;
this.loading = false;
})
.catch((err) => {
console.log(err);
});
},
cancel(id) {
axios
.delete(`http://127.0.0.1:8000/api/todolists/${id}`)
.then(() => {
this.getTodoLists();
})
.catch((err) => {
console.log(err);
});
},
addTodo(todolist) {
axios
.post(`http://127.0.0.1:8000/api/${todolist}/todos`, this.todo)
.then(() => {
this.todo = {}
this.getTodoLists();
})
.catch((err) => {
console.log(err);
});
},
Like down here: I'm writing on the left input and it shows even on the right input.
SOLVED:
With this computed property (with setter and getter) I solved my problem, now just the input in which I'm typing in is filled, and the todo can be post without a problem.
computed: {
getTitle: {
get: function () {
return "";
},
set: function (value) {
this.todo.title = value;
},
},
},
And this is the v-model with the new computed property instead of todo.title
<form
#submit.prevent="addTodo(todolist.id)"
class="td-inputs d-flex align-items-center justify-content-center"
>
<input v-model="getTitle" class="px-2" type="text" />
<button type="submit">
<i class="fas fa-plus fa-2x"></i>
</button>
</form>
You have a variable in data called "todo" and then you are reusing that variable name inside your inner v-for:
<div v-for="todo in todolist.todos" :key="todo.id" class="todo">
{{ todo.title }}
</div>
I think todo is referring to the todo from your data, not from the v-for. Thus, all todo items refer to the same variable. Try to rename one of them and see if it solves the problem.
You are binding your input to your todo declared data object.
Instead, you should bind your input to your todolist object in this way:
<div class="todolist" v-for="todolist in todolists" :key="todolist.id">
....
<input v-model="todolist.title" class="px-2" type="text" />
....
</div>
SOLVED:
With this computed property (with setter and getter) I solved my problem, now just the input in which I'm typing in is filled, and the todo can be post without a problem.
computed: {
getTitle: {
get: function () {
return "";
},
set: function (value) {
this.todo.title = value;
},
},
},
And this is the v-model with the new computed property instead of todo.title
<form
#submit.prevent="addTodo(todolist.id)"
class="td-inputs d-flex align-items-center justify-content-center"
>
<input v-model="getTitle" class="px-2" type="text" />
<button type="submit">
<i class="fas fa-plus fa-2x"></i>
</button>
</form>

Vue.js adding a toggle and method to a button

I have a button that should toggle and also call a method. How do I achieve this? Seems like it can be only one or the other.
new Vue({
el: "#app",
data: {
iExist:false,
iDoNotExist: true,
},
methods: {
iSignedUpforThis: function(){
console.log("step X");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-show="iExist"> i EXISTS </p>
<p v-show="iDoNotExist">
<strong> You are not found: </strong>
<form >
First name:<br>
<input type="text" name="firstname" value="Mickey">
<br>
Last name:<br>
<input type="text" name="lastname" value="Mouse">
<br><br>
</form>
<BUTTON v-on:click="iExists = iDoNotExist">
TOGGLE MY EXISTENCE
</BUTTON>
</div>
Move
iExists = iDoNotExist to a method:
methods: {
iSignedUpforThis: function(){
this.iExist = this.iDoNotExist
console.log("step X");
}
}
<button v-on:click="iSignedUpForThis">
TOGGLE MY EXISTENCE
</button>
First off to accomplish your desired result you need only one Boolean variable. Then in your method just switch between true and false. Also you have an invalid markup - there is closing tap p but no closing. That's why your example does not work.
Notice: it's bad idea to nest form tag inside p tag, so use div instead. It's considered a good practice to associate your input with it's label using label tag. Also there is shortcut for v-on:click - #click. data should be an function that returns an object, this will prevent . multiple instance to share the same object.
If you follow above recommendations you will make your code much clear and bug-less.
new Vue({
el: '#app',
data: {
isExist: false,
},
methods: {
method() {
this.isExist = !this.isExist
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-show="isExist">I exist</div>
<div v-show="!isExist">
<strong>You are not found:</strong>
<form>
<label>First name:<br>
<input type="text" name="firstname" value="Mickey">
</label>
<br>
<label>Last name:<br>
<input type="text" name="lastname" value="Mouse">
</label>
</form>
</div>
<button #click="method">Toggle</button>
</div>
It might be late but I am sure it will help others. Create a component ToggleButton.js and paste the below codes.
<template>
<label for="toggle_button">
<span v-if="isActive" class="toggle__label">On</span>
<span v-if="! isActive" class="toggle__label">Off</span>
<input type="checkbox" id="toggle_button" v-model="checkedValue">
<span class="toggle__switch"></span>
</label>
</template>
<script>
export default {
data() {
return {
currentState: false
}
},
computed: {
isActive() {
return this.currentState;
},
checkedValue: {
get() {
return this.defaultState
},
set(newValue) {
this.currentState = newValue;
}
}
}
}
</script>
Take a look at the article to learn more https://webomnizz.com/create-toggle-switch-button-with-vue-js/

How to apply EDIT and Cancel features using inline row vue Grid

I am trying to make an edit and cancel/undo function on my inline-grid row.
This is what I have tied and I cannot see how to make it functional.
HTML:
<!-- ..iterate.. -->
<span v-if="!row.isEditable" v-html="row.column"></span>
<input
type="text"
class="form-control"
v-model="row.column" //object property set
v-if="row.isEditable"
/>
<button class="btn cancel" v-show="row.isEditable" v-on:click="undoRow(index)"></button>
<button class="btn edit" v-show="!row.isEditable" v-on:click="editRow(index)"></button>
JS Methods:
//..
editRow: function (index) {
this.data[index].isEditable = true;
},
undoRow: function (index) {
this.data[index].isEditable = false;
}

v-model on a newly created property?

On created I:
created: function () {
_.forEach(this.users, (user) => {
Vue.set(user, 'published', true);
});
}
In my template I have:
<div v-for="user in this.users">
<input type="checkbox" v-model="user.published">
</div>
The issue is the checkbox does not seem to be bound to the newly set published property. Why is this and how can I fix it?
Please note, I am unable to change how I am adding the published property to each user.
Try it that way:
<div v-for="user in users">
<input type="checkbox" v-model="user.published">
<button #click="user.published=!user.published">Toggle</button>
</div>
In your hook:
created() {
for (i in this.users) {
this.users[i].published = true;
}
}

Vue component data property not updating after parent data changes

I have a vue component (card-motor) with a prop named motor:
<div v-for="chunk in chunkDataMotores" class="row">
<div v-for="motor in chunk" class="col-md-6">
<card-motor :motor="motor"></card-motor>
</div>
</div>
Whenever data (motor) changes on the parent, the changes on the data property (id_color, id_motor, nombre _motor, etc...) of the component does not get updated. Here the card-motor component:
<template>
<div class="card" :data-motor-id="id_motor">
<div class="card-header" :style="backgroundColor">
<h4 class="text-center">{{nombre_motor}}<button class="btn btn-dark btn-sm pull-right" :data-motor-id="id_motor" #click="show_modal_colores(id_motor)">Color motor</button></h4>
</div>
<div class="card-body">
<div class="card">
<div class="card-header" role="tab" id="headingOne">
<div class="mb-0">
<a data-toggle="collapse" :href="computedId">
Piezas asociadas {{nombre_motor}} <i class="fa fa-caret-down" aria-hidden="true"></i>
</a>
<button #click="addPieza(id_motor)" class="btn pull-right" title="AƱadir pieza nueva al motor"><i class="fa fa-plus text-info" aria-hidden="true"></i></button>
</div>
</div>
<div :id="id_motor" class="collapse" role="tabpanel" aria-labelledby="headingOne" data-parent="#accordion">
<div class="card-body">
<ul class="list-group">
<li class="list-group-item" v-for="pieza in piezas_motor">
<span class="badge badge-secondary">{{nombre_motor}}</span> {{pieza.pieza}}
<button class="btn btn-sm btn-danger pull-right"><i class="fa fa-trash" aria-hidden="true"></i></button>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['motor'],
data: function () {
return {
nombre_motor: this.motor.motor,
id_motor: this.motor.id,
id_color: this.motor.color.id,
piezas_motor: this.motor.piezas,
}
},
methods: {
show_modal_colores: function(id){
let $engine = $('#engine-colors');
$engine.data('motor-id', id);
$engine.find('div.color').removeClass('active');
$engine.find('div[data-id="'+this.activeColor+'"]').addClass('active');
$engine.modal('show');
},
addPieza(id) {
let $form = $('#form-pieza');
$form.data('motor-id', id);
$form.modal('show');
}
},
computed: {
computedId: function () {
return '#'+ this.id_motor;
},
backgroundColor: function () {
return 'background-color: '+ this.motor.color.codigo;
},
activeColor: function () {
return this.motor.color.id;
}
},
}
And here the parent code (root component):
Vue.component('card-motor', require('./components/CardMotor.vue'));
var app = new Vue ({
el: '#app',
data: {
dataMotores: [],
dataPuestos: [],
background_style: {
'background-color': ''
}
},
methods: {
makeActiveColor: function(e) {
$(e.currentTarget).closest('.modal-body').find('div.color').removeClass('active');
$(e.currentTarget).closest('div.color').addClass('active');
},
changeColor: function(e) {
let vm = this;
let id=$(e.currentTarget).closest('div.modal-content').find('.active').data('id');
let motor_id = $(e.currentTarget).closest('#engine-colors').data('motor-id');
axios.post('/admin/motores/change-color', {idmotor:motor_id, idcolor: id})
.then(response=>{
this.getData();
$('#engine-colors').modal('hide');
});
},
getData: function(){
axios.get('/admin/motores/api/data')
.then(response => {
this.dataMotores = response.data.motores;
this.dataPuestos = response.data.puestos;
})
.catch();
}
},
computed: {
chunkDataMotores() {
return _.chunk(this.dataMotores, 2);
}
},
created: function() {
this.getData();
}
});
Data returned from the axios call to the server are arrays of objects (getData method). Computed properties updates properly on the component, but not the data property.
You are making copies of your props, so the component renders, make your copies inside data(), but data() is called once, so when the parent component updates the child does not update.
data: function () {
return {
nombre_motor: this.motor.motor,
id_motor: this.motor.id,
id_color: this.motor.color.id,
piezas_motor: this.motor.piezas,
}
},
You can use motors prop directly, like:
<div class="card-header" :style="backgroundColor">
<h4 class="text-center">
{{ motor.motor }}
<button class="btn btn-dark btn-sm pull-right"
:data-motor-id="motor.id"
#click="show_modal_colores(motor.id)">
Color motor
</button>
</h4>
</div>
You need to pass value of dataMotores in components
<card-motor :motor="dataMotores"></card-motor>