Iris is not just another framework
thegameofcode
January 2014
Iris
Features
Strong object oriented coding and file system organization
Presenter-View-Resource pattern
Easy and fast templating engine
One page navigation using Hash-URL
100% Client multilanguage support
Crossbrowser: Chrome, Firefox e Internet Explorer
Light and fast: 18 KB
Open source: New BSD License
This is an example of a
Todo App
using Iris
With a
Todo App
you can add
tasks
and check them when completed.
Preparing
the enviroment
The first step is to create an
index.html
page and an
init.js
script
Let's start with the
index.html
page
Play
<!DOCTYPE HTML> <html> <head> <meta charset='utf-8'> <title>Todo list</title> <script src='|jquery.min.js'></script> <script src='iris.js'></script> </head> <body> </body> </html> @@@ tooltip: {text: 'JQuery Dependency', wait: 2000} ::: Add JQuery Dependency run: {command: 'goLineDown', times: 2, beforeDelay: 0} run: {command: 'goLineStart', beforeDelay: 0} run: {command: 'goWordRight', times: 3, beforeDelay: 0} tooltip: {text: 'Iris Library', wait: 2000} ::: Add Iris Library Dependency run: {command: 'goLineDown', times: 2, beforeDelay: 0} type: {text: ""} ::: Add Init Script run: {command: 'goWordLeft', times: 2, beforeDelay: 0} tooltip: {text: 'Init Script'} run: {command: 'goDocEnd', times: 1, beforeDelay: 0} run: {command: 'goLineUp', times: 2, beforeDelay: 0} run: {command: 'goLineStart', beforeDelay: 0} tooltip: {text: 'Empty Body'} ::: Leave the body without content
The
init.js
script
Play
| @@@ type: {text: "iris.path = {\n};"} ::: Define de iris.path object tooltip: {text: 'iris.path defines all Iris components', pos: "0:4"} run: { command: "goLineUp", beforeDelay: 0 } run: { command: "goLineEnd", beforeDelay: 0 } type: {text: "\n welcome : 'app/screen/welcome.js',\n"} ::: Define the Welcome Screen type: {text: " welcome_tmpl : 'app/screen/welcome.html'"} tooltip: {text: 'Each Screen has two elements: the presenter and the template', pos: "1:4"} type: {text: ",\n todo_item : 'app/ui/todo_item.js',\n"} ::: Define the Todo UI type: {text: " todo_item_tmpl : 'app/ui/todo_item.html'"} tooltip: {text: 'The same as every UI component', pos: "3:4"} run: { command: "goDocEnd", beforeDelay: 0 } run: { command: "goLineEnd", beforeDelay: 0 } type: {text: "\n\n$(document).ready(function() {"} ::: Entry point type: {text: "\n iris.welcome(iris.path.welcome);"} type: {text: "\n});"} tooltip: {text: 'Calling the iris.welcome() method', pos: "8:4"}
The
Welcome
Screen
The template of
Welcome Screen
(welcome.html)
Play
| @@@ type: {text: "
\n\n
"} ::: Empty Div tooltip: {text: 'Start with an empty \"div\"', pos: "0:5"} run: { command: "goLineUp", beforeDelay: 0 } run: { command: "goLineStart", beforeDelay: 0 } type: {text: "
"} ::: TextBox to add tasks tooltip: {text: 'The \"data-id\" attribute is used to handle the component in the presenter', pos: "1:12"} type: {text: "\n
\n Mark all as complete"} ::: To check all taks type: {text: "\n
"} type: {text: "\n
"} tooltip: {text: 'Checkbox to check/uncheck all tasks', pos: "4:20"} type: {text: "\n
"} ::: To contains the tasks tooltip: {text: 'The list will contain the Todo_item UIs', pos: "5:10"} type: {text: "\n
"} ::: To filter tasks type: {text: "\n
All
", delay: 20} type: {text: "\n
Active
", delay: 20} type: {text: "\n
Completed
", delay: 20} type: {text: "\n
"} tooltip: {text: 'When click, It will navigate to Welcome Screen with a \"filter\" parameter', pos: "8:10", wait: 4000} type: {text: "\n
"} ::: To clear tasks completed tooltip: {text: 'To clear tasks completed', pos: "12:10"}
The presenter of
Welcome Screen
(welcome.js)
Play
| @@@ type: {text: "//In welcome.js"} ::: Calling iris.screen() type: {text: "\niris.screen( ... , ... );"} tooltip: {text: 'iris.screen receives two parameters', pos: "1:4"} select: {from: "1:12", to: "1:17"} type: {text: " function(self) {\n}"} tooltip: {text: 'The first is the lifecycle function (more later)', pos: "1:15", wait: 4000} select: {from: "2:3", to: "2:7"} type: {text: "iris.path.welcome"} tooltip: {text: 'The second is the corresponding value in the iris.path variable', pos: "2:7", wait: 4000} prompt: {title: "Who is the \"self\" variable?", typeDelay: 2000, hideDelay: 4000, text: "It's the Screen cretated by Iris."} prompt: {title: "What is the second parameter for?", typeDelay: 2000, hideDelay: 4000, text: "For Minification and debugging."} moveTo: {pos: "1:29"} type: {delay: 10, text: "\n\n var newTodo, setAllBtn, clearCompletedBtn, todoLeft = 0, numTodos = 0, completeAllShowed = false, filters;\n"} type: {text: "\n self.create = function() {\n };\n"} ::: Defining the lifecycle methods type: {text: "\n self.awake = function() {\n };\n"} tooltip: {text: '\"self.create()\" and \"self.awake()\" are two methods of the component lifecycle', pos: "5:13", wait: 4000} tooltip: {text: '\"self.create()\" is called only once when the component is created', pos: "5:7", wait: 4000} tooltip: {text: '\"self.awake()\" is called each time the component is activated', pos: "8:7", wait: 4000} moveTo: {pos: "8:23"} type: {text: "params"} tooltip: {text: '\"self.awake()\" can receive queryString parameters', pos: "8:23"} moveTo: {pos: "6:0"} type: {text: "\n\n"} run: { command: "goLineUp", times:2, beforeDelay: 0 } type: {text: "\n self.tmpl(iris.path.welcome_tmpl);\n"} ::: Calling iris.template() tooltip: {text: '\"self.tmpl()\" loads the template in the DOM. In this case, in the \"body\" element', pos: "7:7"} type: {text: "\n newTodo = self.get('new-todo').on('keyup', newTodoOnKeyUp);"} ::: Calling self.get() tooltip: {text: '\"self.get()\" retrieves the JQuery object from the template whose \"data-id\" matches with the parameter', pos: "9:17"} type: {delay:10, text: "\n setAllBtn = self.get('toggle-all').on('click', setAll);"} ::: Assigning JQuery events handlers type: {delay:10, text: "\n clearCompletedBtn = self.get('clear-completed').on('click', clearCompleted);"} type: {delay:10, text: "\n filters = self.get('filters').on('click', onFiltersClick);"} tooltip: {text: 'Now we assing the JQuery event handlers', pos: "9:8"} type: {text: "\n\n self.on('destroy-todo', onDestroyTodo);"} ::: Assigning Iris events handlers type: {text: "\n self.on('toggle-todo', onToggleTodo);"} tooltip: {text: 'With \"self.on()\" the component can listening for an Iris event', pos: "14:6"} moveTo: {pos: "20:0"} ::: Notifying filter tasks type: {text: "\n"} run: { command: "goLineUp", beforeDelay: 0 } type: {text: " if ( params !== undefined && params.hasOwnProperty('filter') ) {\n }"} tooltip: {text: 'Here, we check if there is a \"filter\" parameter in the QueryString', pos: "20:10"} moveTo: {pos: "21:0"} type: {text: "\n"} run: { command: "goLineUp", beforeDelay: 0 } type: {text: " self.notify('filter-todos', params.filter);"} tooltip: {text: 'With \"self.notify\" whe can notify an Iris event passing some parameters to the handler', pos: "20:10"} moveTo: {pos: "16:0"} ::: Rendering components type: {text: "\n render();"} moveTo: {pos: "26:0"} type: {delay: 1, text: "\n function render () {\n ...\n }\n\n"} tooltip: {wait: 4000, text: 'The render function just hides o shows the DOM elements according to the app state', pos: '27:8'}
Adding
a Todo Item UI
At the next few steps we are going to explain how to add a Todo Item.
Let's start with the
Todo Item UI template
Play
| @@@ type: {text: "
\n
"} ::: Li Element tooltip: {wait: 4000, text: 'The Todo Items will be added in a list defined in the Welcome Screen, as you surely remember', pos: "0:4"} run: { command: "goLineUp", beforeDelay: 0 } type: {text: "\n
"} ::: Checkbox Element tooltip: {text: 'A checkbox to allow us to check or uncheck the task', pos: "1:4"} type: {text: "\n
"} ::: Label Element tooltip: {text: 'A label to show the text of the task', pos: "2:4"} type: {text: "\n
"} ::: Button Element tooltip: {text: 'A button to destroy the task', pos: "3:4"} type: {text: "\n
"} ::: Textbox Element tooltip: {text: 'A textbox to edit the task', pos: "4:4"} tooltip: {text: 'The \"data-model\" attribute allows you to pass parameters from the presenter (more later)', pos: "4:27"}
The
Todo Item UI presenter
(todo_item.js)
Play
iris.ui(function (self) { | self.create = function() { self.tmpl(iris.path.todo_item_tmpl); self.get("check").on("click", toggle); self.get("destroy").on("click", destroyTodo); self.on("set-all-todos", onSetAllTodos); self.on("clear-completed", onClearCompleted); self.on("filter-todos", onFilter); self.get().on("dblclick", edit); self.get("text").on("change", onTextBlur); self.get().hide().fadeIn("slow"); }; ... },iris.path.todo_item); @@@ tooltip: {wait: 4000, text: 'We started in a similar way as the Welcome Screen', pos: "0:4"} ::: Intro type: {text: "\n self.settings({\n completed : false\n }"} ::: self.settings tooltip: {wait: 4000, text: '\"self.settings\" allows us to store a JavaScript object in the component', pos: "3:5"} moveTo: {pos: "9:0"} type: {text: " self.tmplMode(self.APPEND);"} ::: self.tmplMode tooltip: {wait: 4000, text: 'With \"self.tmplMode(self.APPEND)\" we are telling Iris that several UIs can be stored in the container (ul)', pos: "9:7"}
Creating
a Todo Item
Play
//In welcome.js iris.screen(function(self) { self.create = function() { ... newTodo = self.get("new-todo").on("keyup", newTodoOnKeyUp); ... } ... }, iris.path.welcome); @@@ tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} ::: Intro moveTo: {pos: "7:0"} type: {text: "\n"} run: { command: "goLineUp", beforeDelay: 0 } type: {text: " function newTodoOnKeyUp (e) {\n if ( e.keyCode === 13 ) {\n var ui = self.ui('todo-list', iris.path.todo_item);\n ui.inflate({ text: newTodo.val() });\n }\n }\n"} tooltip: {wait: 2000, text: 'The \"self.ui()\" method creates a new UI', pos: "9:17"} ::: self.ui tooltip: {wait: 3000, text: 'It takes two mandatory parameters and two optional', pos: "9:17"} tooltip: {wait: 3000, text: 'The first mandatory parameter is the UI container', pos: "9:25"} tooltip: {wait: 3000, text: 'The other mandatory parameter is the UI presenter file', pos: "9:42"} tooltip: {wait: 6000, text: 'The optional parameters are the settings object pass to the UI and the template mode, of which we already talked about before', pos: "9:17"} tooltip: {wait: 4000, text: 'The \"self.ui()\" method returns the UI created by iris', pos: "9:7"} ::: ui.inflate tooltip: {wait: 4000, text: 'The \"ui.inflate()\" method allows to pass a JavaScript object to the template', pos: "10:6"} tooltip: {wait: 8000, text: 'Iris will look for those DOM elements of the component whose attribute \"data-model\" matches any attribute of the object passed to the method ...', pos: "10:6"} tooltip: {wait: 6000, text: '... And It will replace their contents with the new value', pos: "10:6"} tooltip: {wait: 8000, text: 'In this case, the content of the DOM elements with \"data-model\" attribute equal to \"text\" will be the task name introduced by the user', pos: "10:6"}
Checking
Todo Items
At these few steps we are going to explain how to
check/uncheck
Todo Items.
Checking/Unchecking a
single
Todo Item
Play
//In todo_item.js iris.ui(function (self) { ... self.create = function() { ... self.get("check").on("click", toggle); ... }; },iris.path.todo_item); @@@ tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} ::: Intro moveTo: {pos: "8:0"} type: {text: " function toggle () {\n self.setting('completed', !self.setting('completed'));\n self.get().toggleClass('completed');\n self.get('check').attr('checked', self.setting('completed'));\n self.notify('toggle-todo', self.setting('completed'));\n }"} ::: self.setting() tooltip: {wait: 4000, text: 'Almost all of this we have already explained', pos: "8:10"} tooltip: {wait: 4000, text: 'The \"self.setting()\" method is similar to \"self.settings()\" and allows us to store a value in the component ...', pos: "9:12"} tooltip: {wait: 4000, text: '... or retrieve it', pos: "9:35"}
Checking/Unchecking
all
Todo Items
Play
//In welcome.js iris.screen(function(self) { self.create = function() { ... setAllBtn = self.get('toggle-all').on('click', setAll); ... } ... }, iris.path.welcome); @@@ tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} ::: Intro moveTo: {pos: "7:0"} type: {text: "\n"} run: { command: "goLineUp", beforeDelay: 0 } type: {text: " function setAll () {\n self.notify('set-all-todos', completeAllShowed);\n }"} tooltip: {wait: 4000, text: 'We just notify the event', pos: "8:10"} tooltip: {wait: 4000, text: 'The \"completeAllShowed\" variable controls whether to check or uncheck the todos', pos: "8:40"} run: {command: "selectAll", beforeDelay: 0} type: {text: "//In todo_item.js"} ::: todo_item.js type: {text: "\niris.ui(function(self) {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.create = function() {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.on('set-all-todos', onSetAllTodos);", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n }", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n}, iris.path.todo_item);", delay: 10} tooltip: {wait: 4000, text: 'We assign the event handler function', pos: "5:35"} moveTo: {pos: "8:0"} type: {text: "\n", delay: 10} run: {command: "goLineUp", beforeDelay: 0} type: {text: "\n function onSetAllTodos (isCheck) {"} type: {text: "\n if ( self.setting('completed') !== isCheck ) {"} type: {text: "\n toggle();"} type: {text: "\n }"} type: {text: "\n }"} tooltip: {wait: 4000, text: 'If the todo does not have the correct state, we call to the \"toggle()\" function previously explained', pos: "9:15"}
Deleting
Todo Items
At this step we are going to explain how to
delete
Todo Items.
Deleting a
single
Todo Item
Play
//In todo_item.js iris.ui(function (self) { ... self.create = function() { ... self.get('destroy').on('click', destroyTodo); ... }; }, iris.path.todo_item); @@@ tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} ::: Intro moveTo: {pos: "8:0"} type: {text: " function destroyTodo () {\n self.notify('destroy-todo', self);\n }"} ::: self.notify() tooltip: {wait: 4000, text: 'We just notify the event ...', pos: "8:10"} tooltip: {wait: 4000, text: '... passing the UI component itself', pos: "9:32"} run: {command: "selectAll", beforeDelay: 0} type: {text: "//In welcome.js"} ::: self.destroyUI() type: {text: "\niris.screen(function(self) {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.create = function() {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.on('destroy-todo', onDestroyTodo);", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n }", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n}, iris.path.welcome);", delay: 10} tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} moveTo: {pos: "8:0"} type: {text: "\n", delay: 10} run: {command: "goLineUp", beforeDelay: 0} type: {text: "\n function onDestroyTodo (todo) {"} type: {text: "\n ..."} type: {text: "\n self.destroyUI(todo);"} type: {text: "\n ..."} type: {text: "\n }"} tooltip: {wait: 4000, text: 'The \"destroyUI()\" method destroys the UI passed as a parameter', pos: "11:9"} run: {command: "selectAll", beforeDelay: 0} type: {text: "//In todo_item.js"} ::: Alternative way tooltip: {wait: 4000, text: 'An alternative way and much simpler to do the same is just ...', pos: "0:4"} type: {text: "\niris.ui(function(self) {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.create = function() {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.get('destroy').on('click', destroyTodo);", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n }", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n}, iris.path.todo_item);", delay: 10} moveTo: {pos: "8:0"} type: {text: "\n", delay: 10} run: {command: "goLineUp", beforeDelay: 0} type: {text: "\n function destroyTodo() {"} type: {text: "\n ..."} type: {text: "\n self.destroyUI();"} type: {text: "\n ..."} type: {text: "\n }"} tooltip: {wait: 4000, text: 'The \"destroyUI()\" method with no arguments destroys the UI itself', pos: "11:9"}
Deleting
all completed
Todo Items
Play
//In welcome.js iris.screen(function(self) { self.create = function() { ... self.get('clear-completed').on('click', clearCompleted); ... } ... }, iris.path.welcome); @@@ tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} ::: Intro moveTo: {pos: "7:0"} type: {text: "\n"} run: { command: "goLineUp", beforeDelay: 0 } type: {text: " function clearCompleted () {\n self.notify('clear-completed');\n }"} tooltip: {wait: 4000, text: 'We just notify the event', pos: "8:10"} run: {command: "selectAll", beforeDelay: 0} type: {text: "//In todo_item.js"} ::: todo_item.js type: {text: "\niris.ui(function(self) {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.create = function() {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.on('clear-completed', onClearCompleted);", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n }", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n}, iris.path.todo_item);", delay: 10} tooltip: {wait: 4000, text: 'We assign the event handler function', pos: "5:35"} moveTo: {pos: "8:0"} type: {text: "\n", delay: 10} run: {command: "goLineUp", beforeDelay: 0} type: {text: "\n function onClearCompleted () {"} type: {text: "\n if ( self.setting('completed') ) {"} type: {text: "\n destroyTodo();"} type: {text: "\n }"} type: {text: "\n }"} tooltip: {wait: 4000, text: 'If the todo is completed, we call to the \destroyTodo()\" function previously explained', pos: "9:9"}
Editing
a Todo Item
Play
//In todo_item.js iris.ui(function (self) { ... self.create = function() { ... self.get().on('dblclick', edit); ... }; }, iris.path.todo_item); @@@ tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} ::: Intro moveTo: {pos: "8:0"} type: {text: " function edit () {\n self.get().addClass('editing');\n self.get('text').select();\n }"} tooltip: {wait: 4000, text: 'Nothing new here ...', pos: "8:10"} type: {text: "\n function onTextBlur () {\n self.get().removeClass('editing');\n self.inflate({text:self.get('text').val()});\n }"} ::: self.inflate tooltip: {wait: 4000, text: 'With the \"self.inflate\" method we update the template as we explained previously', pos: "14:10"}
Filtering
todos
Play
...
All
Active
Completed
...
@@@ tooltip: {wait: 4000, text: 'Remember...', pos: "0:4"} ::: Intro tooltip: {wait: 4000, text: 'When we click on a link, Iris will call the \"awake\" method of the Welcome Screen', pos: "4:20"} run: {command: "selectAll", beforeDelay: 0} type: {text: "//In welcome.js"} ::: self.awake() type: {text: "\niris.screen(function(self) {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.awake = function(params) {", delay: 10} type: {text: "\n if ( params !== undefined && params.hasOwnProperty('filter') ) {", delay: 10} type: {text: "\n self.notify('filter-todos', params.filter);", delay: 10} type: {text: "\n }", delay: 10} type: {text: "\n }", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n}, iris.path.welcome);", delay: 10} tooltip: {wait: 4000, text: 'We just notify the event passing the filter value', pos: "5:10"} tooltip: {wait: 4000, text: 'The filter value is retrieved from the QueryString passed to the \"awake\" method', pos: "5:37"} run: {command: "selectAll", beforeDelay: 0} type: {text: "//In todo_item.js"} ::: todo_item.js type: {text: "\niris.ui(function(self) {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.create = function() {", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n self.on('filter-todos', onFilter);", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n }", delay: 10} type: {text: "\n ...", delay: 10} type: {text: "\n}, iris.path.todo_item);", delay: 10} moveTo: {pos: "8:0"} type: {text: "\n", delay: 10} run: {command: "goLineUp", beforeDelay: 0} type: {text: "\n function onFilter (filter) {"} type: {text: "\n ..."} type: {text: "\n }"} tooltip: {wait: 4000, text: 'The \"onFilter()\" hides or shows the todo acording to the filter value and the completed state', pos: "9:9"}