I know we use
() for detecting event like
<div (click)="doSomething()">
or
<div (blur)="doSomethingElse()">
and {{}} for converting variable to string in template like
<div>{{a_variable_i_want_to_show}}</div>
but what do we use [()] for besides two way binding in ng-model?
Is there a generic usage?
It is two-way binding. Checkout their cheatsheet:
Sets up two-way data binding. Equivalent to: <my-cmp [title]="name" (titleChange)="name=$event">
When used with NgModel, [()] sets up two-way databinding between a component property and a DOM form element. Any change we make to the component property is automatically propagated to the DOM, and any change we make to the form element (i.e., the DOM) is automatically propagated to the component property.
When used with a component, [()] sets up two-way databinding between a parent component property and a child component property. Any change we make to the parent component property is automatically propagated to the DOM. However, any change we make to the child component property is not automatically propagated to the parent – we must use emit(). So it's a little different than NgModel.
The child component must define an input property and an output property, which is an EventEmitter. If the input property is named x, the output property must be named xChange. The child component must explicitly emit any change to x by calling xChange.emit(newValue).
The reason for the naming requirements is because [(childProp)]="parentProp" is a shorthand for [childProp]="parentProp" (childPropChange)="parentProp=$event".
If you need to execute some logic in the parent when a new value is emitted from the child, you'll want to use the expanded form: [childProp]="parentProp" (childPropChange)="doSomething($event)".
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!
Based on my understanding of using v-model on a custom component (docs link below) the custom component should either:
Have a property on the data object of "value" since by default v-model is really 'v-bind:value="valuePassed"'
Have a "model" property on the component instance to specify which property to use.
Therefore, why does this fiddle work?
https://jsfiddle.net/mpctwfaq/2/
EDIT: This fiddle seems to work on Firefox but not Chrome at the moment...
parent:
<custom-select v-bind:options="formOptions" v-model="formOptionsVModel">
child:
this.$emit("input", this.customArraySelectedOptions)
Specifically, I emit the input event to the parent component with the name of a custom property "customArraySelectedOptions". However, I AM able to update the child component from the parent method by resetting the parent's "formOptionsVModel" property.
I would think based on the documentation the "downward" update should fail since the child's component property is not "value" and does not define a specific property using "model".
Docs: https://v2.vuejs.org/v2/guide/components-custom-events.html#Customizing-Component-v-model
v-model is syntax sugar for =>
:value="modelValue" #input="modelValue = $event.target.value"
And hence your variable is being updated.
Let's say I have this code
table(my-attr="value")
...complex component Jade...
and I would like render that my-attr base on property delivered into component. Since v-if works on whole element I cannot do something like
table(my-attr="value", v-if="myProp")
table(v-else)
because I would have to duplicate all the code inside table.
How can I achieve that?
You can use v-bind or interpolate the value directly with {{}}
// (sorry, no jade)
<table v-bind:attribute1="someMethod" attribute2="{{anotherMethod}}">
Now someMethod and anotherMethod should be data, computed properties, or methods of your component, and should return either the attribute's desired value or false. In the latter case, the attribute will not be added to the element at all.
Update: Note that interpolations in attributes have been removed in Vue 2
I am kind of confused here.
Using ngModel i can do the following:
import {NgModel} from "angular2/common";
#Component({
selector: "test",
template: `<input tpye="text" [(ngModel)]="strInObj.str"> {{strInObj|json}}`,
directives: [NgModel]
})
class Test{
strInObj: {str: string} = {str: "this is a string"};
}
Now when i type in the input, the strInObj.str gets updated. I am confused by this, because as far as i know strings are immutable, and there is no way of finding the parent of a reference.
In this case I pass the str property directly, meaning that ngModel gets a reference to the string. If it "changes" the string on that reference a new one is created therefore not changing the original string, which strInObj.str points to. At least that is my understanding.
And there is no way of finding the parent of the reference passed to ngModel (there is no concept like str.parent(), which would return strInObj)
So how do they do this? I tried to understand the ts and js sources, but...well, here i am.
I tried to build a similar directive, but ended up only beeing able to pass objects that wrap strings, i have not found a way to modify the original object when passing the str property directly.... (in the example i would pass strInObj to my directive, which then would work with the str property of the passed object, which works fine).
I'd be glad if someone could help me unriddle this :)
EDIT
In this plunker i have my custom directive StrDirective and an input field with a NgModel directive. Both have the same binding exampleStr which is outputted in a simple span.
Now, when i enter text in the input, you can see exampleStr being updated. This is expected behaviour. I know that this works.
The StrDirective updates its binding when clicked on. You can see that it updates its "working copy" of the string, but exampleStr is not getting updated.
My Question is now: How do they do it / How can i get my directive to update ExampleStr without having to wrap it in an Object?
In Javascript, all strings are immutable. When someone types in an input field, it updates the "working copy" of the string so that the working copy points to a new reference. Or put another way, every time the string changes, its a new string reference.
When a key is pressed that changes the model, ngModelChange output event is triggered, which then updates parent component's model with the new reference. The references are now in sync. When you say "modify the string that was passed to it", that's not possible because strings are immutable.
When there is two-way binding to a model:
[(ngModel)]="str"
The binding is equivalent to:
[ngModel]="str" (ngModelChange)="str=$event"
The #Output ngModelChange event is triggered whenever str (which the model is bound to) changes. In this way, a change in reference is propagated upwards to all components where two-way model binding is setup, so that each model points to the same reference.
[Edit]
In the Plnkr from the updated question, it shows that two-way binding is being restored after the user types a key in the input box. The question is how and why?
To understand what's happening let's look at the two scenarios:
User clicks the label and the event handler is triggered which changes the bound #Input value
User types a key in the input box. Both labels are automatically re-bound to the same reference, and two-way model binding works for both labels.
Before the user clicks the label, all bindings are in sync:
S1 (app component)
/\
(exampleStr binding) S1 S1 (str component)
After the click event, the bound #Input model changes. Then, a round of change detection occurs starting from the root and working its way to the child components in depth first order. Since the #Input binding propagates downwards, nothing really changes to the other bindings.
In the first scenario, this is the state of the bindings after the click event:
S1 (app component)
/\
(exampleStr binding) S1 S2 (str component)
When the user starts to type in the textbox which has two-way binding setup, it triggers an ngModelChange event which changes the value of exampleStr to S3.
S3 (app component)
/\
(exampleStr binding) S3 S2 (str component)
The default change detection strategy then kicks in, which starts from the root and works its way down to the child components in depth first order.
The state of the bindings after a key is pressed is:
S3 (app component)
/\
(exampleStr binding) S3 S3 (str component)
As you can see, all the bindings are in sync again. The default change detection strategy checks all components; changes to the model are propagated through the component's #Input bindings in a predictable and uni-directional flow from parent to child.
To understand how change detection works, think of it happening in phases. This is over-simplified, but it may help with your understanding:
Input bindings are propagated from root to children. Angular keeps track of which models are bound to which input properties. This is needed later for change detection.
An event fires (such as a click event) which modifies #Input properties. With two-way model binding, the change in model is propagated upwards from child to parent.
After the event fires, a single round of change detection is triggered starting from the root (repeat step 1) to re-sync all the bindings. During this process, all application and view bindings are updated.
Note: Angular uses zones to monkey-patch browser events, so it knows to when to trigger change detection.
[Edit]
If you want the message to be updated when the label is clicked, setup two-way binding like you would with ngModel:
#Input("str") value : string;
#Output("strChange") valueChange:EventEmitter<string> = new EventEmitter();
onClick(){
this.value = "new string";
this.valueChange.next(this.value);
}
HTML
<span [(str)]="exampleStr"></span><br>
Demo Plnkr
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.