Do I need to explicitly declare all component properties (vue.js)? - vue.js

I'm working on a component which is a kind of glorified input control, which will save a lot of the repetitive HTML of forms with Bootstrap grids (having to put a label next to each one, assign a 'for' attribute to it, assign a 'form-control' class to the input, etc.). The idea is that the component contains an input control with default values (such as the 'form-control' class) already set, label prepended, etc.
The HTML 'input' control as a whole bunch of optional attributes, some of which I may wish to use at some point - readonly, disabled, placeholder, etc. Do I actually have to declare all of these upfront, as it were, in my component's properties, if I may use one or more of them? This is a bit of a nuisance and rather cumbersome if true. It would be handy if all attributes which weren't explicitly declared were simply made available in the component's scope.
This is, I imagine, a common enough requirement that maybe there's another way of doing this?

You don't need to declare all of them, you can use v-bind to bind an object of optional attributes. So, in your parent you can do:
<custom-input :optional-attrs="{placeholder: 'Type something!'}"></custom-input>
Then in your component simply add it as a prop:
props: {
optionalAttrs: {}
}
And use v-bind on your input in your component:
<input type="text" class="form-control" v-bind="optionalAttrs">
Here's the JSFiddle: https://jsfiddle.net/rww551og/

You have to declare all props which CAN BE USED, but you have to add REQUIRE: TRUE only on those which are required. That's all.

Related

Vue, separate out component templates

I have got a rather large template at the moment for my component. I want to separate aspects of this into it's own component.
However i'm struggling to pass data to this component. I still want to be able to manipulate the data within the child and have the data in the parent update. So for example if I pass in an object, and then use v-model within the child on a textbox, the changes should reflex within the parent.
So, i'd assume as I loop through the list of objects I would v-model them into my child component, like so:
Main.vue
<card v-for="quote in quotes" v-model="quote"></card>
And then of course accept the input within the new model:
Card.vue
export default {
props: [ 'input' ]
}
However i'm getting the following error and I can't really make sense of it.
You are binding v-model directly to a v-for iteration alias. This will not be able to modify the v-for source array because writing to the alias is like modifying a function local variable. Consider using an array of objects and use v-mode
l on an object property instead.

Vue.js global data access with v-model

I am working on a web app where users can work on a project. The structure of the app is as follows:
Component A (app)
Component B1-Bn (header, footer, main window etc., children of A)
Component C1 (Input area; with inputs for the user to work on the project, child of main window)
Component C2 (Output area; canvas which shows the result based on inputs from C1. In the future also a "graphical" input area that syncs with C1. Child of main window)
Component D1-Dn (Single parts of the input area like tables, advanced input components etc. Child of C1)
Now the project that the user is working on consists of an object stored in Component A. Component Dn needs to write to the object in Component A and also C2 in the future.
I can't get the v-model on input components Dn to work. I tried to pass the data from A down to C1 via props / v-bind and then in Dn, I v-model the prop from C1 (which originates from A) to the input-field. I also tried to use the sync modifier without sucess.
I seem to have a lack of understanding of the vue logic. I come from a desktop background where you just define the scope of variables.
I also found that other vue apprentices have the same understanding problem but somehow the answers I found where not sufficient.
I want a "global" variable that can be edited by every component and is linked to elements in the DOM. What would be the best way to achieve this?
Declare your variable at data when creating Vue Object in your root component (Component A) like
var app = new Vue({
data: function(){
return {
showSetting: {}
}
},
})
Now you can access this showSetting variable in any component like
app.showSetting;
//change it in any component
app.showSetting = {a:1,b:2};
//or append new value to object
Object.assign({d:3},app.showSetting);
Thanks for the answers so far. I guess both of them work. I found another solution because now I fully understand how data is passed in vue:Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child component will affect parent state. I will pass all data as arrays in the future, as I only want references. The only question that remains is why the programmer is not allowed to define by himself whether the data is passed by reference or not...
Source: Vue.js Guide

Vue: Forcing child component to react to changes in its prop (which is a dictionary)

I am currently generating a table which lists problems encountered during the selected test using a component generated with this code:
<tr is="entry" v-for="problem in problems" :key="problem.id" v-bind:foo="problem"></tr>
Each problem corresponds to an item whose relevant information is contained within the problem dictionary and referenced in the first few columns of the table. Since the same item can have multiple problems, the same item can appear in multiple rows of the table. Now, each row features some buttons which allow you to modify the underlying item so as to fix the problems.
Whenever I modify one of those underlying items I need to modify it in all the rows, which i do by calling a function in the parent component, but modifying the data inside of the dictionary does not seem to trigger any of my watches or computes inside of the child component, which currently looks something like this:
Vue.component('entry', {
props: ['foo'],
data: function(){
//does some computations
return data
},
watch:{
foo: function(){
this.recompute_entry()
},
},
methods:{
//various methods, including:
recompute_entry: function(){
//updates the data according to changes brought to the entry
},
},
});
I have attempted to include a different prop which i could bind to an entry in a list in my parent component but, besides being pretty clunky, that didn't end up working either, which makes me think I might've gotten something wrong with my component.
Ultimately, I have relied on the fact that v-for iterates through my list in an orderly fashion, which combined with the fact that I generate no other children in my parent component means that a child component would have the same index in my component's children array as it would in my problems array. Therefore I can use this:
this.$children[problem_index].recompute_entry();
Which kind of feels hack-ish and unreliable, but actually works, for once. Is there no alternative safer method to recalculate my child components based on changes made to their props? I really feel there has to be.
I probably would need to see the exact implementation but it sounds like you need to clone your dictionary to trigger the prop change, ie:
let newProblem = Object.assign({}, this.problem);
// change any nested property
newProblem.some.value = 1
// assign back the cloned and modified dictionary
this.problem = newProblem

Dynamic Selection Method of QDataTable quasar-framework vue-js

I have a list of collapsible’s in a v-for, and a QDataTable component inside with multiple selection as below.
<q-collapsible :label="req.label" v-for="(req, index) in requisitions" :key="index" class="collapsible-no-padding requisitionContainer" #open="openRequisition('Requisition' + req.reqId)" #close="closeRequisition('Requisition' + req.reqId)" :id='"Requisition" + req.reqId'>
<q-list>
<q-item link class='ordersContainer'>
<q-item-main>
<q-data-table
:data="req.filteredOrdersList"
:config="orderConfigs"
:columns="orderColumns"
#selection="selectOrders">
</q-data-table>
</q-item-main>
</q-item>
</q-list>
</q-collapsible>
It looks like this
However, since there are more than one table, if a selection is made on any of the tables, it does call the selection method with the selected item, but I have no way to tell which table it is a part of. This is a problem when a selection is removed and you are returned an empty array, for which I need to distinguish between each tables selection event. Is there a way to do it?
You can pass the reference to the req object to the method in the #selection handler.
Since you also need the reference to the array of selected items currently being implicitly passed to the selectOrder method, you'll now need to explicitly pass that value. Normally, you could do that via the $event variable accessible inline.
However, contrary to the documentation, the <q-data-table> component's selection event is emitting two variables: the count of the elements in the array of selected items, and the array of selected items itself, in that order.
To get a reference to the array of selected items, you'll need to access the second argument being emitted via arguments[1]:
#selection="selectOrders(arguments[1], req)"

Computed property is called multiple times in Aurelia

I have a computed property which is dependent on A and B:
#computedFrom(A, B)
get property() {
}
The property is used in element which has repeat.for (5 elements in total):
<element repeat.for 1 to 5 elementProperty=$"{property}">
</element>
When value A changes, I would expect property() to be called just once, and all elements updated with the new value.
But property() is called 6 times.
Why?
Edit:
When I do this...
<template repeat.for 1 to 5>
<element elementProperty=$"{property}"></element>
</template>
...property() is called 6 times too.
Is this how it should work?
If you have a repeat.for, then the source property of any bindings in it is called for each child element that is rendered. This is normal behavior as they are all separate binding instances.
If your binding source changes, all binding targets (in this case 6) need to be updated and that happens by calling the source property. Computed bindings tend to be such simple computations that this is generally not an issue.
If your computed bindings are computationally expensive, you might want to consider using a property observer for the dependent properties and simply setting the computed property when any of them change, rather than letting the binding engine call it on binding.
EDIT
To clarify: this doesn't really have anything to do with computedFrom. A non-computed property would also be read 6 times but there is no getter to hook the debugger on, so you can't see that directly.