Quickstart
A quick dive into getting started with Lore
A quick dive into getting started with Lore
In this step we're going to add an "edit" link to tweets that, when clicked, will launch a dialog to edit the text.
You can view the finished code for this step by checking out the
dialogs.6
branch of the completed project.
The dialog we'll create in this step will allow us to edit a tweet, and we'll be invoking the update
action when it's time to send our changes to the API. Assuming you have a tweet
you want to change, you invoke the action like this:
lore.actions.tweet.update(tweet, {
text: 'Different text'
})
The first argument is the tweet
you want to update, and the second argument is the set of attributes you want to change. In this example, we're changing the text
of the tweet from whatever it current is to "Different text".
If we assume the id
of this tweet is 1
, then invoking this action will send a PUT
request to http://localhost:1337/tweets/1
and include our new properties in the body of the request.
You can learn more about the
update
action here.
Run this command to create a component for our edit link:
lore generate component EditLink
Then update the component to look like this:
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
export default createReactClass({
displayName: 'EditLink',
propTypes: {
tweet: PropTypes.object.isRequired
},
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.update(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.update(tweet, data).payload;
}
});
});
},
render() {
return (
<a className="link" onClick={this.onClick}>
edit
</a>
);
}
});
import React from 'react';
import PropTypes from 'prop-types';
class EditLink extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.update(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.update(tweet, data).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
edit
</a>
);
}
}
EditLink.propTypes = {
tweet: PropTypes.object.isRequired
};
export default EditLink;
import React from 'react';
import PropTypes from 'prop-types';
class EditLink extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired
};
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.update(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.update(tweet, data).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
edit
</a>
);
}
}
export default EditLink;
In the code above, we're rendering a link with an onClick
callback. When clicked, we'll show the dialog, and then invoke the update
action when the data is submitted.
Unlike the create dialog, the update dialog requires you to provide the tweet
you want to update as the first argument. This is required in order to populate the dialog with the current data.
Next we want to add the edit link to each tweet. Open the Tweet
component and update the render()
method to look like this:
// src/components/Tweet.js
...
import EditLink from './EditLink';
...
render() {
...
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<div className="tweet-actions">
<EditLink tweet={tweet} />
</div>
</div>
</li>
);
}
...
With this change in place, refresh the browser and you should see an "edit" link on each of the tweets.
If you click the "edit" link, you'll notice the dialog that opens says "No fields have been provided". Similar to the create dialog, we need to describe the fields that should be displayed.
To fix this, open the tweet
model and update the code to look like this:
// src/models/tweet.js
const fields = {
data: {
text: ''
},
validators: {
text: [function(value) {
if (!value) {
return 'This field is required';
}
}]
},
fields: [
{
key: 'text',
type: 'text',
props: {
label: 'Message',
placeholder: "What's happening?"
}
}
]
};
export default {
dialogs: {
create: fields,
update: fields
}
}
In the code above, we're extracting the "create" fields into a variable called fields
that we can reuse, and then using it to describe the "update" fields as well.
With this change in place, refresh the browser and click one of the "edit" links.
A dialog will appear allowing you to change the text. Once you submit the form, if you look at the network requests, you'll see a PUT request is sent to the API to update the tweet.
The
state
of the tweet is also changed toUPDATING
, so if this were a real application, we could add an if statement to detect when data was being changed and modify our UI to communicate that to the user.
If everything went well, your application should now look like this.
Below is a list of files modified during this step.
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
export default createReactClass({
displayName: 'EditLink',
propTypes: {
tweet: PropTypes.object.isRequired
},
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.update(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.update(tweet, data).payload;
}
});
});
},
render() {
return (
<a className="link" onClick={this.onClick}>
edit
</a>
);
}
});
import React from 'react';
import PropTypes from 'prop-types';
class EditLink extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.update(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.update(tweet, data).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
edit
</a>
);
}
}
EditLink.propTypes = {
tweet: PropTypes.object.isRequired
};
export default EditLink;
import React from 'react';
import PropTypes from 'prop-types';
class EditLink extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired
};
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.update(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.update(tweet, data).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
edit
</a>
);
}
}
export default EditLink;
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import EditLink from './EditLink';
export default connect(function(getState, props) {
const { tweet } = props;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})(
createReactClass({
displayName: 'Tweet',
propTypes: {
tweet: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
},
render() {
const { tweet, user } = this.props;
const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<div className="tweet-actions">
<EditLink tweet={tweet} />
</div>
</div>
</li>
);
}
})
);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import EditLink from './EditLink';
class Tweet extends React.Component {
render() {
const { tweet, user } = this.props;
const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<div className="tweet-actions">
<EditLink tweet={tweet} />
</div>
</div>
</li>
);
}
}
Tweet.propTypes = {
tweet: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
};
export default connect(function(getState, props) {
const tweet = props.tweet;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})(Tweet);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import EditLink from './EditLink';
@connect(function(getState, props) {
const tweet = props.tweet;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})
class Tweet extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
};
render() {
const { tweet, user } = this.props;
const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<div className="tweet-actions">
<EditLink tweet={tweet} />
</div>
</div>
</li>
);
}
}
export default Tweet;
const fields = {
data: {
text: ''
},
validators: {
text: [function(value) {
if (!value) {
return 'This field is required';
}
}]
},
fields: [
{
key: 'text',
type: 'text',
props: {
label: 'Message',
placeholder: "What's happening?"
}
}
]
};
export default {
dialogs: {
create: fields,
update: fields
}
}
Next we're going to create a way to delete tweets.