7
\$\begingroup\$

I am just beginning to learn JavaScript and although I feel I am starting to grasp the language, I don't feel I know how to structure an app properly. I decided to start small and build a "TODO"-app using a modular pattern.

I am aware that I should use more validations, there are unused functions, and there may be a missing semicolon here and there. My main focus on this thing was to see if I could structure an app in a good way.

I would love some feedback on my setup. Is this a proper way to set up such a small application? Am I missing something completely? Am I using the modular pattern correctly?

Generally, have I gotten the gist of it?

(function todoApp() {

var storage = (function () {
    var _db,
        todos,
        addTodo,
        removeTodo,
        updateTodo,
        getAllTodos,
        save;

    if (!window.localStorage.todoApp) {
      window.localStorage.todoApp = JSON.stringify({todos: []});
    }

    _db = JSON.parse(window.localStorage.todoApp);


    addTodo = function addTodo(todo) {
      var id = Date.now().toString();
      _db.todos.push({id: id, todo: todo});
      save();
    };

    removeTodo = function removeTodo(id) {
      _db.todos.every(function (c, i) {
        if (c.hasOwnProperty("id") && c.id == id) {
          _db.todos.splice(i, 1);
          save();
          return false;
        }
      });
    };

    updateTodo = function updateTodo(id, todo) {
      _db.todos.every(function (c, i) {
        if (c.hasOwnProperty("id") && c.id == id) {
          _db.todos[i].todo = todo;
          save();
          return false;
        }
      });
    };

    getAllTodos = function getAllTodos() {
      return _db.todos;
    };

    save = function save() {
      window.localStorage.todoApp = JSON.stringify(_db);
    };

    return {
      addTodo: addTodo,
      removeTodo: removeTodo,
      updateTodo: updateTodo,
      getAllTodos: getAllTodos
    };

  })();

  var view = (function (storage) {
    var $todos = $("[rel='jstodo-container'] > [rel='jstodo-todos']"),
        todos = storage.getAllTodos(),
        render;

    render = function render() {
        $todos.html("");
        todos.forEach(function (c) {
          $todos.append("<li rel='" + c.id + "'>" + c.todo + "</li>");
        });
    };

    return {
      render: render
    };
  })(storage);


  view.render();

  $("[rel='jstodo-container'] > [rel='jstodo-submit']").on("click", function () {
    storage.addTodo($("[rel='jstodo-container'] > [rel='jstodo-input']").val());
    view.render();
  });
})();
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
  <title>JS Bin</title>
</head>
<body>
  <div rel="jstodo-container">
    <h1>Todos: </h1>
    <input type="text" rel="jstodo-input"/>
    <button rel="jstodo-submit">Legg til!</button>
    <ul rel="jstodo-todos"></ul>
  </div>
</body>
</html>

Here is a jsbin of the project (in case the Stack Snippet has permission issues).

\$\endgroup\$
4
\$\begingroup\$

There's some stuff that's confusing in this coding and it's structure. A few things of note:

First when defining a class you do not need the beginning ( and ending )(); What that is for is to create a nameless function that will then be run once and then for all intents and purposes be removed from memory after. You are wanting to create a class that can be referenced and used later on. Simply:

function todoApp(){
    //define class here
}

This then creates an object constructor that you then call to create and store the object:

var myTodoApp = new todoApp();

Then your jQuery call after that, outside of your constructor, not in:

  $("[rel='jstodo-container'] > [rel='jstodo-submit']").on("click", function () {
    myTodoApp.storage.addTodo($("[rel='jstodo-container'] > [rel='jstodo-input']").val());
    myTodoApp.view.render();
  });

When giving a class (your todoApp function) a custom class for a variable, that 'sub-class' should be defined separately and then referenced inside of your class.

Inside of your class, anything you want to be accessible outside of your class should start with this.

var mayTodoApp = new todoApp();

$("[rel='jstodo-container'] > [rel='jstodo-submit']").on("click", function () {
    myTodoApp.storage.addTodo($("[rel='jstodo-container'] > [rel='jstodo-input']").val());
    myTodoApp.view.render();
  });

function todoApp(){
    this.storage = new storageClass();
    this.example = 'this is a string accessible outside of the class';
    //run any initialization code here
}

function storageClass(){
    var _db,
        todos,
        addTodo,
        removeTodo,
        updateTodo,
        getAllTodos,
        save;

        this.addTodo = function addTodo(todo) {
            //do stuff
            this.save(); //note: need 'this.' for class to access itself
        };
}

This is more along the structure you are looking for. A few things I would suggest looking into is the concept of this and maybe some Google results on javascript object oriented programming. Also having a good strong IDE (integrated development environment) helps a lot. I recommend Komodo personally, but to each their own. There might be a better one out there for you. But a good strong one can give code completion and show you syntax errors to help you get the structure down.

One other thing of note that helps quite a bit: Javascript is all read and processed into the DOM before anything is actually run (accept for dynamically added stripts, but that is a whole different beast). What this means is that before any coding is already run, the interpreter has already processed over the class definition. In other words, you can call your classes before defining them:

function todoApp(){
    this.storage = new storageClass();
}
function storageClass(){
    this.addTodo = function(todo){
    }
}
var todo = new todoApp();
todo.storage.addTodo('fooBar');

Will do the same as:

var todo = new todoApp();
todo.storage.addTodo('fooBar');

function storageClass(){
    this.addTodo = function(todo){
    }
}

function todoApp(){
    this.storage = new storageClass();
}

Outside of class definition, order matters, or course. You must create the object instance of your class before using it. Note that the latter is the standard. Classes should be defined below the coding to be run globally, this makes for better readability.

EDIT: I also just noticed your JSON objects. You do not need to define then in that manner. Javascript automatically determines variable type which is why you use var instead of char and int and such. For this same reason JSON objects are automatically defined by format. It's a harder concept for some to grasp at first and I would definitely read more into it but basically it is an associated array, or array of pairs (keys and values). that is in this format:

{key: 'value', key: 'value'}

It is defined inside of curly brackets { } the key is a name to reference the value, and does not need quotes. The value needs quotes unless you want to use a variable. Also numbers do not need quotes for the values. Values can be objects or arrays as well. And the pairs are delimited by a comma ,. As long as you follow these rules javascript does the rest.

var home = '1234 Fake St.';
var example = {date: '12/01/2016', time: '9:42am', location: home, peopleAttending: 10};
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.