1
votes

I am working on a todo type project using Ember-CLI. I used as a starting point the nifty todoMVC project, but built with Ember-CLI using this guide:

http://blaketv.com/2014/10/03/ember-cli-todo-mvc-tutorial-0-0-47//

My question is, how would I go about adding projects at the parent level. So we would have a master-detail type interface and in the sidebar we would have projects and you could CRUD project names, and then when you click on a project name, you see the todos in the detail pane.

I have gotten far enough defining the hasMany relationships to the models, but I cannot figure out if I need multiple {{outlets}} It is very difficult to get everything on the same page and working.

Here is my model for project:

export default DS.Model.extend({
  title: DS.attr('string'),
  isCompleted: DS.attr('boolean'),
  description: DS.attr('string'),
  todos: DS.hasMany('todo', {async: true})
});

and model for todos:

import DS from 'ember-data';

export default DS.Model.extend({
  title: DS.attr('string'),
  isCompleted: DS.attr('boolean')
});

and the main Router:

Router.map(function() {
  this.resource('projects', function () {
    this.route('new'); 
    this.resource('project', { path: ':id' }, function () {
      this.route('todos');
    });
  });
});

Project Route:

export default Ember.Route.extend({
  model: function(params) {
    return this.store.find('project', params.id);
  }
});

Index Route:

export default Ember.Route.extend({
  model: function() {
    return this.store.find('project');
  }
});

Todos Route:

export default Ember.Route.extend({
  model: function() {
    return this.modelFor('todos');
  }
});

So for project.hbs this is where it gets tricky. I create the sidebar with bootsrap and then this outlet shows the todos....

<div class="projects-column col-md-3">
  <div id="inbox-header"><span class="glyphicon glyphicon-inbox"></span> Inbox <span class="badge">42</span></div>
  <div id="projects-header"><span class="glyphicon glyphicon-list-alt"></span> Projects</div>
  <div id="forecast-header"><span class="glyphicon glyphicon-calendar"></span> Forecast</div>
  <div id="log-header"><span class="glyphicon glyphicon-book"></span> Sessions Log</div>
</div>
  <div>{{outlet}}</div>

Index.hbs:

