├── .gitignore ├── .meteor ├── .gitignore ├── release ├── platforms ├── .id ├── .finished-upgraders ├── packages └── versions ├── client ├── main.css ├── main.html ├── main.js └── ui │ ├── Hello.vue │ ├── Post.vue │ ├── ThreadItem.vue │ ├── SortField.vue │ ├── SelectedThread.vue │ ├── App.vue │ └── Forum.vue ├── server └── main.js ├── imports ├── api │ ├── collections.js │ ├── publications.js │ └── methods.js ├── ui │ ├── filters.js │ └── mixins.less └── vuex │ ├── store.js │ └── modules │ ├── thread.js │ └── forum.js ├── package.json ├── .gitattributes └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.3.4.1 2 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /client/main.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ 2 | -------------------------------------------------------------------------------- /server/main.js: -------------------------------------------------------------------------------- 1 | import '/imports/api/methods'; 2 | import '/imports/api/publications'; 3 | -------------------------------------------------------------------------------- /client/main.html: -------------------------------------------------------------------------------- 1 | 2 | meteor-vuex-example 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /imports/api/collections.js: -------------------------------------------------------------------------------- 1 | export const Threads = new Mongo.Collection('threads') 2 | export const Posts = new Mongo.Collection('posts') 3 | -------------------------------------------------------------------------------- /imports/ui/filters.js: -------------------------------------------------------------------------------- 1 | import { Vue } from 'meteor/akryum:vue'; 2 | import moment from 'moment'; 3 | 4 | Vue.filter('dateFromNow', (date) => { 5 | return moment(date).fromNow(); 6 | }); 7 | -------------------------------------------------------------------------------- /imports/ui/mixins.less: -------------------------------------------------------------------------------- 1 | .padding(@px) { 2 | padding: @px; 3 | } 4 | 5 | .margin(@px) { 6 | margin: @px; 7 | } 8 | 9 | .border-radius(@px) { 10 | border-radius: @px; 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meteor-vuex-example", 3 | "private": true, 4 | "scripts": { 5 | "start": "meteor run" 6 | }, 7 | "dependencies": { 8 | "meteor-node-stubs": "~0.2.0" 9 | }, 10 | "devDependencies": { 11 | "moment": "^2.14.1", 12 | "vue": "^1.0.24", 13 | "vuex": "^1.0.0-rc.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | ad1q0e1puof3110gh2de 8 | -------------------------------------------------------------------------------- /client/main.js: -------------------------------------------------------------------------------- 1 | // Libs 2 | import {Meteor} from 'meteor/meteor'; 3 | import {Vue} from 'meteor/akryum:vue'; 4 | 5 | // Api 6 | import '/imports/api/methods'; 7 | 8 | // Filters 9 | import '/imports/ui/filters'; 10 | 11 | // Main app 12 | Meteor.startup(() => { 13 | new Vue({ 14 | el: 'body', 15 | replace: false 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /imports/api/publications.js: -------------------------------------------------------------------------------- 1 | import {Meteor} from 'meteor/meteor'; 2 | import {Threads, Posts} from './collections'; 3 | 4 | Meteor.publish('threads', function() { 5 | return Threads.find(); 6 | }); 7 | 8 | Meteor.publish('posts', function(thread_id) { 9 | check(thread_id, String); 10 | 11 | return Posts.find({ 12 | thread_id 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | -------------------------------------------------------------------------------- /client/ui/Hello.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | 27 | 36 | -------------------------------------------------------------------------------- /client/ui/Post.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | 37 | -------------------------------------------------------------------------------- /imports/vuex/store.js: -------------------------------------------------------------------------------- 1 | import {StoreModule} from 'meteor/akryum:vuex'; 2 | 3 | const root = new StoreModule(); 4 | 5 | // Add some initial state 6 | root.addState({ 7 | name: 'world' 8 | }); 9 | 10 | // Using centralized getters is good practice 11 | // They are also cached by vue just like computed props 12 | root.addGetters({ 13 | name: state => state.name 14 | }); 15 | 16 | // Only mutations can change the store state 17 | root.addMutations({ 18 | NAME(state, text) { 19 | state.name = text; 20 | } 21 | }); 22 | 23 | // Using centralized actions is good practice 24 | root.addActions({ 25 | setName({store}, text) { 26 | // state is immutable 27 | store.dispatch('NAME', text); 28 | } 29 | }); 30 | 31 | // Submodule 32 | import forum from './modules/forum'; 33 | root.addModule(forum); 34 | 35 | // Export the vuex native store 36 | export const store = root.exportStore(); 37 | -------------------------------------------------------------------------------- /client/ui/ThreadItem.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | 26 | 50 | -------------------------------------------------------------------------------- /client/ui/SortField.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 34 | 35 | 43 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base # Packages every Meteor app needs to have 8 | mobile-experience # Packages for a great mobile UX 9 | mongo # The database Meteor supports right now 10 | reactive-var # Reactive variable for tracker 11 | jquery # Helpful client-side library 12 | tracker # Meteor's client-side reactive programming library 13 | 14 | standard-minifier-css # CSS minifier run for production mode 15 | standard-minifier-js # JS minifier run for production mode 16 | es5-shim # ECMAScript 5 compatibility for older browsers. 17 | ecmascript # Enable ECMAScript2015+ syntax in app code 18 | 19 | static-html 20 | akryum:vue 21 | akryum:vue-component 22 | akryum:vuex 23 | akryum:vue-less 24 | check 25 | -------------------------------------------------------------------------------- /imports/api/methods.js: -------------------------------------------------------------------------------- 1 | import {Meteor} from 'meteor/meteor'; 2 | import {Threads, Posts} from './collections'; 3 | 4 | Meteor.methods({ 5 | 'threads.create': function(name) { 6 | check(name, String); 7 | 8 | let _id = Threads.insert({ 9 | name, 10 | created: new Date(), 11 | date: new Date() 12 | }) 13 | 14 | return _id; 15 | }, 16 | 'threads.remove': function(_id) { 17 | check(_id, String); 18 | Posts.remove({ 19 | thread_id: _id 20 | }); 21 | Threads.remove(_id); 22 | }, 23 | 'posts.create': function(thread_id, message) { 24 | check(thread_id, String); 25 | check(message, String); 26 | 27 | let thread = Threads.findOne(thread_id); 28 | if(!thread) { 29 | throw new Meteor.Error('Thread not found'); 30 | } 31 | 32 | let _id = Posts.insert({ 33 | thread_id, 34 | message, 35 | created: new Date() 36 | }); 37 | 38 | Threads.update(thread_id, { 39 | $set: { 40 | date: new Date() 41 | } 42 | }); 43 | 44 | return _id; 45 | }, 46 | 'posts.remove': function(_id) { 47 | check(_id, String); 48 | Posts.remove(_id); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /client/ui/SelectedThread.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 50 | -------------------------------------------------------------------------------- /client/ui/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple meteor app with vuex 2 | 3 | A simple meteor project featuring [vue](https://vuejs.org/) as ui layer ([more info](https://github.com/Akryum/meteor-vue-component)), with [state management](https://github.com/Akryum/meteor-vue-component/tree/master/packages/vuex). 4 | 5 | ## Steps to reproduce 6 | 7 | In the console, create the project and add the relevant packages: 8 | 9 | meteor create meteor-vuex-example 10 | cd ./meteor-vuex-example 11 | meteor remove blaze-html-templates autopublish insecure 12 | meteor add static-html check akryum:vue akryum:vue-component akryum:vuex akryum:vue-less 13 | meteor 14 | 15 | All the required npm dependencies will be automatically added to your project's `package.json` and installed with `meteor npm install`. 16 | 17 | Replace the `client/main.html` file with: 18 | 19 | ```html 20 | 21 | meteor-vuex-example 22 | 23 | 24 | 25 | 26 | 27 | ``` 28 | 29 | Replace the `client/main.js` file with: 30 | 31 | ```javascript 32 | // Libs 33 | import {Meteor} from 'meteor/meteor'; 34 | import {Vue} from 'meteor/akryum:vue'; 35 | 36 | // Main app 37 | import App from '/imports/ui/App.vue'; 38 | 39 | Meteor.startup(() => { 40 | new Vue({ 41 | el: 'body', 42 | replace: false, 43 | components: { 44 | App 45 | } 46 | }); 47 | }); 48 | ``` 49 | 50 | Create store modules and add components with `vuex` options ([more info](https://github.com/Akryum/meteor-vue-component/tree/master/packages/vuex#usage)). 51 | -------------------------------------------------------------------------------- /client/ui/Forum.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 55 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | akryum:npm-check@0.0.3 2 | akryum:vue@1.0.3 3 | akryum:vue-component@0.5.1 4 | akryum:vue-component-dev-client@0.0.5 5 | akryum:vue-component-dev-server@0.0.1 6 | akryum:vue-less@0.0.3 7 | akryum:vuex@0.3.0 8 | allow-deny@1.0.5 9 | autoupdate@1.2.10 10 | babel-compiler@6.8.3 11 | babel-runtime@0.1.9_1 12 | base64@1.0.9 13 | binary-heap@1.0.9 14 | blaze@2.1.8 15 | blaze-tools@1.0.9 16 | boilerplate-generator@1.0.9 17 | caching-compiler@1.0.5_1 18 | caching-html-compiler@1.0.6 19 | callback-hook@1.0.9 20 | check@1.2.3 21 | ddp@1.2.5 22 | ddp-client@1.2.8_1 23 | ddp-common@1.2.6 24 | ddp-server@1.2.8_1 25 | deps@1.0.12 26 | diff-sequence@1.0.6 27 | ecmascript@0.4.6_1 28 | ecmascript-runtime@0.2.11_1 29 | ejson@1.0.12 30 | es5-shim@4.5.12_1 31 | fastclick@1.0.12 32 | geojson-utils@1.0.9 33 | hot-code-push@1.0.4 34 | html-tools@1.0.10 35 | htmljs@1.0.10 36 | http@1.1.7 37 | id-map@1.0.8 38 | jquery@1.11.9 39 | launch-screen@1.0.12 40 | livedata@1.0.18 41 | logging@1.0.13_1 42 | meteor@1.1.15_1 43 | meteor-base@1.0.4 44 | minifier-css@1.1.12_1 45 | minifier-js@1.1.12_1 46 | minimongo@1.0.17 47 | mobile-experience@1.0.4 48 | mobile-status-bar@1.0.12 49 | modules@0.6.4 50 | modules-runtime@0.6.4_1 51 | mongo@1.1.9_1 52 | mongo-id@1.0.5 53 | npm-mongo@1.4.44_1 54 | observe-sequence@1.0.12 55 | ordered-dict@1.0.8 56 | promise@0.7.2_1 57 | random@1.0.10 58 | reactive-var@1.0.10 59 | reload@1.1.10 60 | retry@1.0.8 61 | routepolicy@1.0.11 62 | spacebars@1.0.12 63 | spacebars-compiler@1.0.12 64 | standard-minifier-css@1.0.7_1 65 | standard-minifier-js@1.0.7_1 66 | static-html@1.0.10_1 67 | templating-tools@1.0.4 68 | tracker@1.0.14 69 | ui@1.0.11 70 | underscore@1.0.9 71 | url@1.0.10 72 | webapp@1.2.9_1 73 | webapp-hashing@1.0.9 74 | -------------------------------------------------------------------------------- /imports/vuex/modules/thread.js: -------------------------------------------------------------------------------- 1 | import { StoreSubModule } from 'meteor/akryum:vuex'; 2 | 3 | const subModule = new StoreSubModule('thread'); 4 | 5 | subModule.addState({ 6 | selectedThreadId: null 7 | }); 8 | 9 | subModule.addGetters({ 10 | selectedThreadId: sate => sate.selectedThreadId 11 | }); 12 | 13 | subModule.addMutations({ 14 | FORUM_SELECTED_THREAD_ID(state, id) { 15 | state.selectedThreadId = id; 16 | } 17 | }); 18 | 19 | subModule.addActions({ 20 | selectThread({store}, id) { 21 | // state is immutable 22 | store.dispatch('FORUM_SELECTED_THREAD_ID', id); 23 | }, 24 | createThread(_, name) { 25 | return this.callMethod('threads.create', name, (err, result) => { 26 | if(err) { 27 | console.error(err); 28 | } else { 29 | // Call another action on the submodule 30 | this.actions.selectThread(result); 31 | } 32 | }); 33 | }, 34 | removeThread ({state}) { 35 | return this.callMethod('threads.remove', state.selectedThreadId); 36 | }, 37 | createPost ({state}, msg) { 38 | return this.callMethod('posts.create', state.selectedThreadId, msg) 39 | }, 40 | removePost(_, id) { 41 | return this.callMethod('posts.remove', id) 42 | } 43 | }); 44 | 45 | // Meteor integration 46 | 47 | // Import meteor collections 48 | import {Threads, Posts} from '/imports/api/collections'; 49 | 50 | subModule.addTrackers({ 51 | selectedThread() { 52 | let sub; 53 | return { 54 | init(data) { 55 | data.selectedThread = null; 56 | data.posts = []; 57 | }, 58 | watch(state) { 59 | // Dynamic subscription 60 | if(sub) { 61 | sub.stop(); 62 | } 63 | if(state.selectedThreadId) { 64 | sub = Meteor.subscribe('posts', state.selectedThreadId); 65 | console.log('subscribed posts to thread ', state.selectedThreadId); 66 | } 67 | 68 | return { 69 | id: state.selectedThreadId 70 | } 71 | }, 72 | update(data, {id}) { 73 | data.selectedThread = Object.freeze(Threads.findOne({ 74 | _id: id 75 | })); 76 | data.posts = Object.freeze(Posts.find({ 77 | thread_id: id 78 | }, { 79 | sort: {created: -1} 80 | }).fetch()); 81 | console.log('posts', data.posts); 82 | }, 83 | getters: { 84 | getSelectedThread: data => data.selectedThread, 85 | getPosts: data => data.posts 86 | } 87 | } 88 | } 89 | }) 90 | 91 | export default subModule; 92 | -------------------------------------------------------------------------------- /imports/vuex/modules/forum.js: -------------------------------------------------------------------------------- 1 | import {StoreSubModule} from 'meteor/akryum:vuex'; 2 | 3 | const subModule = new StoreSubModule('forum'); 4 | 5 | const sortFields = ['date', 'name']; 6 | 7 | subModule.addState({ 8 | sortDirection: -1, 9 | sortField: sortFields[0] 10 | }); 11 | 12 | subModule.addGetters({ 13 | sortDirection: state => state.sortDirection, 14 | sortField: state => state.sortField 15 | }); 16 | 17 | subModule.addMutations({ 18 | FORUM_SORT_DIRECTION(state, direction) { 19 | state.sortDirection = direction; 20 | }, 21 | FORUM_SORT_FIELD(state, field) { 22 | state.sortField = field; 23 | } 24 | }); 25 | 26 | subModule.addActions({ 27 | toggleSortDirection({store, state}) { 28 | // state is immutable 29 | store.dispatch('FORUM_SORT_DIRECTION', -1*state.sortDirection); 30 | }, 31 | cycleSortField({store, state}) { 32 | // state is immutable 33 | let index = sortFields.indexOf(state.sortField) + 1; 34 | if(index === sortFields.length) { 35 | index = 0; 36 | } 37 | store.dispatch('FORUM_SORT_FIELD', sortFields[index]); 38 | } 39 | }); 40 | 41 | // Meteor integration 42 | 43 | // Import a meteor collection 44 | import {Threads} from '/imports/api/collections'; 45 | 46 | // Add trackers to the store module 47 | subModule.addTrackers({ 48 | // Name of the tracker 49 | threads() { 50 | // Context variables 51 | let sub; 52 | 53 | // You can execute arbitrary code here 54 | 55 | return { 56 | // Initialize the meteor data 57 | init(data) { 58 | data.threads = [] 59 | }, 60 | // When the tracker is being used 61 | activate() { 62 | // Subscribe to the publication 63 | sub = Meteor.subscribe('threads'); 64 | }, 65 | // When the tracker is no longer used 66 | deactivate() { 67 | // Stop the subscription 68 | sub.stop(); 69 | }, 70 | // Watch store changes 71 | // State is relative to the module 72 | watch(state) { 73 | // state is immutable 74 | return { 75 | sortDirection: state.sortDirection, 76 | sortField: state.sortField 77 | } 78 | }, 79 | // Update the meteor data 80 | // Data is relative to the module 81 | update(data, {sortDirection, sortField}) { 82 | // Meteor data query 83 | let threads = Threads.find({}, { 84 | sort: { 85 | [sortField]: sortDirection 86 | } 87 | }).fetch(); 88 | 89 | // Update the module meteor data 90 | data.threads = Object.freeze(threads); 91 | }, 92 | // Getters 93 | // These are computed properties and are cached by vue 94 | getters: { 95 | // Getters should follow the get naming convention 96 | getThreads: data => data.threads 97 | }, 98 | // If true, the tracker will be activated right away 99 | // Else, you need to add it on a vue component or call tracker.addClient() 100 | isActivated: false 101 | } 102 | } 103 | }); 104 | 105 | 106 | // Nested Submodule 107 | import thread from './thread'; 108 | subModule.addModule(thread); 109 | 110 | export default subModule; 111 | --------------------------------------------------------------------------------