The slot properties in Vue share data from children components (slots) to their parent, that's pretty clear to me.
However, sometimes a slot exposes a function within its slot properties. In example, VCombobox has a selection slot which exposes this:
{
parent: VueComponent,
item: object,
index: number,
select: function,
selected: boolean,
disabled: boolean
}
The select property is actually a function. To me, that kind of property does not make sense.
So, What are those functions trying to do? Why should I expose a function in a slot? (I tested to call it and it is expecting a parameter, an error is raised)
Well in this case it doesn't make sense to me either because the selection slot is rendering items which are already selected so sharing select function which receives native browser event (as click for example) and just make the item selected again does not make sense, but I believe there is an use case and reason for that...
But in general passing a function as a slot prop is very useful!
Let's say you are creating a "pagination" component. You component has a "prev" and "next" buttons. When clicked those buttons execute some function inside your component (the function maybe changes the page number, maybe send some event ....but this is irrelevant in this example). But you want give the user of your component the option to define how the "prev" and "next" buttons look like. So you create a slot. But at the same time you want to give a user of your slot a functionality which should be executed when "their" representation of the buttons is clicked - easiest way to do that is to pass the function into the scoped slot. User can replace the visuals but keep the original functionality...
Related
I'm trying to implement such algo using vueJS:
We have two input fields, for example, we translate from one language to another. These two fields belongs to the two components that are made for easier integration;
These two components are the same type and are in the one parent "host" component;
Whenever we type something inside one component, it immediately being caught by the parent component and then translates using some algo to value for another one component. Then parent should put that value to the second child's field;
Communication should be in two ways in both child components with their parent.
I'm done with this algo, but I think I'm doing wrong.. I'm receiving warning that I mutate prop which is a bad idea.
If you know the best way of doing such algo, PLS, advice it.
Here is my implementation:
Parent has two values: for the first input and for the second. They are bound to children' props using .sync modifier. Like this: :value.sync="firstValue" and :value.sync="secondValue";
Inside children we have prop named value(on which we sync our parent's data). Then we simply bind this prop using v-model: v-model="value";
And after all raise event for .sync updates: #keyup="$emit('update:value', value);";
On the parent bind some functions for events handling, that doesn't matter.
Again, algorithm performing well. But I think I'm doing bad with such things. I mutate prop value in each component using v-model, which isn't good. But .sync modifier won't work on value - I've checked, it only works on props, because v-bind only works on props as I can see...
So, I'm very glad to hear advises for improvement..
You could consider using a store like Vuex in this case.
After parent component did the transformation, dispatch action and mutate global store property.
In child 2 component, watch to the changes to this store property and then update it's input model
You can use v-model with components. Docs
That said, I would do something like this: Codepen
<field v-model="text"></field>
<field v-model="processedText"></field>
and then use watchers
watch: {
text(text) {
this.processedText = this.process(text);
},
processedText(text) {
this.text = this.process(text);
}
}
Of course there's probably more than one way to do it, but this seems like the shortest route :)
Thanks to all answers you guys provided. I've learned some new tricks from these advises. But I'd like to make a clarification and provide the best(for my opinion) choice!
For the start, my method:
In the child component: Note, that I'm using VueJs2, so the configuration of CHILD might be different to use v-model. For inputs inside children we set: #input="$emit('input', $event.target.value)" v-bind:value="value", where value is a defined prop;
In the parent component: We don't need to set watchers. The problem of this decision(which is provided in vch's answer) is that it might be the root of infinity recursion inside parent's component. So we could just listen for input events inside children components, like this: #input="someValueChanged">. And we could even have a two-way listener field inside parent, by just using v-model on our custom components: v-model="some value".
The pros of my methods are clear: we have universal component for ANY input type. We can make a custom listeners by listening directly to emitted events. And we can put a needed modifiers, like .lazy on those custom inputs. There are no disadvanatages tbh, or I haven't found them yet.
vch's answer:
Cool method, I've tried it, but it seems to be badly recursive inside parents component. Your codepen works, but in my case(VueJS2) it seems to be broken..
But I've started from your method and just removed useless watchers and added event listeners.
Pavan's answer:
Again, working method. We could really use storage for such case, but I see sort of disadvantages here:
We use not an out-of-box extension - VueX, which have to be installed. I'm fan of built-in methods tbh;
We can't provide separated logic for every input - we'll have to make custom component for each mechanism, cuz they have to listen to different storage variables.
Thx you all! It was very useful!
I have an Array of strings which I filter though an input. The value of this input is watched, and the new filtered Array is created upon changes in the input value. This works fine.
Independently of this I have a switch toggle which changes the way the filtering works, by modifying the function in the watch.
My problem: toggling this switch is not taken into account until the moment the input changes again. This is normal and expected: the watched value did not change.
Is there a way to :
either force the execution of a watch function (in which case I would also watch the toggle)
or use an OR construction in the watch ("if either the input or the toggle change, do this and that...")
What do right now is to have one extra method that does the filtering, that is called on the input or slider change. This works but I am wondering whether there is a better way to cleverly slim down the code.
One way would be to use computed property to return filtered Array. No need to use watch. Computed properties are just for that occasions 😉
If you want to use watch:
create a method that does the filtering.
Fire this method in a watcher
Fire it when toggle change.
Two working solutions, but computed would work better.
You definitely should use a computed property instead of watch in this case.
computed: {
computedArray() {
let result = [];
// your filtering code that include switch toggle
return result;
}
}
I have a list of items in a component:
list: Array<MyType>;
The user can select and deselect elements on click:
toggleItem(item: MyType) {
if (this.selection.has(item)) {
this.selection.delete(item);
return;
}
this.selection.add(item);
}
The selected items are stored in a Set:
selected: Set<MyType> = new Set();
Now I need to toggle a CSS class and a title-attribute depending whether an element is selected or not:
<button class="toggle"
type="button"
[ngClass]="{'selected': selection.has(item)}"
[title]="selection.has(item) ? 'Select' : 'Deselect'"
(click)="toggleItem(item)">
{{ item.title }}
</button>
Now I've read somewhere that it is a bad idea, to evaluate function calls as Angular periodically will call them for change detection, like here:
[ngClass]="{'selected': selection.has(item)}"
They say that it's better to check a variable or a member of the object instead, like:
[ngClass]="{'selected': item.selected}"
Is is true and does it decrease performance the way I use it currently? Should I add a property to each item that is set when it's added to or removed from the Set?
Whenever Angular performs change detection, it checks whether anything that's variable and is in the template has changed.
Now, the check is pretty straight forward in case of model variables as Angular can simply read values of them in order to check for a change.
But that's not really the case with a function. With functions, Angular can only determine if the variable has changed by calling the function itself.
Now it doesn't make much of a difference if the function is a one-liner return of a value.
But in case the function has complex logic, it will essentially kill all the performance. As every time the change detection cycle runs, Angular will call that function in order to check for changes and detect them.
Hence, it's not really recommended to write functions in data-binding syntax in templates.
NOTE: I've also written a Medium Article about this. I'd suggest you check it out. It's mainly about Angular Reactive Forms performance. But it will help you better understand what I said above.
I have an autocomplete component which has following props:
value (display text),
onChange (fired on every keystroke)
onSelect(fired when user picks something from the list of
suggestions, passes selected object + display text of selected object - the same as value)
state:
- suggestionsList (I've decided it's a state as it's something internal to the component).
On one of the views parent of this component doesn't care about the value after each keystroke. It just cares about the selected item passed by onSelect handler. So in this scenario value & onChange props are not needed. value could become an internal state of the autocomplete component.
But on one of the other views parent would like to also know about each keystrokes. This means the parent would need to hold the value in his state and also pass it as props. onChange props would be also needed to notify parent to change its state.
How to construct the autocomplete component so it handles both scenarios? Which properties should be props and which state?
Any suggestions appreciated.
In the second case, if the parent needs to know about the current value, it doesn't need to hold the variable, so I guess that if you pass the current value (from an internal state variable) as a parameter to the onChange prop function, the parent will notice the change and update accordingly.
Another way to do it is to use the value property and a internal state variable. What I mean is: if the parent wishes to control the value it will have an state entry for it and will be responsible for updating it, in other case the value property must not be provided because it would end as a constant. Your component will use this property as the current value every time its provided, and the parent should use onChange to update the va;ue that it holds.
You can check the source code of the auto complete component in material-ui.
I have a React component that includes an input element. When the value of the input changes the handler function is supposed to validate the input and act accordingly. The actual validator is dynamically created (via new Function(code)) with the actual code passed as a string prop to the element. The validation code never changes for a given component.
Right now I am doing the function construction in the actual onChange handler which seems unnecessary. It does not belong in state either. I would like to create the function once, store it somewhere and use it on demand. My question is, where should it be stored? Should I make it an attribute of the actual component object? The statics object seems also reasonable, but can one pass properties dynamically (like the code string above) and if yes, how?
Recomputing the validator inside the onchange is not that bad from a reproducibility point of view. It only affects performance and if that is an issue one possibility would be to use the caching mechanism of your choice:
handleOnChange: function(){
if(this.cachedValidatorString !== this.props.validatorString){
this.cachedValidatorString = this.props.validatorString;
this.cachedValidator = new Function(...);
}
// ...
}
Another, perhaps cleaner, approach would be to update the validatorFunction field inside the render method. That is the earliest you can update it and it guarantees that it will always correspond to your current props.
render: function(){
this.validatorFunction = new Function(this.props.validatorString);
return <input onChange={...} />;
}
As for the other possibilities you mentioned:
I agree that this.state is not the best place to put the validator function because in general you want to avoid putting things that can be computed from the props in the state. See props in getInitialState as an anti pattern
Finally, I don't think statics would make much sense here. Static properties are shared by all instances of your class but the validator functions need to be different for each instance of the component.