Emberjs Displaying and Creating a subclass to a model - ember.js

Model: 'Category'
Subclass: 'Workflow'
I am trying to display the different 'Workflows' available for each 'Category' but I am receiving this error >>
Uncaught Error: Nothing handled the event 'createWorkflow'.
Here's some code
VpcYeoman.Category = DS.Model.extend({
permittype: DS.attr('string'),
isCompleted: DS.attr('boolean'),
classNameBindings: ['isAdministrator']
});
VpcYeoman.Workflow = VpcYeoman.Category.extend({
workflowtype: DS.attr('string')
})
VpcYeoman.Category.FIXTURES = [
{
id: 1,
permittype:'Building'
},
{
id: 2,
permittype:'Electrical'
},
{
id: 3,
permittype:'Zoning'
},
{
id: 4,
permittype:'Fire'
}
];
I'm also a little stumped on how to make FIXTURES for this subclass. I attempted recreating VpcYeoman.Workflow.FIXTURES = [id & workflowType examples], but it didn't display.
Category.hbs
<div class="department-header">
<div class="user-header">
Category: {{permittype}}
</div>
</div>
<table class="table table-hover table-responsive">
<thead>
<tr class="people-list">
<td><h4>Workflow Type</h4></td>
</tr>
</thead>
<table>
{{#each workflows}}
<tr>
<td>
{{workflowtype}}
</td>
</tr>
{{/each}}
</table>
<div class="input-bar">
<img src="images/lightning-icon-edited.png" class="input-icon">
{{input type="text" value=newWorkflowtype placeholder="Create a workflow and press enter" action="createWorkflow"}}
</div>
&&
VpcYeoman.CategoriesController = Ember.ArrayController.extend({
actions: {
createCategory: function () {
var permittype = this.get('newPermittype');
if (!permittype.trim()) {return;}
var category = this.store.createRecord('category', {
permittype: permittype
});
this.set('newPermittype', '');
category.save();
},
createWorkflow: function () {
var workflowtype = this.get('newWorkflowtype');
if (!workflowtype.trim()) {return;}
var workflow = this.store.createRecord('workflow', {
workflowtype: workflowtype
});
this.set('newWorkflowtype', '');
workflow.save();
}
}
});
&&
VpcYeoman.CategoriesRoute = Ember.Route.extend({
model: function() {
return this.store.find('category');
},
setupController:function(controller, model){
this._super(controller, model);
controller.set('workflows', this.store.find('workflow'));
}
});
VpcYeoman.CategoryRoute = Ember.Route.extend({
});

I'm assuming that you have a categories and category routes/templates based on the differently named things up there.
actions go the the particular route's controller Category then the route's route Category then up the routes Categories, Application
It looks like you are setting the workflows on the Categories controller, but trying to use it in the Category template

Related

Ember js filter model hasMany content via checkbox

I am building a small educational app where the structure is as follows -
Exam hasMany Subjects and Subjects hasMany courses.
My model relationships -
App.Exam = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
subjects : DS.hasMany('subject',{ async: true }),
});
App.Subject = DS.Model.extend({
name: DS.attr('string'),
description:DS.attr('string'),
exam: DS.belongsTo('exam', { async: true })
});
Initially I display all the exams and on exam/1 I display all the subjects belonging to that exam.
I am having trouble in filtering the subjects via checkbox
Here is the Demo
Not able to figure out how to do it. Can someone suggest me how to approach this ?
Basically on click of physics checkbox only physics subject should be displayed in the view.
I am basically using the MultipleSelectionFilterComponent from my blog post mentioned in the comments. This component will take care of managing the selection of the different checkboxes and send a filter function to the controller. There you can use the function to filter the data. You can refer to my post for more details.
Here is the working demo.
The code looks like
App.ExamsExamRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('exam', params.exam_id).then(function (exam) {
console.log("found", exam);
return exam;
});
},
setupController: function(controller, model) {
this._super(controller, model);
controller.set('filteredContent', model.get('data.subjects'));
}
});
App.ExamsExamController = Em.ObjectController.extend({
filteredContent: [],
actions: {
filterBySubject: function(filterFn) {
this.set('filteredContent',
this.get('model.data.subjects').filter(filterFn));
}
}
});
<ul class="list-group">
{{#multiple-selection-filter filter-key-path="name"
action="filterBySubject"}}
{{#each subject in model.subjects}}
<li class="">
<label>
<input type="checkbox" class="item-checkbox" {{bind-attr
value=subject.name}}/>
{{subject.name}}
</label>
</li>
{{/each}}
{{/multiple-selection-filter}}
</ul>
<h3>Subjects Details - </h3>
{{#each subject in filteredContent}}
<div class="col-md-3 well">
{{subject.name}}
<br>{{subject.description}}
</div>
{{/each}}
Here is the code to the MultipleSelectionFilterComponent.
App.MultipleSelectionFilterComponent = Em.Component.extend({
selectedItems: [],
click: function(event) {
var el = Em.$(event.target);
var filterFn;
if(el.is('input[type=checkbox]')) {
if(el.is(':checked')) {
this.get('selectedItems').pushObject(el.val());
} else {
this.get('selectedItems').removeObject(el.val());
}
}
if(this.get('selectedItems.length')) {
filterFn = function(item) {
return this.get('selectedItems')
.contains(Em.get(item, this.get('filter-key-path')));
}.bind(this);
} else {
filterFn = function() {return true;};
}
this.sendAction('action', filterFn);
}
});
that is SIMPLY not possible in ember.js
yeahhh it sux

Ember: store.update updates all attributes?

I'm creating a real time multiplayer textual game in Ember.js.
So far very exciting, but I'm running a little problem.
I have a game model which looks a bit like this:
App.Game = DS.Model.extend({
numbers: DS.attr(),
drawnNumbers: DS.attr(), // array
gameStatus: DS.attr(),
table: DS.belongsTo('table'),
bingoCards: DS.hasMany('bingoCard', { async: true })
});
My controller looks like this (leaving out unnecessary information):
App.GameController = Ember.ObjectController.extend({
gameBingoCards: function () {
var gameId;
gameId = this.get('id');
console.log("inside gameBingoCards");
return this.get('store').filter('bingoCard', function (bingoCard) {
return (bingoCard.get('game.id') === gameId);
});
}.property('model.bingoCards'),
ownBingoCards: function () {
var gameId, userId;
gameId = this.get('id');
userId = this.get('session.content.id');
console.log("inside ownBingoCards");
return this.get('store').filter('bingoCard', function (bingoCard) {
return (bingoCard.get('game.id') === gameId && bingoCard.get('user.id') === userId);
});
}.property('gameBingoCards.[]'),
gameMessages: function () {
var gameId;
gameId = this.get('id');
console.log("gameMessages");
return this.get('store').filter('message', function (message) {
return (message.get('game.id') === gameId);
});
}.property('model.messages'),
});
In the view I render the cards:
{{#each bingoCard in ownBingoCards}}
<div class="col-sm-4">
<div class="table-responsive">
<span class="label label-primary">Card {{bingoCard.id}}</span>
<table class="table table-bordered table-card">
<tbody>
{{#each row in bingoCard.squares}}
<!-- displaying the numbers here -->
{{/each}}
</tbody>
</table>
</div>
</div>
{{/each}}
Whenever the game updates I update the store like this:
record = serializer.extractSingle(store, type, data);
// record looks like this:
// {id: "538c56843800226245c3621a", gameStatus: "idle"}
store.update("game", record);
If I open the console I get the following:
inside ownBingoCards GameController.js:102
inside gameBingoCards GameController.js:32
inside ownBingoCards GameController.js:102
Note: the game receives many updates during the game, so every time all the cards get rerendered. How can I prevent this?
edit:
After I reload the page on that specific game route it only goes inside ownBingoCards and gameBingoCards once and it doesn't re-render everytime after an update.
edit2:
The gameMessages attribute also only gets called once, why does the gameBingoCards keep getting called?
Alright, I've fixed it after countless hours.
My route looked like this:
model: function (params) {
return this.store.find('game', params.game_id);
},
setupController: function (controller, model) {
model.reload();
controller.set('model', model);
},
And I've changed it to this:
model: function (params) {
return this.store.find('game', params.game_id);
},
setupController: function (controller, model) {
model.reload();
controller.set('model', model);
controller.set('modelBingoCards', model.get('bingoCards'));
controller.set('modelMessages', model.get('messages'));
},
Plus I also changed the property listeners to .property('modelMessages') and .property('modelBingoCards').
Could any please tell me why this worked?

Why is the URL not updated when I click the link for that route

I am using EmberJs version 1.4.
When I click on one of the links I would expect the URL to include the id of the selected widget but nothing appears and when I look at the params parameter in the route model hook it has no properties and I would expect the id to be one of its properties so could someone help me to understand what am I missing?
In other words I would expect the URL to become awesome.html#/widgets/5 but it always is awesome.html#/widgets
Thank you!
This is my ember code:
window.Awesome = Ember.Application.create();
Awesome.Router.map(function() {
this.resource("awesome", {path: "/"}, function(){
this.route('login');
});
this.resource("widgets", function () {
this.resource('widget', { path: '/:widgetId' }, function () {
this.route('general', { path: 'info' });
this.route('configuration');
this.route('operations');
})
});
});
Awesome.WidgetsRoute = Awesome.AuthenticationRoute.extend({
model: function(){
//TODO: Call a service to get the model.
return { widgets: [{ widgetId: 1, widgetName: "Great Widget" }, { widgetId: 2, widgetName: "Fantastic Widget" }, { widgetId: 3, widgetName: "Brutal Widget" }] };
}
});
Awesome.WidgetIndexRoute = Awesome.AuthenticationRoute.extend({
model: function (params) {
var receivedWidgetId = params.widgetId;
return { widgetName: "Hardcoded Widget", widgetId: receivedWidgetId };
}
});
These are the templates:
<script type="text/x-handlebars" data-template-name="widgets">
<section class="span3 left-section">
<div class="btn-group-vertical btn-group-justified registration-actions-menu">
<button id="createNewWidget" class="btn btn-link">Create New Widget</button>
<button id="joinWidgetTeam" class="btn btn-link">Join Widget Team</button>
</div>
<div class="registered-widgets-menu">
<div class="btn-group-vertical">
{{#each widget in widgets}}
{{#link-to 'widget' widget class="btn btn-link"}}{{widget.widgetName}}{{/link-to}}
{{/each}}
</div>
</div>
</section>
<section class="span8">
{{outlet}}
</section>
</script>
<script type="text/x-handlebars" data-template-name="widget">
<div id="widgetOptions">
<!-- TODO: Change the anchors for handlebars link-to helpers. -->
<h1>{{widgetName}}</h1> <h5>{{widgetId}}</h5>
<ul id="widgetNavigation">
<li>Widget Info</li>
<li>Widget Configuration</li>
<li>Widget Operations</li>
</ul>
</div>
<div id="widgetContent">
<!-- TODO: Design some awesome widget content. -->
Some awesome widget content
</div>
</script>
I also have an authentication route from which the other routes inherit. Even though I don't believe it has something to do with the issue I'll include just in case I am wrong.
Awesome.AuthenticationRoute = Ember.Route.extend({
beforeModel: function(transition){
if(!Awesome.get('loggedUser')){
this.redirectToLogin(transition);
}
},
redirectToLogin: function(transition) {
var loginController = this.controllerFor('awesome.login');
loginController.set('attemptedTransition', transition);
this.transitionTo('awesome.login');
}
});
It looks like it's totally working to me, when you click on one of the widgets
http://emberjs.jsbin.com/mohex/1
Additionally it looks like you're mixing up the WidgetIndexRoute and WidgetRoute. The widget resource should be displayed like this (though this is unrelated to the issue you're describing)
Awesome.WidgetRoute = Awesome.AuthenticationRoute.extend({
model: function (params) {
var receivedWidgetId = params.widgetId;
return { widgetName: "Hardcoded Widget", widgetId: receivedWidgetId };
}
});

Router understanding issue with Ember 1.0 RC6

The following code worked fine with RC4 but it doesn't work with RC6. index.html#/users/1/edit doesn't fill the form to edit the user entry. Can anybody tell me what I have to change to get this working with the new router?
app.js
App = Ember.Application.create();
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' }, function() {
this.route('edit');
});
})
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.UserController = Ember.ObjectController.extend();
App.UserEditRoute = Ember.Route.extend({
model: function() {
return this.modelFor("user")
},
renderTemplate: function() {
this.render({ into: 'users' });
},
setupController: function(controller, model) {
if (model.get('isNew') == false) {
var map = this.map || Ember.Map.create();
this.map = map;
var transaction = this.get('store').transaction();
if (map.get(model)) {
transaction = map.get(model);
} else {
map.set(model, transaction);
transaction.add(model);
}
}
},
events: {
commitThis: function(model) {
var map = this.map;
var transaction = map.get(model);
transaction.commit();
map.remove(model);
App.Router.router.transitionTo('users.index')
},
rollbackThis: function(model) {
var map = this.map;
var transaction = map.get(model);
transaction.rollback();
transaction.add(model);
App.Router.router.transitionTo('users.index')
}
}
});
App.UserEditController = Ember.ObjectController.extend({
save: function(model) {
this.send('commitThis', model)
},
undo: function(model) {
this.send('rollbackThis', model)
}
});
App.Store = DS.Store.extend({
adapter: 'DS.FixtureAdapter'
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
App.User.FIXTURES = [{
id: 1,
firstName: "Yehuda",
lastName: "Katz"
}, {
id: 2,
firstName: "Tom",
lastName: "Dale"
}]
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Userlist Demo</title>
<link href="css/bootstrap.css" rel="stylesheet">
<style>
body {
padding-top: 60px;
}
</style>
<link href="css/bootstrap-responsive.css" rel="stylesheet">
</head>
<body>
<script type="text/x-handlebars">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="#">Demo</a>
<div class="nav">
<ul class="nav">
<li>{{#linkTo 'users'}}Users{{/linkTo}}</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="index">
<h2>Demo Ember.js Application</h2>
<p>
A list of all users can be found {{#linkTo 'users'}}here{{/linkTo}}.
</p>
</script>
<script type="text/x-handlebars" data-template-name="users">
<div class='row'>
<div class='span7'>
<table class='table table-striped'>
<thead>
<tr>
<th>First name</th>
<th>Last name <i class="icon-arrow-down"></i></th>
<th></th>
</tr>
</thead>
<tbody>
{{#each this itemController="user"}}
<tr {{bindAttr class="isDirty:warning"}}>
<td>{{firstName}}</td>
<td>{{lastName}}</td>
<td>
{{#unless isNew}}
{{#linkTo 'user.edit' this activeClass="disabled" classNames="btn btn-small"}}<i class="icon-edit"></i> Edit{{/linkTo}}
{{/unless}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class='span5'>
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="user/edit">
<h2>Edit</h2>
<p><strong>First name</strong><br>{{input value=firstName type=text tabindex=1}}</p>
<p><strong>Last name</strong><br>{{input value=lastName type=text tabindex=2}}</p>
<p>
<button {{action 'save' this}} {{bindAttr class=":btn :btn-small :btn-primary content.isDirty:enabled:disabled"}} tabindex=4>Save changes</button>
<button {{action 'undo' this}} {{bindAttr class=":btn :btn-small content.isDirty:enabled:disabled"}} tabindex=5>Undo changes</button>
</p>
</script>
<script src="js/libs/jquery-1.9.1.js"></script>
<script src="js/libs/handlebars.js"></script>
<script src="js/libs/ember.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/libs/md5.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Pretty sure this is because App.UserEditRoute.setupController is not calling _super. Strange cause I thought that breaking change was back in RC4. Anyway, try this:
App.UserEditRoute = Ember.Route.extend({
// ...
setupController: function(controller, model) {
this._super(controller, model);
// ...
}
}
Possibly related: Seems like setupController is saving state (this.map) on the route object. Surprised it works at all, for sure has potential to cause problems. Instead setupController should set properties on the local controller or model arguments, or use this.controllerFor() to access another controller.
In this case seems like a lot of code is not necessary, could just save/rollback on the model itself. So to simplify:
App = Ember.Application.create();
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' }, function() {
this.route('edit');
});
})
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.UserController = Ember.ObjectController.extend();
App.UserEditRoute = Ember.Route.extend({
model: function() {
return this.modelFor("user")
},
renderTemplate: function() {
this.render({ into: 'users' });
},
events: {
save: function(model) {
model.save().then( function() {
App.Router.router.transitionTo('users.index')
}, function() {
alert('save failed!');
});
},
undo: function(model) {
model.rollback();
App.Router.router.transitionTo('users.index')
}
}
});
App.Store = DS.Store.extend({
adapter: 'DS.FixtureAdapter'
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
App.User.FIXTURES = [{
id: 1,
firstName: "Yehuda",
lastName: "Katz"
}, {
id: 2,
firstName: "Tom",
lastName: "Dale"
}]
See working example here: http://jsbin.com/ixucos/2/edit

Sorting an array does not update the Dom

I can not make the following code work in my test app:
this.propertyWillChange('tableContent');
this.get('tableContent').sort(function (a, b) {
var nameA = a.artikel_name,
nameB = b.artikel_name;
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
return 0 //default return value (no sorting)
});
this.propertyDidChange('tableContent');
The data gets sorted, but the dom is not updated.
The template looks like this:
<tbody>
{{#each NewApp.router.gridController.tableContent}}
{{#view NewApp.TableRow rowBinding="this"}}
<td style="width: 100px">{{view.row.product_no}}</td>
<td align="right" style="width: 100px">{{view.row.price}}</td>
<td>{{view.row.artikel_name}}</td>
{{/view}}
{{/each}}
</tbody>
I tried to reproduce this problem with a short jsfiddle snippet. But there it works. The only difference is, that I fetch the data using an ajax call (and some additional router setup).
selectionChanged: function () {
var that = this;
if (this.selection) {
$.getJSON("api/v1/lists/" + this.selection.id + "/prices", function (content) {
that.set('tableContent', content);
});
}
}.observes('selection')
The same code works if i copy the array and reassign the copied array.
Did you try to use the built-in SortableMixin ? If not, is this good for you ?
JavaScript:
App = Ember.Application.create();
App.activities = Ember.ArrayController.create({
content: [{name: 'sleeping'}, {name: 'eating pizza'},
{name: 'programming'}, {name: 'looking at lolcats'}],
sortProperties: ['name']
});
App.ActivityView = Ember.View.extend({
tagName: "li",
template: Ember.Handlebars.compile("{{content}}")
});
App.SortButton = Ember.View.extend({
tagName: "button",
template: Ember.Handlebars.compile("Sort"),
click: function() {
App.activities.toggleProperty('sortAscending');
}
});
jsfiddle: http://jsfiddle.net/Sly7/cd24n/#base