OOUI ToDo App Tutorial - Part 1

# OOUI Basics: Part 1

## ToDo List App

In this tutorial we'll walk through creating a simple JavaScript ToDo app with the OOUI library, which was created by the Wikimedia Foundation. OOUI has a lot of potential for super-powerful JavaScript applications in your browser — so we will start small and grow as we go, hopefully giving you a taste of the library and its concepts.

## Project Files

After installing OOUI in our project directory, we will start our project by creating these three files:

- The JavaScript initialization file: create a directory named `assets/` and inside it create a file and name it `init.js`. This will be our initialization script.
- The CSS file: create `todo.css` and place it in your main project directory—we will use this file later for all of our custom CSS styling.
- The HTML file: create `index.html` and place it in your main project directory. This is the first file we'll be populating with code.

## Add to the Project

We will now attach the CSS and JavaScript files, along with the OOUI files, to our HTML page. This is how our basic page should look:

    <!doctype html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title>ToDo OOUI</title>
    		<meta name="description" content="A demo ToDo app made with OOUI">
    		<meta name="viewport" content="width=device-width, initial-scale=1">

    		<!-- jQuery -->
    		<script src="node_modules/jquery/dist/jquery.min.js"></script>
    		<!-- OOjs -->
    		<script src="node_modules/oojs/dist/oojs.jquery.min.js"></script>
    		<!-- OOUI -->
    		<script src="node_modules/oojs-ui/dist/oojs-ui.min.js"></script>
    		<!-- OOUI theme -->
    		<script src="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.js"></script>
    		<link rel="stylesheet" href="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.css">

    		<!-- ToDo app custom -->
    		<link rel="stylesheet" href="todo.css">
    		<script src="assets/init.js"></script>
    	</head>
    	<body>
    		<div class="wrapper">
    			<h1>Demo ToDo app with OOUI</h1>
    		</div>
    	</body>
    </html>

We will use the `wrapper` div element to inject our application into.

## Building the Base

So now that we have our basic page, we need to start writing code. Our ToDo app should have two main pieces to start with: An input to add a new item, and a list displaying all items that have been added.

For the input We will need to use an `OO.ui.TextInputWidget`.

And since a ToDo list allows us to show a list of items that can be selected, for the list itself we will use an `OO.ui.SelectWidget` (You can see a demo of all OOUI widgets in the demo page).

Here is how you can add a `SelectWidget` and an `TextInputWidget` to your `assets/init.js` file:

    $( document ).ready( function () {
    	var input = new OO.ui.TextInputWidget(),
    		list = new OO.ui.SelectWidget();

    	// Append to the wrapper
    	$( '.wrapper' ).append(
    		input.$element,
    		list.$element
    	);
    } );

Let's break this up and see what we did there. One of OOUI's principles is to separate the data from the UI, so each one of the widgets we're creating is first and foremost an object that is separate from the DOM. That object contains the DOM element itself in the `$element` property, which we use to attach to the document, but the behavior itself (as we will soon see) is done through the general OOUI object.

So in short, we created two widgets—a text input and a select widget—and then attached their `$element` to the document. If you load your page, it should have the title and an input. The list is invisible because we don't have a way to add elements to it yet—so let's do that now.

## Adding Items to the List

We have our input, and we have the list, and now we need to connect them. `OO.ui.TextInputWidget` emits several events. One of them is simply "enter" when the Enter key is pressed (You can see all events in the documentation). Let's make our input add an item to the list when we hit the "enter" key. Since the list is an `OO.ui.SelectWidget` we should add into it an `OO.ui.OptionWidget`.

    // Respond to 'enter' keypress
    input.on( 'enter', function () {
    	// Add the item
    	list.addItems( [
    		new OO.ui.OptionWidget( {
    			data: input.getValue(),
    			label: input.getValue()
    		} )
    	] );
    } );

That would add an item to the list. Try it out! Add a ToDo item and click Enter in the demo box below:

## Add Highlights for Hover and Select Indication

You can make your ToDo list items change color to indicate an item has been hovered or selected. We'll achieve this effect by overriding existing selectors. Add the following code to your `todo.css` file:

    .oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected {
    	background-color: #80ccff;
    }

    .oo-ui-optionWidget-highlighted {
    	background-color: #b9e3ff;
    }

Now try it out:

## Unique Values

But what happens if we try to add an item that already exists in the list? Let's add a condition that checks whether the item exists first before it is added. Let's also prevent adding an empty input.

Update your `assets/init.js` file to look like this:

    // Respond to 'enter' keypress
    input.on( 'enter', function () {
    	// Check for duplicates and prevent empty input
    	if ( list.findItemFromData( input.getValue() ) ||
    			input.getValue() === '' ) {
    		input.$element.addClass( 'todo-error' );
    		return;
    	}
    	input.$element.removeClass( 'todo-error' );

    	// Add the item
    	list.addItems( [
    		new OO.ui.OptionWidget( {
    			data: input.getValue(),
    			label: input.getValue()
    		} )
    	] );
    } );

