# A Todo MVC Application using Iris and Vue.js ## Hackernoon Article: https://medium.com/hackernoon/a-todo-mvc-application-using-iris-and-vue-js-5019ff870064 Vue.js is a front-end framework for building web applications using javascript. It has a blazing fast Virtual DOM renderer. Iris is a back-end framework for building web applications using The Go Programming Language (disclaimer: author here). It's one of the fastest and featured web frameworks out there. We wanna use this to serve our "todo service". ## The Tools Programming Languages are just tools for us, but we need a safe, fast and “cross-platform” programming language to power our service. [Go](https://golang.org) is a [rapidly growing](https://www.tiobe.com/tiobe-index/) open source programming language designed for building simple, fast, and reliable software. Take a look [here](https://github.com/golang/go/wiki/GoUsers) which great companies use Go to power their services. ### Install the Go Programming Language Extensive information about downloading & installing Go can be found [here](https://golang.org/dl/). [![](https://i3.ytimg.com/vi/9x-pG3lvLi0/hqdefault.jpg)](https://youtu.be/9x-pG3lvLi0) > Maybe [Windows](https://www.youtube.com/watch?v=WT5mTznJBS0) or [Mac OS X](https://www.youtube.com/watch?v=5qI8z_lB5Lw) user? > The article does not contain an introduction to the language itself, if you’re a newcomer I recommend you to bookmark this article, [learn](https://github.com/golang/go/wiki/Learn) the language’s fundamentals and come back later on. ## The Dependencies Many articles have been written, in the past, that lead developers not to use a web framework because they are useless and "bad". I have to tell you that there is no such thing, it always depends on the (web) framework that you’re going to use. At production environment, we don’t have the time or the experience to code everything that we wanna use in the applications, and if we could are we sure that we can do better and safely than others? In short term: **Good frameworks are helpful tools for any developer, company or startup and "bad" frameworks are waste of time, crystal clear.** You’ll need two dependencies: 1. Vue.js, for our client-side requirements. Download it from [here](https://vuejs.org/), latest v2. 2. The Iris Web Framework, for our server-side requirements. Can be found [here](https://github.com/kataras/iris), latest v12. > If you have Go already installed then just execute `go get github.com/kataras/iris/v12@latest` to install the Iris Web Framework. ## Start If we are all in the same page, it’s time to learn how we can create a live todo application that will be easy to deploy and extend even more! We're going to use a vue.js todo application which uses browser' s local storage and doesn't have any user-specified features like live sync between browser's tabs, you can find the original version inside the vue's [docs](https://vuejs.org/v2/examples/todomvc.html). Assuming that you know how %GOPATH% works, create an empty folder, i.e "vuejs-todo-mvc" in the %GOPATH%/src directory, there you will create those files: - web/public/js/app.js - web/public/index.html - todo/item.go - todo/service.go - web/controllers/todo_controller.go - web/main.go _Read the comments in the source code, they may be very helpful_ ### The client-side (vue.js) ```js /* file: vuejs-todo-mvc/web/public/js/app.js */ // Full spec-compliant TodoMVC with Iris // and hash-based routing in ~200 effective lines of JavaScript. var ws; ((async () => { const events = { todos: { saved: function (ns, msg) { app.todos = msg.unmarshal() // or make a new http fetch // fetchTodos(function (items) { // app.todos = msg.unmarshal() // }); } } }; const conn = await neffos.dial("ws://localhost:8080/todos/sync", events); ws = await conn.connect("todos"); })()).catch(console.error); function fetchTodos(onComplete) { axios.get("/todos").then(response => { if (response.data === null) { return; } onComplete(response.data); }); } var todoStorage = { fetch: function () { var todos = []; fetchTodos(function (items) { for (var i = 0; i < items.length; i++) { todos.push(items[i]); } }); return todos; }, save: function (todos) { axios.post("/todos", JSON.stringify(todos)).then(response => { if (!response.data.success) { window.alert("saving had a failure"); return; } // console.log("send: save"); ws.emit("save") }); } } // visibility filters var filters = { all: function (todos) { return todos }, active: function (todos) { return todos.filter(function (todo) { return !todo.completed }) }, completed: function (todos) { return todos.filter(function (todo) { return todo.completed }) } } // app Vue instance var app = new Vue({ // app initial state data: { todos: todoStorage.fetch(), newTodo: '', editedTodo: null, visibility: 'all' }, // we will not use the "watch" as it works with the fields like "hasChanges" // and callbacks to make it true but let's keep things very simple as it's just a small getting started. // // watch todos change for persistence // watch: { // todos: { // handler: function (todos) { // if (app.hasChanges) { // todoStorage.save(todos); // app.hasChanges = false; // } // }, // deep: true // } // }, // computed properties // http://vuejs.org/guide/computed.html computed: { filteredTodos: function () { return filters[this.visibility](this.todos) }, remaining: function () { return filters.active(this.todos).length }, allDone: { get: function () { return this.remaining === 0 }, set: function (value) { this.todos.forEach(function (todo) { todo.completed = value }) this.notifyChange(); } } }, filters: { pluralize: function (n) { return n === 1 ? 'item' : 'items' } }, // methods that implement data logic. // note there's no DOM manipulation here at all. methods: { notifyChange: function () { todoStorage.save(this.todos) }, addTodo: function () { var value = this.newTodo && this.newTodo.trim() if (!value) { return } this.todos.push({ id: this.todos.length + 1, // just for the client-side. title: value, completed: false }) this.newTodo = '' this.notifyChange(); }, completeTodo: function (todo) { if (todo.completed) { todo.completed = false; } else { todo.completed = true; } this.notifyChange(); }, removeTodo: function (todo) { this.todos.splice(this.todos.indexOf(todo), 1) this.notifyChange(); }, editTodo: function (todo) { this.beforeEditCache = todo.title this.editedTodo = todo }, doneEdit: function (todo) { if (!this.editedTodo) { return } this.editedTodo = null todo.title = todo.title.trim(); if (!todo.title) { this.removeTodo(todo); } this.notifyChange(); }, cancelEdit: function (todo) { this.editedTodo = null todo.title = this.beforeEditCache }, removeCompleted: function () { this.todos = filters.active(this.todos); this.notifyChange(); } }, // a custom directive to wait for the DOM to be updated // before focusing on the input field. // http://vuejs.org/guide/custom-directive.html directives: { 'todo-focus': function (el, binding) { if (binding.value) { el.focus() } } } }) // handle routing function onHashChange() { var visibility = window.location.hash.replace(/#\/?/, '') if (filters[visibility]) { app.visibility = visibility } else { window.location.hash = '' app.visibility = 'all' } } window.addEventListener('hashchange', onHashChange) onHashChange() // mount app.$mount('.todoapp'); ``` Let's add our view, the static html. ```html