Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/counter-vue/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/build
/yarn.lock
*.log
15 changes: 15 additions & 0 deletions examples/counter-vue/Counter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<div id="app">
Clicked: {{ model.get('value') }} times, count is {{ model.getEvenOrOdd() }}.
<div>
<button @click="model.sendIncrement">+</button>
<button @click="model.sendDecrement">-</button>
</div>
</div>
</template>

<script>
export default {
props: ['model']
}
</script>
9 changes: 9 additions & 0 deletions examples/counter-vue/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Counter Example

This is the classic Counter example implemented with `vue` + `modulajs`.

## Installation

1. `yarn install` (or `npm install`)
2. `yarn start` (or `npm start`)
3. Visit `index.html` locally with browser
16 changes: 16 additions & 0 deletions examples/counter-vue/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Vue from 'vue';
import Container from './container';
import Counter from './Counter.vue';
import { CounterModel } from './counter_model';

const vue = new Vue({
el: '#app',
render: h => h(Container, {
props: {
Component: Counter,
Model: CounterModel
}
})
});

export default vue;
62 changes: 62 additions & 0 deletions examples/counter-vue/container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Vue from 'vue';
import { createStore } from 'modulajs'; // eslint-disable-line

/**
* This container is a convenient helper to pair a vue component and a modulajs
* model.
*
* It's expected to keep only one container at root level of the vue app, with
* "root" component and "root" model specified. Models other than root model
* could be designed as its child model or descendant models, then passed down
* with component props.
*/
const Container = Vue.component('container', {
props: {
Component: {
type: Object,
required: true
},
Model: {
type: Function,
required: true
}
},

data() {
// Create a ModulaJS store to hold state in the decorated model
this.__store = createStore(this.Model);

return Object.assign({}, {
state: this.__store.getState(),
__store_unsubscribe: undefined
});
},

beforeMount() {
// Subscribe to store changing, then notify the listeners
this.__store_unsubscribe = this.__store.subscribe(this.handleUpdate);
// Bootstrap the state tree in root model
this.__store.getState().sendInit();
},

destroyed() {
this.__store_unsubscribe();
this.__store = null;
},

methods: {
handleUpdate() {
Object.assign(this.$data, { state: this.__store.getState() });
}
},

render(createElement) {
return createElement(this.Component, {
props: {
model: this.state.get('decoratedModel')
}
});
}
});

export default Container;
55 changes: 55 additions & 0 deletions examples/counter-vue/counter_model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createModel, createConstants } from 'modulajs'; // eslint-disable-line
import PropTypes from 'prop-types';

export const ActionTypes = createConstants('COUNTER', {
INCREMENT: null,
DECREMENT: null
});

export const CounterModel = createModel({
displayName: 'CounterModel',

propTypes: {
value: PropTypes.number.isRequired
},

defaults: {
value: 0
},

sendIncrement() {
this.dispatch({ type: ActionTypes.INCREMENT });
},

recvIncrement() {
return {
type: ActionTypes.INCREMENT,
update(model) {
const newModel = model.set('value', value => value + 1);

return [newModel];
}
};
},

sendDecrement() {
if (this.get('value') > 0) {
this.dispatch({ type: ActionTypes.DECREMENT });
}
},

recvDecrement() {
return {
type: ActionTypes.DECREMENT,
update(model) {
const newModel = model.set('value', value => value - 1);

return [newModel];
}
};
},

getEvenOrOdd() {
return this.get('value') % 2 === 0 ? 'even' : 'odd';
}
});
12 changes: 12 additions & 0 deletions examples/counter-vue/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Counter Example with vue + modulajs</title>
</head>
<body>
<div id="app"></div>
<script src="build/shared.js"></script>
<script src="build/main.js"></script>
</body>
</html>
22 changes: 22 additions & 0 deletions examples/counter-vue/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "counter-vue-modulajs",
"version": "1.1.0",
"description": "Counter example with vue + modulajs",
"main": "index.js",
"author": "yiwsong",
"license": "Apache-2.0",
"scripts": {
"start": "webpack --watch --config webpack.config.js"
},
"dependencies": {
"prop-types": "^15.6.1",
"vue": "^2.5.16"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"vue-loader": "^15.0.4",
"vue-template-compiler": "^2.5.16",
"webpack": "^3.11.0"
}
}
42 changes: 42 additions & 0 deletions examples/counter-vue/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const path = require('path');
const webpack = require('webpack');
const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
devtool: 'inline-source-map',

entry: ['./app.js'],

output: {
path: path.join(__dirname, 'build'),
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/build/'
},

module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
{ test: /\.vue$/, loader: 'vue-loader' }
]
},

resolve: {
alias: {
modulajs: path.resolve(__dirname, '../../src/index.js')
}
},

plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'shared',
filename: 'shared.js'
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new VueLoaderPlugin()
]
};