mirror of
https://github.com/kataras/iris.git
synced 2025-02-09 02:34:55 +01:00
write a README at the _examples/tutorial/vuejs-todo-mvc mini tutorial, which will be published to other sites soon
Former-commit-id: cee096d0e327b0180bd4ec83d5674729c67b3ddb
This commit is contained in:
parent
e389097249
commit
4dbecf762c
|
@ -22,8 +22,8 @@ Many articles have been written, in the past, that lead developers not to use a
|
||||||
|
|
||||||
You’ll need only two dependencies:
|
You’ll need only two dependencies:
|
||||||
|
|
||||||
1. The Iris Web Framework, for our server-side requirements. Can be found [here](https://github.com/kataras/iris)
|
1. The Iris Web Framework, for our server-side requirements. Can be found [here](https://github.com/kataras/iris), latest v10.
|
||||||
2. Vue.js, for our client-side requirements. Download it from [here](https://vuejs.org/)
|
2. Vue.js, for our client-side requirements. Download it from [here](https://vuejs.org/), latest v2.
|
||||||
|
|
||||||
> If you have Go already installed then just execute `go get -u github.com/kataras/iris` to install the Iris Web Framework.
|
> If you have Go already installed then just execute `go get -u github.com/kataras/iris` to install the Iris Web Framework.
|
||||||
|
|
||||||
|
@ -34,10 +34,530 @@ If we are all in the same page, it’s time to learn how we can create a live to
|
||||||
We're going to use a vue.js todo application which uses browser'
|
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).
|
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 with any name in the %GOPATH%/src directory, we 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)
|
### The client-side (vue.js)
|
||||||
|
|
||||||
|
```js
|
||||||
|
/* file: web/public/js/app.js */
|
||||||
|
// Full spec-compliant TodoMVC with Iris
|
||||||
|
// and hash-based routing in ~200 effective lines of JavaScript.
|
||||||
|
|
||||||
|
var socket = new Ws("ws://localhost:8080/todos/sync");
|
||||||
|
|
||||||
|
socket.On("saved", function () {
|
||||||
|
// console.log("receive: on saved");
|
||||||
|
fetchTodos(function (items) {
|
||||||
|
app.todos = items
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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");
|
||||||
|
socket.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
|
||||||
|
<!-- file: web/public/index.html -->
|
||||||
|
<!doctype html>
|
||||||
|
<html data-framework="vue">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Iris + Vue.js • TodoMVC</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/todomvc-app-css@2.0.4/index.css">
|
||||||
|
<!-- this needs to be loaded before guide's inline scripts -->
|
||||||
|
<script src="https://vuejs.org/js/vue.js"></script>
|
||||||
|
<!-- $http -->
|
||||||
|
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||||
|
<!-- -->
|
||||||
|
<script src="https://unpkg.com/director@1.2.8/build/director.js"></script>
|
||||||
|
<!-- websocket sync between multiple tabs -->
|
||||||
|
<script src="/todos/iris-ws.js"></script>
|
||||||
|
<!-- -->
|
||||||
|
<style>
|
||||||
|
[v-cloak] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<section class="todoapp">
|
||||||
|
<header class="header">
|
||||||
|
<h1>todos</h1>
|
||||||
|
<input class="new-todo" autofocus autocomplete="off" placeholder="What needs to be done?" v-model="newTodo" @keyup.enter="addTodo">
|
||||||
|
</header>
|
||||||
|
<section class="main" v-show="todos.length" v-cloak>
|
||||||
|
<input class="toggle-all" type="checkbox" v-model="allDone">
|
||||||
|
<ul class="todo-list">
|
||||||
|
<li v-for="todo in filteredTodos" class="todo" :key="todo.id" :class="{ completed: todo.completed, editing: todo == editedTodo }">
|
||||||
|
<div class="view">
|
||||||
|
<!-- v-model="todo.completed" -->
|
||||||
|
<input class="toggle" type="checkbox" @click="completeTodo(todo)">
|
||||||
|
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
|
||||||
|
<button class="destroy" @click="removeTodo(todo)"></button>
|
||||||
|
</div>
|
||||||
|
<input class="edit" type="text" v-model="todo.title" v-todo-focus="todo == editedTodo" @blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)"
|
||||||
|
@keyup.esc="cancelEdit(todo)">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<footer class="footer" v-show="todos.length" v-cloak>
|
||||||
|
<span class="todo-count">
|
||||||
|
<strong>{{ remaining }}</strong> {{ remaining | pluralize }} left
|
||||||
|
</span>
|
||||||
|
<ul class="filters">
|
||||||
|
<li>
|
||||||
|
<a href="#/all" :class="{ selected: visibility == 'all' }">All</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#/active" :class="{ selected: visibility == 'active' }">Active</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#/completed" :class="{ selected: visibility == 'completed' }">Completed</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
|
||||||
|
Clear completed
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
<footer class="info">
|
||||||
|
<p>Double-click to edit a todo</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
### The server-side (iris)
|
### The server-side (iris)
|
||||||
|
|
||||||
|
Our view model.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// file: todo/item.go
|
||||||
|
package todo
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
SessionID string `json:"-"`
|
||||||
|
ID int64 `json:"id,omitempty"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Completed bool `json:"completed"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Our service.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// file: todo/service.go
|
||||||
|
package todo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
Get(owner string) []Item
|
||||||
|
Save(owner string, newItems []Item) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryService struct {
|
||||||
|
// key = session id, value the list of todo items that this session id has.
|
||||||
|
items map[string][]Item
|
||||||
|
// protected by locker for concurrent access.
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemoryService() *MemoryService {
|
||||||
|
return &MemoryService{
|
||||||
|
items: make(map[string][]Item, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryService) Get(sessionOwner string) []Item {
|
||||||
|
s.mu.RLock()
|
||||||
|
items := s.items[sessionOwner]
|
||||||
|
s.mu.RUnlock()
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemoryService) Save(sessionOwner string, newItems []Item) error {
|
||||||
|
var prevID int64
|
||||||
|
for i := range newItems {
|
||||||
|
if newItems[i].ID == 0 {
|
||||||
|
newItems[i].ID = prevID
|
||||||
|
prevID++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
s.items[sessionOwner] = newItems
|
||||||
|
s.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We are going to use some of the MVC functionalities of the iris web framework here but you can do the same with the standard API as well.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// file: controllers/todo_controller.go
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"vuejs-todo-mvc/src/todo"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
"github.com/kataras/iris/mvc"
|
||||||
|
"github.com/kataras/iris/sessions"
|
||||||
|
"github.com/kataras/iris/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TodoController is our TODO app's web controller.
|
||||||
|
type TodoController struct {
|
||||||
|
Service todo.Service
|
||||||
|
|
||||||
|
Session *sessions.Session
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeActivation called once before the server ran, and before
|
||||||
|
// the routes and dependencies binded.
|
||||||
|
// You can bind custom things to the controller, add new methods, add middleware,
|
||||||
|
// add dependencies to the struct or the method(s) and more.
|
||||||
|
func (c *TodoController) BeforeActivation(b mvc.BeforeActivation) {
|
||||||
|
// this could be binded to a controller's function input argument
|
||||||
|
// if any, or struct field if any:
|
||||||
|
b.Dependencies().Add(func(ctx iris.Context) (items []todo.Item) {
|
||||||
|
ctx.ReadJSON(&items)
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get handles the GET: /todos route.
|
||||||
|
func (c *TodoController) Get() []todo.Item {
|
||||||
|
return c.Service.Get(c.Session.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostItemResponse the response data that will be returned as json
|
||||||
|
// after a post save action of all todo items.
|
||||||
|
type PostItemResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyResponse = PostItemResponse{Success: false}
|
||||||
|
|
||||||
|
// Post handles the POST: /todos route.
|
||||||
|
func (c *TodoController) Post(newItems []todo.Item) PostItemResponse {
|
||||||
|
if err := c.Service.Save(c.Session.ID(), newItems); err != nil {
|
||||||
|
return emptyResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return PostItemResponse{Success: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TodoController) GetSync(conn websocket.Connection) {
|
||||||
|
conn.Join(c.Session.ID())
|
||||||
|
conn.On("save", func() { // "save" event from client.
|
||||||
|
conn.To(c.Session.ID()).Emit("saved", nil) // fire a "saved" event to the rest of the clients w.
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.Wait()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And finally our main application's endpoint.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// file: web/main.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"vuejs-todo-mvc/src/todo"
|
||||||
|
"vuejs-todo-mvc/src/web/controllers"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
"github.com/kataras/iris/sessions"
|
||||||
|
"github.com/kataras/iris/websocket"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/mvc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
|
||||||
|
// serve our app in public, public folder
|
||||||
|
// contains the client-side vue.js application,
|
||||||
|
// no need for any server-side template here,
|
||||||
|
// actually if you're going to just use vue without any
|
||||||
|
// back-end services, you can just stop afer this line and start the server.
|
||||||
|
app.StaticWeb("/", "./public")
|
||||||
|
|
||||||
|
// configure the http sessions.
|
||||||
|
sess := sessions.New(sessions.Config{
|
||||||
|
Cookie: "iris_session",
|
||||||
|
})
|
||||||
|
|
||||||
|
// configure the websocket server.
|
||||||
|
ws := websocket.New(websocket.Config{})
|
||||||
|
|
||||||
|
// create a sub router and register the client-side library for the iris websockets,
|
||||||
|
// you could skip it but iris websockets supports socket.io-like API.
|
||||||
|
todosRouter := app.Party("/todos")
|
||||||
|
// http://localhost:8080/todos/iris-ws.js
|
||||||
|
// serve the javascript client library to communicate with
|
||||||
|
// the iris high level websocket event system.
|
||||||
|
todosRouter.Any("/iris-ws.js", websocket.ClientHandler())
|
||||||
|
|
||||||
|
// create our mvc application targeted to /todos relative sub path.
|
||||||
|
todosApp := mvc.New(todosRouter)
|
||||||
|
|
||||||
|
// any dependencies bindings here...
|
||||||
|
todosApp.Register(
|
||||||
|
todo.NewMemoryService(),
|
||||||
|
sess.Start,
|
||||||
|
ws.Upgrade,
|
||||||
|
)
|
||||||
|
|
||||||
|
// controllers registration here...
|
||||||
|
todosApp.Handle(new(controllers.TodoController))
|
||||||
|
|
||||||
|
// start the web server at http://localhost:8080
|
||||||
|
app.Run(iris.Addr(":8080"), iris.WithoutVersionChecker)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the Iris web server you've just created by executing `go run main.go` from your current path (%GOPATH%/src/%your_folder%/web/).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ go run main.go
|
||||||
|
Now listening on: http://localhost:8080
|
||||||
|
Application Started. Press CTRL+C to shut down.
|
||||||
|
_
|
||||||
|
```
|
||||||
|
|
||||||
|
Open one or more browser tabs at: http://localhost:8080 and have fun!
|
||||||
|
|
||||||
|
![](screen.png)
|
||||||
|
|
||||||
|
### Download the Source Code
|
||||||
|
|
||||||
|
The whole project, all the files you saw in this article are located at: https://github.com/kataras/iris/tree/master/_examples/tutorial/vuejs-todo-mvc
|
||||||
|
|
||||||
|
## Thank you, once again
|
||||||
|
|
||||||
|
Happy new year and thank you for your pattience, once again:) Don't hesitate to post any questions and provide feedback(I'm very active dev therefore you will be heard here!)
|
||||||
|
|
||||||
|
Don't forget to check out my medium profile and twitter as well, I'm posting some (useful) stuff there too:)
|
||||||
|
|
||||||
|
- https://medium.com/@kataras
|
||||||
|
- https://twitter.com/MakisMaropoulos
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
https://vuejs.org/v2/examples/todomvc.html (using browser's local storage)
|
https://vuejs.org/v2/examples/todomvc.html (using browser's local storage)
|
||||||
|
|
||||||
|
https://github.com/kataras/iris/tree/master/_examples/mvc (mvc examples and features overview repository)
|
BIN
_examples/tutorial/vuejs-todo-mvc/screen.png
Normal file
BIN
_examples/tutorial/vuejs-todo-mvc/screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 101 KiB |
|
@ -52,6 +52,10 @@ func (c *TodoController) Post(newItems []todo.Item) PostItemResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TodoController) GetSync(conn websocket.Connection) {
|
func (c *TodoController) GetSync(conn websocket.Connection) {
|
||||||
|
// join to the session in order to send "saved"
|
||||||
|
// events only to a single user, that means
|
||||||
|
// that if user has opened more than one browser window/tab
|
||||||
|
// of the same session then the changes will be reflected to one another.
|
||||||
conn.Join(c.Session.ID())
|
conn.Join(c.Session.ID())
|
||||||
conn.On("save", func() { // "save" event from client.
|
conn.On("save", func() { // "save" event from client.
|
||||||
conn.To(c.Session.ID()).Emit("saved", nil) // fire a "saved" event to the rest of the clients w.
|
conn.To(c.Session.ID()).Emit("saved", nil) // fire a "saved" event to the rest of the clients w.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Full spec-compliant TodoMVC with Iris
|
// Full spec-compliant TodoMVC with Iris
|
||||||
// and hash-based routing in ~120 effective lines of JavaScript.
|
// and hash-based routing in ~200 effective lines of JavaScript.
|
||||||
|
|
||||||
var socket = new Ws("ws://localhost:8080/todos/sync");
|
var socket = new Ws("ws://localhost:8080/todos/sync");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user