We've successfully created a working Application. When we run it, data is presented to the browser as intended. But what happens if we want to change the data? Currently, if we change the model, nothing. Lucky for you, Cascade makes this simple.
Observable Properties
Cascade provides Observable properties for objects. Once established, these special properties may be subscribed to. Then, if the value of the property changes, the subscribers will be notified with the updated value. These special properties can be read and written to exactly like regular properties.
We can use the TypeScript decorator @observable
in the class definition to make a property observable.
@observable property: type = value;
If we don't want to use the decorator, we can attach an observable property to an object with the call:
Cascade.createObservable<T>(obj: any, property: string, value?: T);
Let's take our User
example from the last chapter, and make it observable. So, simply import the @observable
decorator, and add it in front of any properties you want to observe.
import { observable } from 'cascade';
export default class User {
@observable firstName: string;
@observable lastName: string;
}
And there we have it! Our firstName
and lastName
properties are now Observables!
Handling Input
Now that we can watch for changes in our data, let's set up some inputs!
export default class UserView extends Component<IUserViewProps> {
render() {
let {user} = this.props;
return (
<div>
<p>First name: {user.firstName}</p>
<p>Last name: {user.lastName}</p>
<p>First name: <input type="text" value={user.firstName} /></p>
<p>Last name: <input type="text" value={user.lastName} /></p>
</div>
);
}
}
There are a couple of things to note:
- We have included two
<input>
tags. - We inject the value using
{}
notation.
When you run it, you will see your data now displayed in two text fields. But what happens when you type in the inputs? Currently, still nothing.
Cascade works with one way data binding, meaning that changes to data flow from Application State to Components not the other way around. This is to prevent circular references, where an update moves from A to B to C and so on, but somehow goes back to A. If that happens, we will end up in an infinite loop.
So, any changes to our User
will show up in our UserView
, but changes to our UserView
don't automatically go back to our User
. In order for that to happen, we need to handle Events
.
An Event
is triggered when something happens, like when a user clicks the mouse, or presses a key. It can even happen when an AJAX call completes. Normally, a program will execute its instructions until there is nothing left to do, and it will either end, or wait for input. So, we need to handle that input.
For our UserView
add these two methods above the render
method.
updateFirstName = (event) => {
this.props.user.firstName = event.target.value;
}
updateLastName = (event) => {
this.props.user.lastName = event.target.value;
}
There are a couple of things to note:
- These methods will handle the
Event
, and store the value of the target into ourUser
. - We are using the Arrow Function notation, as the
this
value may be changed while executing.
But how do we hook these handlers up to our inputs?
<p>First name: <input type="text" value={user.firstName} oninput={this.updateFirstName} /></p>
<p>Last name: <input type="text" value={user.lastName} oninput={this.updateLastName} /></p>
And that's it! Any time the user updates the text inputs, the Event
is triggered, and the User
is updated.
We could also use regular methods instead of Arrow Functions for updateFirstName
and updateLastName
. In that case, when we inject them, we must use Function.bind()
.
<p>First name: <input type="text" value={user.firstName} oninput={this.updateFirstName.bind(this)} /></p>
<p>Last name: <input type="text" value={user.lastName} oninput={this.updateLastName.bind(this)} /></p>
In many cases, this syntax is harder to read, but it is personal preference. In some cases, where you must inject a specific value into the method, Function.bind(this, value)
is useful.
Running our Application
So, we can now display and update our User
. Try running it and see what happens!