vueJS
Initializing
new Vue({
el: '#app',
data: {
foods: [],
food_name: ''
},
ready: function() {
},
methods: {
addFood: function() {
var food_name = this.food_name;
this.foods.push({
name: food_name
});
}
}
})
The "View"
<div id="app">
<ul>
<li v-for="food in foods">
{{ food.name }}
</li>
</ul>
<form @submit.prevent="addFood()">
<input type="text" v-model="food_name">
<button class="button" type="submit" @click.prevent="addFood()">
</form>
</div>
Methods
new Vue({
el: '#app',
data: {
foods: [],
food_name: ''
},
methods: {
addFood: function() {
var food_name = this.food_name;
this.foods.push({
name: food_name
});
},
deleteFood: function(index) {
this.foods = _.without(this.foods, this.foods[index]);
}
}
})
The "View"
<div id="app">
<ul>
<li v-for="food in foods">
{{ food.name }} <a href="" @click.prevent="deleteFood($index)">X</a>
</li>
</ul>
<form @submit.prevent="addFood()">
<input type="text" v-model="food_name">
<button class="button" type="submit" @click.prevent="addFood()">
</form>
</div>
Callbacks
new Vue({
el: '#app',
data: {
foods: [],
food_name: ''
},
ready: function() {
var _this = this;
$.get('/foods')
.success(function(results) {
_this.foods = results;
}, 'json');
},
methods: {
addFood: function() {
var food_name = this.food_name;
this.foods.push({
name: food_name
});
}
}
})
Custom Filters
// Price filter
Vue.filter('price', {
read: function(val) {
return typeof val != 'undefined' ? '$' + parseFloat(val).toFixed(2) : '';
},
write: function(val, oldVal) {
var number = + val.replace(/[^\d.]/g, '')
return isNaN(number) ? 0 : parseFloat(number.toFixed(2));
}
});
new Vue({
el: '#app',
data: {
foods: [],
food_name: '',
food_price: 0
},
ready: function() {
var _this = this;
$.get('/foods')
.success(function(results) {
_this.foods = results;
}, 'json');
},
methods: {
addFood: function() {
var food_name = this.food_name
food_price = this.food_price;
this.foods.push({
name: food_name,
price: food_price
});
}
}
})
The "View"
<div id="app">
<ul>
<li v-for="food in foods">
{{ food.name }} - {{ food.price | price }} <a href="" @click.prevent="deleteFood($index)">X</a>
</li>
</ul>
<form @submit.prevent="addFood()">
<input type="text" v-model="food_name"><br />
<input type="text" v-model="food_price" price><br />
<button class="button" type="submit" @click.prevent="addFood()">
</form>
</div>
Components / Routing
var FormRepeater = Vue.extend({
template: '#form-repeater',
data: function() {
return {
children: [],
child: {
name: '',
dominant_hand: ''
}
}
},
ready: function() {
if ($('#children').length) {
this.$set('children', JSON.parse($('#children').html()));
}
if (!this.children.length) {
var child = jQuery.extend({}, this.child);
this.children.push(child);
}
},
methods: {
addChild: function() {
var child = jQuery.extend({}, this.child);
this.children.push(child);
},
removeChild: function(index) {
var children = _.without(this.children, this.children[index]);
this.$set('children', children);
},
submitChildren: function() {
console.log(this.children);
}
}
});
var ShoppingCart = Vue.extend({
template: '#shopping-cart',
data: function() {
return {
products: [],
cart_items: [],
loading: false
}
},
ready: function() {
var _this = this;
$.get('http://vue_api.local/order', function(order) {
_this.products = order.products;
_this.cart_items = order.cart_items;
}, 'json');
},
methods: {
addProduct: function(index) {
var product = $.extend({}, this.products[index]);
this.loading = true;
var quantity = parseInt(product.quantity);
if (quantity >= 1) {
var find = _.findIndex(this.cart_items, { id: product.id });
if (find === -1) {
product.quantity = quantity;
this.cart_items.push(product);
} else {
product.quantity = quantity + this.cart_items[index].quantity;
this.cart_items.$set(index, product);
}
this.products[index].quantity = '';
this.saveProgress('Product has been added.');
}
},
removeProduct: function(index) {
this.cart_items = _.without(this.cart_items, this.cart_items[index]);
this.saveProgress('Product has been removed.');
},
saveProgress: function(text) {
var cart_items = JSON.stringify(this.cart_items),
_this = this;
$.post('http://vue_api.local/saveOrder', { cart: cart_items }, function(response) {
if (response.status) {
$.growl.notice({ title: '', message: text });
}
_this.loading = false;
}, 'json');
},
getTotal: function() {
return 0;
}
}
});
// Price filter
Vue.filter('price', {
read: function(val) {
return typeof val != 'undefined' ? '$' + parseFloat(val).toFixed(2) : '';
},
write: function(val, oldVal) {
var number = + val.replace(/[^\d.]/g, '')
return isNaN(number) ? 0 : parseFloat(number.toFixed(2));
}
});
var app = Vue.extend({});
var router = new VueRouter();
router.map({
'/form-repeater': {
component: FormRepeater
},
'/shopping-cart': {
component: ShoppingCart
}
});
router.start(app, '#app');
The "View"
<html>
<head>
<!-- Google Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<!-- CSS Reset -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css">
<!-- Milligram CSS minified -->
<link rel="stylesheet" href="/sandbox/vue/bower_components/milligram/dist/milligram.min.css">
<link rel="stylesheet" href="app.css">
<link rel="stylesheet" href="jquery.growl.css">
<!-- You should properly set the path from the main file. -->
</head>
<body class="container">
<div id="app" class="row">
<div class="column">
<h1>VueJS Examples</h1>
<ul>
<li>
<a v-link="{ path: '/form-repeater' }">
repeater items with form
</a>
</li>
<li>
<a v-link="{ path: '/shopping-cart' }">
shopping cart
</a>
</li>
<li>
<a v-link="{ path: '' }">
</a>
</li>
</ul>
<router-view></router-view>
<script type="x/template" id="form-repeater">
<div class="row">
<div class="column-10">
<a href="" @click.prevent="addChild()" class="button">Add Child</a>
</div>
</div>
<form method="post" @submit.prevent="submitChildren()">
<div class="row">
<div class="column-50" v-for="child in children">
<h2>Child #{{ $index }}</h2>
<label>Name</label>
<input type="text" name="data[{{ $index }}][name]" v-model="children[$index].name"><br/>
<label>Dominant Hand</label>
<label>Left Hand</label>
<input type="radio" name="data[{{ $index }}][dominant_hand]" v-model="children[$index].dominant_hand" value="Left Hand"><br/>
<label>Right Hand</label>
<input type="radio" name="data[{{ $index }}][dominant_hand]" v-model="children[$index].dominant_hand" value="Right Hand"><br/>
<a href="" @click.prevent="removeChild($index)" v-if="$index != 0">Delete</a>
</div>
</div>
<div class="row">
<button type="submit" class="button">Submit</button>
</div>
</form>
</script>
<script type="x/template" id="shopping-cart">
<h1>Products</h1>
<h4>Cart</h4>
<table>
<thead>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th></th>
</thead>
<tbody>
<tr v-for="product in products">
<td>{{ product.name }}</td>
<td>{{ product.price | price }}</td>
<td>
<div class="column column-20">
<input type="number" name="products[{{ $index }}][quantity]" v-model="products[$index].quantity" price>
</div>
</td>
<td>
<a class="button" @click.prevent="addProduct($index)">+</a>
</td>
</tr>
</tbody>
</table>
<h4>Total: {{ getTotal() | price }}</h4>
</script>
</div>
</div>
</body>
<script type="text/javascript" src="vue.min.js"></script>
<script type="text/javascript" src="//code.jquery.com/jquery-2.2.0.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/vue.router/0.7.10/vue-router.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script type="text/javascript" src="jquery.growl.js"></script>
</html>
Pros / Cons
Pros
- Easier to get into vs Angular 1
- Better LTS support vs Angular 1 (it appears)
- Super easy-to-use syntax, the layout of methods, data, filters, etc. just makes sense.
- Data binding is a lot faster/better than angular 1.
Cons
- Less overall support/users than Angular 1, and likely, 2.
- Not as mature as Angular.
- Doesn't come with things such as http.
Resources
vueJS
By Charlie Page
vueJS
- 791