Angular2 ngModel: Why can it change an immutable string? - javascript

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

Related

VueJS sync two input fields

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!

Where to store the state to make React components more reusable

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.

Angular 2 - pass an object field by reference. Reusable way to edit objects

I am creating reusable table component which will enable editing of objects fields to prepare them for sending to the API.
Having an object:
person: {
name: "John"
job: {
type: "IT"
title: "Software Engineer"
}
}
I would like to pass the object nested field to a component and edit. F.e:
<edit-field [field]="person.job.title"></edit-field>
Which results in an input field that edits exactly the title field in original object. The problem is that person.job.title is a string, not and object or array so it's not passed by reference.
I had 2 ideas haw the problem could be solved:
1) pass "person.job.title" as a string:
<edit-field [field]="'person.job.title'"></edit-field>
or
<edit-field field="person.job.title"></edit-field>
And in component class do split by a ".":
let fields = this.field.split('.');
and then do a while loop to access the field by reference.
We could also do 2 inputs:
<edit-field-component [fieldRef]="person.job" [field]="'title'"></edit-field-component>
and then inside component do this.fieldRef[this.field]
I am wondering if there is any other, more clean, way to achieve that.
Basically, you want to accomplish two-way binding - i.e. changes to the object value: eg person.job.title updates your new edit component, but also changes made from your edit component also get reflected back to the object value.
In Angular, that means you have to bind both ways:
<edit-field [field]="person.job.title" (change)="person.job.title=$event"></edit-field>
where your edit-field component has an #Output property that emits the changed value whenever someone types into it. The value emitted from the #Output property will be in the variable $event and you simply want to assign that back to the property that you want to update.
So, your EditFieldComponent can look something like:
#Component({
.....
template: `
<input (input)="change.emit($event.target.value)" .... />
`
})
export class EditFieldComponent {
change = new EventEmitter();
}
The above means that whenever an input event triggers on your input field, the component's change output property will emit the new value of the input field.
===========
If you understand everything above, then Angular provides a little shortcut for this exact scenario. If the output property of your component is named a specific way, you can simplify how you write the two way binding.
So, if your input property is field, and you name your output property fieldChange you can make use of some fancy syntax to cut down how much you have to type.
i.e.
<edit-field [field]="person.job.title" (fieldChange)="person.job.title=$event"></edit-field>
is equivalent to:
<edit-field [(field)]="person.job.title"></edit-field>
[field]="person.job.title" is one-way binding (changes of person.job.title are propagated to field but not the other way round)
[(field)]="person.job.title" would achieve two-way binding (changes made by fieldChange method are also propagated back to person.job.title)
If you want to reffer your object or property of your object to your component, you need to create an #Output property with type eventEmitter.
#Input('field') field: any;
#Output() fieldChange = new EventEmitter();
Be carefull to name your output property with "Change" suffix word. It will detect automatically change from your object and notify to your main object.
Javascript just like Java is passed by value, they have never offered passed by reference. So in your case, your best option is to pass your person object directly. Even though it will be copied inside your function, the copy still refers to the same object, so changing a field in the copy will also change the corresponding field in the original.

Updating state of the app without touching the DOM - Vue.js

I'm going through the Vue.js guide and I came across this paragraph. It says:
Note in the method we simply update the state of our app without touching the DOM
Basically the method simply reverses a string on click of a button. What I'm unable to understand is the concept of Updating the state of app without touching the DOM. What does the state mean here? Can somebody explain this in layman terms?
One of the cornerstones of Vue.js is its simply implemented two-way data binding. This means that when a state or data value is changed within the Vue object/instance, it is also synced and changed in the DOM and vice versa without having to manually update both.
In a pure Javascript situation such as:
function changeData() {
document.getElementById('data').innerHTML = "Second";
}
<div>
<span id="data">First</span>
</div>
<button onclick="changeData()">Change Value</button>
In this, we are directly manipulating the DOM to change the text value of the span element, however, with Vue's two-way data binding, only the instance's state/data must be changed for both to update.
If I really simplify it, then I would say that it's the data that's used for your application. Which includes, for example, your reversed string.
Or for example:
State: { isSwitchedOn: false }
UpdateStateFunc: (state, value) => state.isSwitchedOn = value;
So, we can update isSwitchedOn using UpdateStateFunc, but that doesn't mean we push that data to the DOM (ie. what's visible to the user). Making it visible to the user would be, perhaps, another function.

Best practice for ReactJS form components