Now we can only add unique items to this list. When an illegal value is added (an empty input or an existing item), we attach the class `.todo-error` to the input.

For it to actually show something, we need to define it in our CSS file. Add this to your `todo.css` file:

    .todo-error input {
    	background-color: #ff9696;
    }

Let's try out a live demo of this version, and see if it behaves as expected:

It works! Now, let's add a little bit of extra flair to the app.

## More Custom Styling

Let's make sure that our list and our input are styled a bit better, and add a placeholder test to the input. Let's go back to the piece in our `init.js` file where we created the widgets, and add configuration to both:

    $( document ).ready( function () {
    	var input = new OO.ui.TextInputWidget( {
    			placeholder: 'Add a ToDo item'
    		} ),
    		list = new OO.ui.SelectWidget( {
    			classes: [ 'todo-list' ]
    		} );

    	// code continues...
    } );

The above configuration adds a CSS class to the list widget and a placeholder to the text input widget.

In the part of our `init.js` where we've added items to the list, we can use `input.setValue( '' );` to clear the input each time an item is added.

    // ...code

    		list.addItems( [
    		new OO.ui.OptionWidget( {
    			data: input.getValue(),
    			label: input.getValue()
    		} )
    	] );
    	input.setValue( '' );
    } );

We can now go edit our `todo.css` stylesheet. Notice that we can also style the underlying objects, which (for now) we will do by calling their oo-ui-style class, similarly to what we did in the Highlights section.

    .wrapper {
    	width: 60%;
    	margin-left: auto;
    	margin-right: auto;
    }

    .todo-list .oo-ui-optionWidget {
    	border-bottom: 1px solid #666;
    }

One last thing: let's add in some padding to our CSS.

    .oo-ui-inputWidget {
    	margin-bottom: 0.5em;
    }

    .todo-list .oo-ui-labelElement-label {
    	margin-left: 0.25em;
    }

    .oo-ui-labelElement .oo-ui-optionWidget {
    	padding: 0.25em;
    }

That's it. We now have a basic ToDo app. Yay!

Reload your own app and look at your wonderful result. Here is our final demo:

For your convenience, we included the complete code below.

## The Complete Code

Here's the full code of our `assets/init.js` file:

    $( document ).ready( function () {
    	var input = new OO.ui.TextInputWidget( {
    			placeholder: 'Add a ToDo item'
    		} ),
    		list = new OO.ui.SelectWidget( {
    			classes: [ 'todo-list' ]
    		} );

    	// Respond to 'enter' keypress
    	input.on( 'enter', function () {
    		// Check for duplicates and prevent empty input
    		if ( list.findItemFromData( input.getValue() ) ||
    				input.getValue() === '' ) {
    			input.$element.addClass( 'todo-error' );
    			return;
    		}
    		input.$element.removeClass( 'todo-error' );

    		// Add the item
    		list.addItems( [
    			new OO.ui.OptionWidget( {
    				data: input.getValue(),
    				label: input.getValue()
    			} )
    		] );
    		input.setValue( '' );
    	} );

    	// Append the app widgets
    	$( '.wrapper' ).append(
    		input.$element,
    		list.$element
    	);
    } );

Here's the full code of our `index.html` file:

    <!doctype html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title>ToDo OOUI</title>
    		<meta name="description" content="A demo ToDo app made with OOUI">
    		<meta name="viewport" content="width=device-width, initial-scale=1">

    		<!-- jQuery -->
    		<script src="node_modules/jquery/dist/jquery.min.js"></script>
    		<!-- OOjs -->
    		<script src="node_modules/oojs/dist/oojs.jquery.min.js"></script>
    		<!-- OOUI -->
    		<script src="node_modules/oojs-ui/dist/oojs-ui.min.js"></script>
    		<!-- OOUI theme -->
    		<script src="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.js"></script>
    		<link rel="stylesheet" href="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.css">

    		<!-- ToDo app custom -->
    		<link rel="stylesheet" href="todo.css">
    		<script src="assets/init.js"></script>
    	</head>
    	<body>
    		<div class="wrapper">
    			<h1>Demo ToDo app with OOUI</h1>
    		</div>
    	</body>
    </html>

Here's the full code of our `todo.css` file:

    .todo-error input {
    	background-color: #ff9696;
    }

    .wrapper {
    	width: 60%;
    	margin-left: auto;
    	margin-right: auto;
    }

    .todo-list .oo-ui-optionWidget {
    	border-bottom: 1px solid #666;
    }

    .oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected {
    	background-color: #80ccff;
    }

    .oo-ui-optionWidget-highlighted {
    	background-color: #b9e3ff;
    }

    .oo-ui-inputWidget {
    	margin-bottom: 0.5em;
    }

    .todo-list .oo-ui-labelElement-label {
    	margin-left: 0.25em;
    }

    .oo-ui-labelElement .oo-ui-optionWidget {
    	padding: 0.25em;
    }