The Problem
I have a Rails 4 backed Ember app that needs to iterate over some children, and then iterate over that child's children. The setup is Session -> hasMany -> Annotations -> hasMany -> Indicators.
I can load the /session/1 show template and display the session's properties. I can also iterate over my session's annotations and display the annotation's text. However When I iterate over the annotation's indicators, nothing shows up. If I output {{#with annotation}}{{indicators}}{{/with}} I just get <DS.PromiseArray:ember802>
Ember makes AJAX calls to /sessions/1 and /annotations?ids%5B%5D=113&ids%5B%5D=112. However it never makes a call to /indicators.
I've seen other posts that describe the same issue, but the solutions for those often came down to camel casing etc. In this case, since /indicators is never even being called, what am I doing wrong?
Environment
ember.js - 1.5.1
ember-data.js - 1.0.0-beta.7
Written in CoffeeScript
Ember Setup
Insight.ApplicationAdapter = DS.ActiveModelAdapter.extend({})
Insight.ApplicationSerializer = DS.ActiveModelSerializer.extend({})
Models
App.Indicator = DS.Model.extend {
title: DS.attr 'string'
}
App.Annotation = DS.Model.extend {
text: DS.attr 'string'
session: DS.belongsTo 'session', inverse: 'annotations'
indicators: DS.hasMany 'indicator', async: true
}
App.Session = DS.Model.extend {
subject: DS.attr 'string'
students: DS.attr 'number'
time: DS.attr 'string'
annotations: DS.hasMany 'annotation', async: true
}
Routes
App.SessionRoute = Ember.Route.extend {
model: (params)->
return #store.find('session', params.session_id)`
Session Show Template (Relevant Portion)
<section class='content'>
{{#each annotation in annotations itemController="annotation"}}
{{#with annotation}}
<li {{bind-attr class="isCompleted:completed isEditing:editing"}}>
{{#if isEditing}}
{{edit-annotation class="edit" value=bufferedText focus-out="doneEditing" insert-newline="doneEditing" escape-press="cancelEditing"}}
{{else}}
{{text}}
</div>
{{/if}}
{{#each indicator in indicators}}
<button>{{indicator.title}}</button>
{{/each}}
</li>
{{/with}}
{{/each}}
{{view Ember.TextField id="new-annotation" placeholder="Enter an annotation" valueBinding="newAnnotation" action="createAnnotation"}}
</section>
JSON GET Payloads
{"session":{
"id":4,
"subject":"Name of Subject",
"students":1,
"time":"08:52",
"annotations":[113,112]}
}
{"annotations":
[
{ "id":112,
"text":"this is my first annotation",
"session":4,
"indicators":[1]
},
{ "id":113,
"text":"This annotation has indicators",
"session":4,
"team_member":8,
"indicators":[1,2]
}
]
}
The problem lies in JSON format.
Format that you're using is the format that is accepted by the RESTAdapter
data: [
title: "Some Title",
items: [1,2]
]
ActiveModelAdapter however expects the data to be in this format:
data: [
title: "Some Title",
item_ids: [1,2]
]
To solve the problem you can either swap to RESTAdapter OR change the data producer to produce proper format instead.
JSBin with above code made working and
other question that reference the same issue.
Related
Could someone tell me what my form data's string ends at the comma? I'm sending a form with react with a string with foo, bar but only foo is sent in the form data. This has nothing to do with the database as in the chrome dev tools, under network, I see only one value of the string. Here's a image of my actual views:
Only Wordpress, gets submitted. Noticed the ,?
Some code, in React (I'm using react-select):
<form action="/" method="post">
<Select ref="stateSelect" options={skillsOptions} simpleValue multi={this.state.multi} value={this.state.skillsSelectValue} name="skills" onChange={this.skillsUpdateValue} valueKey="value" labelKey="name" loadOptions={this.getContributors} />
</form>
skillsUpdateValue: function(newValueCnd) {
// var str = newValueCnd.split(',');
this.setState({
skillsSelectValue: newValueCnd
});
this.setState({
skills: newValueCnd
});
},
Am I missing something from the form? Using just Rails, all words great.
Edit:
Input options:
var skillsOptions = [
{ value: 'one', name: 'One' },
{ value: 'two', name: 'Two' }
];
I have a JSON object like the following:
{
"sessions": [
{
"title": Session Title,
"room": "Ballroom A"
},
{
"title": Session Title #2,
"room": "Ballroom B"
}
],
"speakers": [
{
"name": John Doe,
"twitter": "jdoe"
},
{
"name": John Smith,
"twitter": "jsmith"
}
]
}
I am trying to bind to the sessions child of the object. The full object is in a variable named conferenceData and the code I am using to display the titles is:
<div *ngFor="#session of conferenceData.sessions">{{session.title}}</div>
When I do this, I get the error: TypeError:
Cannot read property 'sessions' of undefined in [conferenceData.sessions in ProductListComponent#65:17]
If I assign the child to a variable:
this.sessionData = this.conferenceData.sessions;
Then bind to the sessionData variable instead, it works as expected. This is using TypeScript and Angular 2. I suppose this could be a type issue with TypeScript, but I thought by this time it was all JavaScript. Any help would be appreciated.
Thanks!
This is probably, because this.conferenceData is not assigned yet, when Angular is rendering your view the first time.
You can easily fix this with the elvis operator:
<div *ngFor="#session of conferenceData?.sessions">{{session.title}}</div>
The problem is the time to fetch the json data, the component doesn't have the data when the angular renders the view. The solution is uses 'elvis' (?) operator to ensure the data is ready:
-
<div *ngFor="#session of conferenceData?.sessions">
{{session.title}}
</div>
The second solution is hide the ngFor until the conferenceData is ready:
<div *ngIf="conferenceData">
<div *ngFor="#session of conferenceData.sessions">
{{session.title}}
</div>
</div>
This is how I have created my ember FIXTURE:
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter;
App.Category = DS.Model.extend({
name: DS.attr(),
parent_id: DS.attr()
});
App.Category.FIXTURES = [
{
id: 1,
name: 'user1',
email: 'user1#gmail.com',
parent_id: 0
},
{
id: 2,
name: 'user2',
email: 'user2#gmail.com',
parent_id: 1
}
];
Here is a part of my ember view where 'parent-title' is a helper:
{{#each category in controller}}
<tr>
<td>{{category.name}}</td>
<td>{{parent-title category.parent_id}}</td>
<td>Edit/Delete</td>
</tr>
{{/each}}
What I want is that during listing if the parent_id is 0 it should print 'master' else the name of parent category. In my exapmle parent of user2 is id=1 show it should print 'user1'.
Below is the helper I have used:
Ember.Handlebars.helper('parent-title', function(parent_id){
if (parent_id > 0) {
var parent = category.findBy('id', parent_id);
return parent.name;
} else {
return 'master';
}
});
I know if I replace the line App.Category.FIXTURES = [ with var Category = [ I can get it done but I want ot do it with FIXTURES.
I can tell you that accessing data like that is a bad idea. If I were you, I would change parent_id to be a relationship, not an attribute (since that's really what it is). Then you can access the parent's name in templates with category.parent.name. Making it a relationship also gives you a few other luxuries.
But if you want to maintain backward compatibility, try using a computed property.
App.Category = DS.Model.extend({
name: DS.attr(),
parent_id: DS.attr(),
parent: function() {
return DS.PromiseObject.create({
promise: this.get('store').find('category', this.get('parent_id'))
});
}.property('parent_id'),
parent_name: function() {
return this.get('parent.name');
}.property('parent.name')
});
EDIT: If you want to change that into a relationship (which I think it should be), it's fairly simple. Instead of DS.attr, you use DS.belongsTo. For instances.
App.Categor = DS.Model.extend({
name: DS.attr(),
// I dropped the _id part because it's no longer an ID
parent: DS.belongsTo('category', { inverse: null })
});
This tells Ember-Data to interpret the ID you give in the parent field as another category object. So category.get('parent') will return another category object, not a number. But in your case, to make it work, you'll have to convert all of the 0 IDs to null IDs. I wasn't sure if that was possible, which is why I recommended the computed property.
EDIT: To display master in case of a null parent, use the Handlebars if expression.
{{#if parent}}
{{parent.name}}
{{else}}
master
{{/if}}
So in my PhoneJS web app, I have a dxList widget, with checkboxes on each item. I want to be able to select multiple items, and then do something with them. I'm trying to bind the 'checked' binding to an observable, but I get an 'undefined' error.
Here's the code for the dxTemplate for the list
<div data-options="dxTemplate:{name:'item'}">
<span data-bind="text: $data.information"></span>
<div data-bind="dxCheckBox: { checked: check_boxes }"></div>
</div>
The problem is that check_boxes is in the viewModel, not the item array. I need to access values in the viewModel. I've tried viewModel.check_boxes, but with no success.
Here's the js code:
AppNamespace.otherView = function (params) {
var viewModel = {
my_list: [
{
key: 'Group 1',
items: [
{ information: 'Test 1' },
{ information: 'Test 2'},
{ information: 'Test 3' }
]
}
],
check_boxes: ko.observable(false),
//...etc
Has anyone had any experience with this, and is there a solution?
Thanks!
Knockout provides special properties to access parent binding contexts. In your case both $parent and $root should work.
More on this topic in Knockout docs: Binding context.
Hypothetical example to illustrate a problem I am having using angular-UI select2. Let's say I have a screen where I want to edit a "game" model. A game, among other things has players. I want to be able to set the players via a select2 drop down menu. Here's some example code:
app.js
$scope.getGamePromise().then(function(results) {
$scope.game = results;
console.log(game.players); //prints [{name:'Joe',age: 15},{name:'Sally',age:16}]
});
$scope.players = [
{
name: 'Joe',
age: 15
},
{
name: 'Fred',
age: 14
},
{
name: 'Sally',
age: 16
},
{
name: 'Lucy',
age: 13
}
]
view.html
<select ngModel="game.players" ui-select2 multiple>
<option ng-repeat="player in players" value="player">{{ player.name }}</option>
</select>
When I want to save this 'game' object, I send the game object up to the server. The server is expecting game.players to be an array of objects. However, what is being sent up is a string. I am moderately familiar with angular, and completely new to select2. How can I get my select2 to set game.players as an array of objects instead of strings?
I guess you find another solution or you don't have the problem anymore. Anyway I post it here:
Use
<input>
instead of
<select>
Example:
<input type="hidden" ui-select2="playersCfg" ng-model="players"/>
And following configuration:
$scope.playersCfg = {
multiple: true,
allowClear: true,
data: { results: Player.query(), text: 'name' },
formatResult: function(item) {
return "<div class='select2-user-result'>" + item.name + "</div>";
},
formatSelection: function(item) {
return item.name;
}
};
Player.query()
is a resource which returns a list of player containing a name (name) and an identifier (id)
Hope it would help somebody!