Nova这个项目的初衷是想让你的定制化变得足够简单。事实上,你不仅可以扩展Nova默认的集合Posts和Comments,你也可以轻松的创建你自己的集合。
在这个视频中,我会教你如何创建一个Movies集合,创建一个分页列表来显示数据,同时还有表单来插入和编辑条目,所有的这些都几乎不需要写后端代码!
Nova Features
以下是我们将基于Nova实现的特性:
- 发布:自动发布所需数据
- 订阅:创建指定发布的订阅
- 分页:只发送必要的数据到客户端
- 连接:在发布和显示的时候连接数据
- 方法:创建三个create/edit/delete方法
- 表单:创建表单来插入或修改条目
视频地址:http://v.qq.com/page/m/9/v/m01869uzt9v.html
代码
以下是nova-app.jsx
的代码:
import {mount} from 'react-mounter';
//////////////////////////////////////////////////////
// Collection & Schema //
//////////////////////////////////////////////////////
Movies = new Mongo.Collection("movies");
const isLoggedIn = user => !!user;
const isOwner = (user, document) => user._id === document.userId;
const schema = new SimpleSchema({
name: {
type: String,
publish: true,
control: "text",
insertableIf: isLoggedIn,
editableIf: isOwner
},
createdAt: {
type: Date,
publish: true,
},
year: {
type: String,
publish: true,
optional: true,
control: "text",
insertableIf: isLoggedIn,
editableIf: isOwner
},
review: {
type: String,
publish: true,
control: "textarea",
insertableIf: isLoggedIn,
editableIf: isOwner
},
userId: {
type: String,
publish: true,
join: {
collection: () => Meteor.users,
joinAs: "user",
fields: ["_id", "username"]
}
}
});
Movies.attachSchema(schema);
//////////////////////////////////////////////////////
// Route //
//////////////////////////////////////////////////////
FlowRouter.route('/demo', {
name: 'demo',
action() {
mount(MoviesWrapper);
}
});
//////////////////////////////////////////////////////
// Methods //
//////////////////////////////////////////////////////
Movies.smartMethods({
createCallback: function (user, document) {
document = _.extend(document, {
createdAt: new Date(),
userId: Meteor.userId()
});
return document;
},
deleteCallback: isOwner
});
//////////////////////////////////////////////////////
// Publications //
//////////////////////////////////////////////////////
if (Meteor.isServer) {
Movies.smartPublish("movies.list");
}
以下是nova-component.jsx
的代码,也就是视频中的React部分:
import NoSSR from 'react-no-ssr';
import Core from 'meteor/nova:core';
import SmartContainers from "meteor/utilities:react-list-container";
import FormContainers from "meteor/utilities:react-form-containers";
FlashContainer = Core.FlashContainer;
ModalButton = Core.ModalButton;
NewDocContainer = FormContainers.NewDocContainer;
EditDocContainer = FormContainers.EditDocContainer;
ListContainer = SmartContainers.ListContainer;
//////////////////////////////////////////////////////
// MoviesWrapper //
//////////////////////////////////////////////////////
MoviesWrapper = React.createClass({
render() {
return (
<div className="wrapper">
<NoSSR onSSR={<p>Loading…</p>}>
<LogInButtons />
</NoSSR>
<FlashContainer />
<div className="main">
<ListContainer
collection={Movies}
publication="movies.list"
terms={{options: {sort: {createdAt: -1}}}}
options={{sort: {createdAt: -1}}}
joins={Movies.getJoins()}
>
<MoviesList/>
</ListContainer>
</div>
</div>
)
}
});
//////////////////////////////////////////////////////
// MoviesList //
//////////////////////////////////////////////////////
MoviesList = React.createClass({
renderNew() {
const component = (
<ModalButton label="Add Movie" className="button button--primary">
<NewDocContainer collection={Movies} label="Add Movie" methodName="movies.create"/>
</ModalButton>
)
return !!this.props.currentUser ? component : "";
},
render() {
return (
<div className="movies">
{this.renderNew()}
{this.props.results.map(movie => <Movie key={movie._id} {...movie} currentUser={this.props.currentUser}/>)}
{this.props.hasMore ? (this.props.ready ? <LoadMore {...this.props}/> : <p>Loading…</p>) : <p>No more movies</p>}
</div>
)
}
});
//////////////////////////////////////////////////////
// Movie //
//////////////////////////////////////////////////////
Movie = React.createClass({
renderEdit() {
const movie = this.props;
const component = (
<ModalButton label="Edit" className="button button--secondary">
<EditDocContainer collection={Movies} document={movie} label="Edit Movie" methodName="movies.edit"/>
</ModalButton>
);
return (
<div className="item-actions">
{this.props.currentUser && this.props.currentUser._id === movie.userId ? component : ""}
</div>
)
},
render() {
const movie = this.props;
return (
<div key={movie.name} style={{marginBottom: "15px"}}>
<h2>{movie.name} ({movie.year})</h2>
<p>{movie.review} – by <strong>{movie.user && movie.user.username}</strong></p>
{this.renderEdit()}
</div>
)
}
});
const LoadMore = props => <a href="#" className="load-more button button--primary" onClick={props.loadMore}>Load More ({props.count}/{props.totalCount})</a>
资源
1. Nova
你可以clone Telescope上Nova这个分支,视频里的文件是demo-app.jsx
和demo-component.jsx
git clone -b nova https://github.com/TelescopeJS/Telescope.git
2. 扩展包
- React List Container: 用来订阅一个发布,然后向子组件传入记录
- React Form Containers: 用来显示一个简单的新建和编辑记录表单
- Smart Publications: 用来自动创建发布
- Smart Methods: 用来自动创建方法
参考
原文地址:http://www.telescopeapp.org/blog/building-apps-with-nova-react-meteor/