I am looking for a best practice to have a ReactJS component responsible for the form for users to edit a given entity. Very simplified example here. Actual forms would in many cases have several more fields and more GUI functionality.
React.createClass({
getInitialState: function() {
return {
entity: {
property1: null,
property2: null
}
};
},
handleChange: function(e) {
var entity = this.state.entity;
switch(e.target.name) {
case 'property1':
entity.property1 = e.target.value;
break;
case 'property2':
entity.property2 = e.target.value;
break;
}
this.setState({
entity: entity
});
},
render: function() {
return (
<div className="entity-form">
<form onChange={this.handleChange}>
<input type="text" name="property1" value={this.state.entity.property1} />
<br />
<textarea name="property2" value={this.state.entity.property2}></textarea>
<br />
</form>
</div>
);
}
});
The fields of the form is directly editing an entity object, that could then be saved to a RESTful api. I want the component to be updated as the user change the fields, so the GUI could react based on the input during typing (like validations, info etc).
In theory, I could have the whole state object represent the entity that is being edited, so every property of the entity is first level state variables. However, I want to be able to add additional state variables for GUI functions and other things related to what the component is going to do, so I would prefer the entity object to be one state variable like the "entity" state variable above. The object could of course be some more complicated object, like a Backbone model or similar, but in this simplified example, I just use a simple object whit the required properties.
So, in search of the best practice way to make React components for this purpose, I have some questions:
Props or state.
In this case, I have chosen to put the entity object with the content for the form in a state variable instead of prop. This is to be able to update the object during form input without having to call the parent and update the props. As far as my React experience goes, that would be the best practice for a form component like this.
Controlled or uncontrolled inputs.
In the simplified example above, I use controlled inputs. This leads to updating the state and re-rendering the component on every change (like every character entered of a text field). Is this the best practice? The good thing is that the component has full control of what happens, instead of having defaultValue paramters, and on some event (like the user pressing a save button), the component extract the values, update the entity and save it to the server. Is there any reasons (or opinions) on if controlled or uncontrolled inputs should be used in cases like this?
onChange for the form or every input
The example has an onChange on the form tag, and it causes the handleChange method to be called every time any of the fields in the form is changed. However, since the inputs are controlled (have value parameters), React complains that the input fields does not have an onChange property. Does this mean having a common onChange on the form tag is bad practice, and I should remove it and put onChange on every single field instead?
Updating individual properties
In the above example, I use a switch based on what input field is being update (when handleChange is called). I guess I could instead make sure all field names is in sync with the property names of the entity, and I can set properties of the entity object in handleChange based on the name of the field from the event (e.target.name). However, this makes it hard to have individual needs per field, even if most fields just update an entity property directly. I guess an alternativ is a switch with a default block setting based on the name of the input, and case blocks for any field that require other ways of updating (like filtering the value before setting it on the entity). Please comment this if you know some much better way of handeling field updates this way.
Updating the state entity
One big problem of this example, is the way the entity object is updated. Since the entity variable in the handleChange is set to the entity object from current state, this is just a pointer, and updating the entity variable will change the object in state. The React pages say you should never update state directly. One of the reasons is something I have experienced when updating the state this way before calling setState. If having a shouldComponentUpdate method, the prevState contain the new state, since the content of the prevState argument sent to the shouldComponentUpdate is based on what was in the state when setState was called. As far as I know, there is no simple way to clone a object in javascript. So the question is, when having whole objects that I need to update properties of (and not touching the other values in the object) instead of just running setState of a single state variable, what is the best way to do this without causing theese kinds of state mixups?
Anything that is going to change goes in State.
If you're looking at loading an existing entity and editing it, you want controlled inputs, and you want to set the values accordingly. I tend to stay away from defaultValue in most cases (outside of dropdowns)
This ties back in to your previous question. If you specify a value, you are using a controlled input, and you have to provide an onChange handler for any controlled input, otherwise it is set in stone. A benefit of controlled inputs is that users can't edit them, so if you had some properties locked down (maybe for read only, security reasons), when the user attempts to save, even if they edited the HTML directly, React should pull the property's value from the vDOM representation (could be wrong here, but I believe I've tested this before). Anyway, you have to have onChange set directly on controlled inputs. As far as using event delgation (at the form level), this isn't a bad practice at all for a lot of events, this is just a specific scenario (controlled inputs) where you need onChange events specified for each element.
Not entirely sure what the ask on this one is, but I used refs instead of target.name.
So, you're correct in that you should never alter the state directly, and this is a tricky bit from the docs. React is going to alter state directly, it's just going to do it in the implementation through setState. If you alter state outside of this method call, unexpected things will happen and errors will be thrown.
shouldComponentUpdate only does shallow comparisons, but there are a few solutions here.
One is to stringify the objects, this is a quick and dirty object comparison, don't really recommend it, but it works.
A better solution, and one I have used with React + Flux is to implement a propertyChanged bool, and just check that in your shouldComponentUpdate.
Now, this will require you to be aware of setting it when things change, i.e., you changed something deeper in the object graph. Say propertyOne is an object with a property that gets changed in your handleChange method. You would validate the input however you wish, then set propertyChanged = true, and you then need to implement componentDidUpdate. We're making an assumption here, but if the component has updated, you set propertyChanged back to false so you don't have any further triggering of unwanted updates. I hope that makes sense. It's kinda like a one-way notifyPropertyChanged.
I'm providing a quick example of what I would probably do for a more dynamic implementation that allows you to add more properties to your object (only shallow properties in this implementation, obviously you could write a more robust solution). Let me know if you have any further questions or if I didn't answer something.
http://jsfiddle.net/rpv9trhh/
var e = {
prop1: 'test',
prop2: 'wee',
prop3: 'another property',
propFour: 'Oh yeah!'
};
var FormComp = React.createClass({
getInitialState: function(){
return {
entity: this.props.entity
}
},
render: function() {
var ent = this.state.entity;
var that = this;
var inputs = [];
for(var key in ent){
inputs.push(<input
key={key}
style={{display:'block'}}
type="text"
ref={key} onChange={that._propertyChanged.bind(null, key)}
value={ent[key]} />)
}
return <form>
{inputs}
<input
type="button"
onClick={this._saveChanges}
value="Save Changes" />
</form>;
},
_propertyChanged: function(propName) {
var nextProp = this.refs[propName].getDOMNode().value;
var nextEntity = this.state.entity;
nextEntity[propName] = nextProp;
this.setState({
entity: nextEntity
});
},
_saveChanges: function() {
var updatedEntity = this.state.entity;
for(var key in updatedEntity){
alert(updatedEntity[key]);
}
//TODO: Call to service to save the entity, i.e.
ActionCreators.saveEntity(updatedEntity);
}
});
React.renderComponent(<FormComp entity={e} />, document.body);

Categories