<ul>
  {{#each model}}
  <li>{{link-to title "project.todos" this}}</li>
 {{/each}}

So this above when you click on the project title link, it shows the associated todos.... but it renders in the left pane... it's probably just something about the CSS layout...but something tells me there is a very Ember-ish way to do this that I am missing.

Then in /project/todo.hbs we have the iteration

{{#each model.todos}}
    <li>{{title}}</li>
{{/each}}

I haven't even really addressed making the CRUD for controllers or anything. Most likely this above is laughable and there is a much more elegant way to approach this...

Basically I want a projects parent route, that I do CRUD with... and then when you render a list of project links in the sidebard and click on one, you get in the right pane the rendered ToDoMVC working app.

Of course this is just a starting point for my application. Most likely if someone comes up with a elegant way to do this, we can turn it into an open source project on github for others to learn from.

I think a bunch of burgeoning ember developers are having a hard time with this type of thing because of the multiple ways to do it (outlets, partials, render, render into other templates, views, components, etc)

Don't really know how to get any further.

1

1 Answers

0
votes

Not sure if you're still stuck, but I'd try it without bootstrap as a side bar, and just put an {{#each}} [full code here]

App = Ember.Application.create({
  LOG_TRANSITIONS: true,
  LOG_BINDINGS: true,
  LOG_VIEW_LOOKUPS: true,
  LOG_ACTIVE_GENERATION: true,
  debugMode: true
});

App.Router.map(function() {
  this.resource('projects', {
    path: '/'
  });
  this.resource('project', {
    path: '/projects/:project_id'
  }, function() {
    // URL = '/projects/:id/todos'
    this.resource('project.todos', {
      path: '/todos'
    }, function() {
      // URL = '/project/:id/todos/new'
      this.route("new");
    });

  });
});
App.ApplicationAdapter = DS.FixtureAdapter.extend();
//App.Store = DS.Store.extend({adapter : DS.FixtureAdapter});

App.ProjectsRoute = Ember.Route.extend({
  model: function() {
    return this.store.findAll('project');
  },
  actions: {
    addproject: function() {
      var newproject = this.store.createRecord('project', {
        name: "My New project"
      });
    },
    removeproject: function(project) {
      console.log(project);
      console.log(this.controller.get("model"));
      this.controller.get("model").removeObject(project);
    }
  }
});

App.ProjectRoute = Ember.Route.extend({
  model: function(params) {
    return this.store.find('project', params.project_id).then(function(project) {
      return project;
    });
  }

});
App.ProjectsIndexRoute = Ember.Route.extend({
  model: function(params) {
    return this.modelFor('project');
  }
});

App.ProjectTodosRoute = Em.Route.extend({

  model: function(params) {
    return this.modelFor('project');

  },
  actions: {
    addtodo: function() {
      this.transitionTo("project.todos.new");
    }
  }

});
App.projecttodosNewRoute = Em.Route.extend({
  model: function(params) {
    parentprojectId = this.modelFor('project').get("id");

    newtodo = this.store.createRecord('todo', {
      id: "5",
      name: "John Doe",
      //project : parentprojectId
      project: this.store.getById('project', parentprojectId)


    });
    console.log("new todo = " + newtodo);
    return newtodo;

  },
  actions: {
    save: function() {
      //console.log(this.controllerFor('projecttodosNew').content);
      //console.log('save of newtodo = '+this.controllerFor('projecttodosNew').get('newtodo'));
      console.log('newtodo~ ' + newtodo.get('name') + ', ' +
        newtodo.id + ', ' + newtodo);
      newtodo.save()
        //this.controllerFor('projecttodosNew').content.save()
        .then(function() {
          this.transitionTo("project.todos");
        });

    },
    cancel: function() {
      console.log("rollback for " + this.get("controller.model"));
      this.get("controller.model").rollback();
      this.set("controller.model", null);
      this.transitionTo("project.todos");
    }
  }
});

//App.projecttodosNewController = Ember.ObjectController
//			.extend({
//				needs : [ 'application', 'project'],
//				newtodo : null
//    });
App.Project = DS.Model.extend({
  name: DS.attr(),
  todos: DS.hasMany('todo', {
    async: true
  })
});

App.Project.FIXTURES = Em.A([{
  id: 1,
  name: 'Monday',
  todos: ['2']
}, {
  id: 2,
  name: 'Tuesday',
  todos: ['1', '2']
}, {
  id: 3,
  name: 'Wednesday',
  todos: ['4']
}]);

App.Todo = DS.Model.extend({
  name: DS.attr('string'),
  //project : DS.belongsTo('project')
});
App.Todo.FIXTURES = [{
  id: 1,
  name: 'shop',
  project: 1
}, {
  id: 2,
  name: 'sell things',
  project: 2
}, {
  id: 4,
  name: 'dance',
  project: 3
}];
/* Put your CSS here */

html,
body {
  margin: 20px;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Ember Starter Kit</title>
  <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
  <script src="http://builds.emberjs.com/tags/v1.6.1/ember.js"></script>
  <script src="http://builds.emberjs.com/tags/v1.0.0-beta.10/ember-data.prod.js"></script>

</head>

<body>
  <script type="text/x-handlebars">
    <h2>Welcome to "The Project/TODO Demo"</h2>
    {{outlet}}
  </script>

  <script type="text/x-handlebars" data-template-name="projects">
    <ul>
      {{#each item in model}}
      <li>{{#link-to 'project.todos' item }}{{item.name}}, List of todos{{/link-to}} ,
        <button {{action "removeproject" item}}>X</button>
      </li>
      {{/each}}

    </ul>
    <button type="button" {{action "addproject" this.id}}>Add a project</button>
    {{outlet}}
  </script>
  <script type="text/x-handlebars" data-template-name="project/index">
    <br><b>Name of project:</b> {{name}}

  </script>
  <script type="text/x-handlebars" data-template-name="project">
    {{#link-to "projects"}}Home{{/link-to}} {{outlet}}
  </script>
  <script type="text/x-handlebars" data-template-name="project/todos/index">
    <h1></h1>
    <b>todos</b> 
    <br>
    <ul>
      {{#each todo in todos}}
      <li>{{todo.name}}</li>
      {{/each}}
    </ul>
    <button type="button" {{action "addtodo"}}>Add a todo</button>
    <br>{{#link-to 'project' this}}project details page{{/link-to}} {{outlet}}
  </script>
  <script type="text/x-handlebars" data-template-name="project/todos/new">
    <h1></h1>
    <b>New todos</b> 
    <br>
    <ul>
      <li>Name: {{input type='text' value=model.name}}</li>
      <li>todo Id: {{input type='text' value=id}}</li>
      <li>Parent project Id: {{project}}</li>
    </ul>
    <button type="button" {{action "save"}}>Save todo</button>
    <button type="button" {{action "cancel"}}>cancel</button>
    <br>{{outlet}}
  </script>
</body>

</html>

on the top of your homepage / index. After you get that working you can start playing with the layout with bootstrap, and getting it looking cool.

similar to links doc. Hope that gets you passed your hurdle.