mirror of
https://github.com/vale981/Vulcan
synced 2025-03-06 10:01:40 -05:00
Merge pull request #1820 from b-d-m-p/master
Added comments from the example-simple video tutorial for reference f…
This commit is contained in:
commit
db8c148d89
10 changed files with 168 additions and 56 deletions
|
@ -14,6 +14,9 @@ accounts-password@1.4.0
|
|||
|
||||
############ Your Packages ############
|
||||
|
||||
#tutorial-step-1 - You can follow along with the video here: http://docs.vulcanjs.org/example-simple.html
|
||||
# This is where you enable packages.
|
||||
|
||||
example-simple
|
||||
# example-movies
|
||||
# example-instagram
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
// client/main.js #tutorial-step-3 - This is the file that is called into package.js.
|
||||
import '../modules/index.js';
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/*
|
||||
|
||||
List of movies.
|
||||
components/MoviesList.jsx #tutorial-step-7 -
|
||||
The component for our list of movies, which we called in to modules/routes.js.
|
||||
|
||||
Wrapped with the "withList" and "withCurrentUser" containers.
|
||||
|
||||
#tutorial-step-11 -
|
||||
Now, we are going to look at this in a bit more detail.
|
||||
This component is a React component. We only have one but it does a bunch of things...
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
@ -11,57 +16,109 @@ import { Components, withList, withCurrentUser, registerComponent } from 'meteor
|
|||
|
||||
import Movies from '../../modules/movies/collection.js';
|
||||
|
||||
const MoviesList = ({results = [], currentUser, loading, loadMore, count, totalCount}) =>
|
||||
|
||||
<div style={{maxWidth: '500px', margin: '20px auto'}}>
|
||||
|
||||
{
|
||||
/* These are "props". They are variables for the component that are passed by the components parent.
|
||||
In this case, to create the parent we wrapped the component in "Higer Order Compoents" (See the Higer Order Compoents section below.)
|
||||
By doing this, we can pass on those props to the children of he HOCs and give them access to the props... */
|
||||
}
|
||||
const MoviesList = ({
|
||||
results = [],
|
||||
currentUser,
|
||||
loading,
|
||||
loadMore,
|
||||
count,
|
||||
totalCount
|
||||
}) => (
|
||||
<div style={{ maxWidth: "500px", margin: "20px auto" }}>
|
||||
{/* First, this is a Helment tag. It's a React package that loads head tags. We're using it to load the Bootstrap stylesheet.
|
||||
This is not Vulcan specific but it is an easy way to add tags to the head... */}
|
||||
<Helmet>
|
||||
<link name="bootstrap" rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"/>
|
||||
<link
|
||||
name="bootstrap"
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
|
||||
/>
|
||||
</Helmet>
|
||||
|
||||
{/* user accounts */}
|
||||
|
||||
<div style={{padding: '20px 0', marginBottom: '20px', borderBottom: '1px solid #ccc'}}>
|
||||
|
||||
<div
|
||||
style={{
|
||||
padding: "20px 0",
|
||||
marginBottom: "20px",
|
||||
borderBottom: "1px solid #ccc"
|
||||
}}
|
||||
>
|
||||
{/* ...This is the log in form component. It allowed you to create an account in our web app.
|
||||
It takes care of all accounts stuff like changing passwords, signing in and out, and so on
|
||||
Just pop this in anywhere you want to use it. It's in the Vulcan accounts package... */}
|
||||
<Components.AccountsLoginForm />
|
||||
|
||||
</div>
|
||||
|
||||
{loading ?
|
||||
|
||||
<Components.Loading /> :
|
||||
|
||||
{/* ... We have a test for the loding variable (See the "Higher Order Components" section at the bottom and then the "props" section at the top.)... */}
|
||||
{loading ? (
|
||||
<Components.Loading />
|
||||
) : (
|
||||
<div className="movies">
|
||||
|
||||
{/* new document form */}
|
||||
{/* new document form - this if for inserting new documents. Because the collection has the schema, when we generate the form, it know what the colleciton should look like
|
||||
You only need to specify the colleciton. You don't need to code any of the form. Validation will work and it will show you fields based on your user permission...*/}
|
||||
|
||||
{Movies.options.mutations.new.check(currentUser) ?
|
||||
<div style={{marginBottom: '20px', paddingBottom: '20px', borderBottom: '1px solid #ccc'}}>
|
||||
{Movies.options.mutations.new.check(currentUser) ? (
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "20px",
|
||||
paddingBottom: "20px",
|
||||
borderBottom: "1px solid #ccc"
|
||||
}}
|
||||
>
|
||||
<h4>Insert New Document</h4>
|
||||
<Components.SmartForm collection={Movies} />
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
<Components.SmartForm collection={Movies} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* documents list */}
|
||||
{/* documents list - this is another small utility in Vulcan and it will display it as a card... */}
|
||||
|
||||
{results.map(movie => <Components.Card fields={['name', 'year', 'review']} key={movie._id} collection={Movies} document={movie} currentUser={currentUser} />)}
|
||||
|
||||
{/* load more */}
|
||||
{results.map(movie => (
|
||||
<Components.Card
|
||||
fields={["name", "year", "review"]}
|
||||
key={movie._id}
|
||||
collection={Movies}
|
||||
document={movie}
|
||||
currentUser={currentUser}
|
||||
/>
|
||||
))}
|
||||
|
||||
{totalCount > results.length ?
|
||||
<a href="#" onClick={e => {e.preventDefault(); loadMore();}}>Load More ({count}/{totalCount})</a> :
|
||||
{/* load more - this is the load more button. On click we trigger the loadMore function which is passed as a prop by withList... */}
|
||||
|
||||
{totalCount > results.length ? (
|
||||
<a
|
||||
href="#"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
loadMore();
|
||||
}}
|
||||
>
|
||||
Load More ({count}/{totalCount})
|
||||
</a>
|
||||
) : (
|
||||
<p>No more items.</p>
|
||||
}
|
||||
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
// ...this is where we specify how to load the data in the options object that we pass to withList
|
||||
// if you want, you can specify many more options here, like how often to look for data or what fields to query from, filtering and sorting options. ...
|
||||
const options = {
|
||||
collection: Movies,
|
||||
limit: 5,
|
||||
};
|
||||
|
||||
// These two functions (withList & withCurrentUser) are Higher Order Components (HOC) and by wrapping our component with this we can give it "props". (See the "props" section at the top.)
|
||||
registerComponent('MoviesList', MoviesList, withCurrentUser, [withList, options]);
|
||||
|
||||
|
||||
// #tutorial-step-12 - Well. that's it! If you like this, go on to the movies-example, where we get more granular when it comes to data loading.
|
||||
// Well thanks for tuning in! Come visit us at our chat room at slack.vulcanjs.org. See you soon!
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// modules/index.js - #tutorial-step-8 -
|
||||
// This is where we import our collections
|
||||
|
||||
// The Movies collection
|
||||
import './movies/collection.js';
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/*
|
||||
|
||||
The main Movies collection definition file.
|
||||
modules/movies/collection.js - #tutorial-step-9 -
|
||||
This is the main Movies collection definition file.
|
||||
|
||||
A collection in VulcanJS is basically is a model, a type of data, like posts, comments, or users.
|
||||
|
||||
*/
|
||||
|
||||
|
@ -12,17 +15,30 @@ import schema from './schema.js';
|
|||
|
||||
Movies collection definition
|
||||
|
||||
We create a new collection with the createCollection function
|
||||
*/
|
||||
const Movies = createCollection({
|
||||
|
||||
// It takes the collection name...
|
||||
collectionName: 'Movies',
|
||||
|
||||
// ...the type name, which is it's name of that type's singular instance
|
||||
// usually it is the same as the collection name but singular.
|
||||
// It comes in useful when it is time to build our GraphQL schema...
|
||||
typeName: 'Movie',
|
||||
|
||||
// ...this is a JS schema, not GraphQL...
|
||||
schema,
|
||||
|
||||
// ...then our default resolvers and default mutations...
|
||||
|
||||
// A resolver is the thing that gives you data, that fetches it in the database and sends it to the client.
|
||||
// There are three default resolvers: list - for a list of documents, single - for a single document, and total - for the total number of documents that match a given query.
|
||||
// You can code your own too. Check out the next example, the movies example to do so...
|
||||
resolvers: getDefaultResolvers('Movies'),
|
||||
|
||||
// A mutation is the act of changing data on the server.
|
||||
// There are three default mutaitons: inserting a new document, editing an existing document, and removing a document. You can only do this if you own it.
|
||||
mutations: getDefaultMutations('Movies'),
|
||||
|
||||
});
|
||||
|
@ -31,8 +47,11 @@ const Movies = createCollection({
|
|||
|
||||
Permissions for members (regular users)
|
||||
|
||||
...members are default users in Vulcan...
|
||||
|
||||
*/
|
||||
const membersActions = [
|
||||
// ...these are the actions that members can do...
|
||||
'movies.new',
|
||||
'movies.edit.own',
|
||||
'movies.remove.own',
|
||||
|
@ -43,13 +62,22 @@ Users.groups.members.can(membersActions);
|
|||
|
||||
Default sort
|
||||
|
||||
This is the default sort view for this data type...
|
||||
|
||||
*/
|
||||
Movies.addDefaultView(terms => ({
|
||||
options: {
|
||||
sort: {
|
||||
// ...We want to order by when it was created.
|
||||
// This gets passed to MongoDB.
|
||||
// This will insert in the same order on the server and the client,
|
||||
// which is how the app knew where to put our new Jaws 14 entry in the page.
|
||||
createdAt: -1
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
export default Movies;
|
||||
|
||||
// There were three things I mentioned that you might not have heard of:
|
||||
// schema, revolvers, and mutations. I will talk about them in the next steps.
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
/*
|
||||
|
||||
modules/movies/schema.js - #tutorial-step-10 -
|
||||
This is a JS object that defines every property of a collection document...
|
||||
|
||||
A SimpleSchema-compatible JSON schema
|
||||
|
||||
*/
|
||||
|
||||
const schema = {
|
||||
|
||||
// default properties
|
||||
|
||||
_id: {
|
||||
type: String,
|
||||
optional: true,
|
||||
viewableBy: ['guests'],
|
||||
viewableBy: ["guests"]
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
optional: true,
|
||||
viewableBy: ['guests'],
|
||||
viewableBy: ["guests"],
|
||||
onInsert: (document, currentUser) => {
|
||||
return new Date();
|
||||
}
|
||||
|
@ -24,45 +26,50 @@ const schema = {
|
|||
userId: {
|
||||
type: String,
|
||||
optional: true,
|
||||
viewableBy: ['guests'],
|
||||
viewableBy: ["guests"],
|
||||
resolveAs: {
|
||||
fieldName: 'user',
|
||||
type: 'User',
|
||||
fieldName: "user",
|
||||
type: "User",
|
||||
resolver: (movie, args, context) => {
|
||||
return context.Users.findOne({ _id: movie.userId }, { fields: context.Users.getViewableFields(context.currentUser, context.Users) });
|
||||
return context.Users.findOne(
|
||||
{ _id: movie.userId },
|
||||
{ fields: context.Users.getViewableFields(context.currentUser, context.Users) }
|
||||
);
|
||||
},
|
||||
addOriginalField: true
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// custom properties
|
||||
|
||||
name: {
|
||||
label: 'Name',
|
||||
label: "Name",
|
||||
type: String,
|
||||
optional: true,
|
||||
viewableBy: ['guests'],
|
||||
insertableBy: ['members'],
|
||||
editableBy: ['members'],
|
||||
// ...these next three are interesting—they take a user group that says which group can do what action.
|
||||
// ...guests are anonymous users...
|
||||
viewableBy: ["guests"],
|
||||
/// ...members can only edit documents that they own. This is part of the default mutations. Back to modules/movies/collection.js...
|
||||
insertableBy: ["members"],
|
||||
editableBy: ["members"]
|
||||
},
|
||||
year: {
|
||||
label: 'Year',
|
||||
label: "Year",
|
||||
type: String,
|
||||
optional: true,
|
||||
viewableBy: ['guests'],
|
||||
insertableBy: ['members'],
|
||||
editableBy: ['members'],
|
||||
viewableBy: ["guests"],
|
||||
insertableBy: ["members"],
|
||||
editableBy: ["members"]
|
||||
},
|
||||
review: {
|
||||
label: 'Review',
|
||||
label: "Review",
|
||||
type: String,
|
||||
optional: true,
|
||||
control: 'textarea',
|
||||
viewableBy: ['guests'],
|
||||
insertableBy: ['members'],
|
||||
editableBy: ['members']
|
||||
},
|
||||
|
||||
control: "textarea",
|
||||
viewableBy: ["guests"],
|
||||
insertableBy: ["members"],
|
||||
editableBy: ["members"]
|
||||
}
|
||||
};
|
||||
|
||||
export default schema;
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
// modules/routes.js #tutorial-step-6 -
|
||||
// Thi is the file that is called into package.js that allows the component to be found.
|
||||
|
||||
// First, we import this from vulcan core, which is a utility to add a new route.
|
||||
import { addRoute, Components } from 'meteor/vulcan:core';
|
||||
|
||||
// Then, we add the component for what we want to add.
|
||||
import '../components/movies/MoviesList.jsx';
|
||||
|
||||
// Next, we add the name 'movies', the path, which is the root, and the component name 'MovieList'.
|
||||
addRoute({ name: 'movies', path: '/', componentName: 'MoviesList' });
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
// server/main.js #tutorial-step-4 - This is the file that is called into package.js.
|
||||
import '../modules/index.js';
|
||||
import './seed.js';
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
|
||||
Seed the database with some dummy content.
|
||||
server/seed.js #tutorial-step-5 -
|
||||
This is a file to seed the database with some dummy content.
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// packages.js #tutorial-step-2 - Decribes the contents of the package as well as the dependencies.
|
||||
|
||||
Package.describe({
|
||||
name: 'example-simple',
|
||||
});
|
||||
|
@ -5,6 +7,8 @@ Package.describe({
|
|||
Package.onUse(function (api) {
|
||||
|
||||
api.use([
|
||||
|
||||
// Here are our dependencies:
|
||||
|
||||
// vulcan core
|
||||
'vulcan:core@1.8.3',
|
||||
|
@ -17,6 +21,7 @@ Package.onUse(function (api) {
|
|||
|
||||
api.addFiles('lib/stylesheets/style.css');
|
||||
|
||||
// Here is the entry point for client & server:
|
||||
api.mainModule('lib/server/main.js', 'server');
|
||||
api.mainModule('lib/client/main.js', 'client');
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue