Given my current router:
Posts
Create
Post
Edit
Comments
Create
Comment
InnerComments
Create
InnerComment
Index (post.comment.innerComment.index)
Ember.js is not loading any existing innerComments from the backend, but it is doing a hell of a job in creating and saving them like the picture below:
Not only I am able to create the inner comments, I am also able to successfully edit them as long as I do not reload the page... This unique behaviour tells me that the issue seems to be rather specific, but I just cannot find it. The code snippets below summarizes how things are loaded in order, but you can also check out the Source Code and these two screenshots with Ember Inspector:
Screenshot 1 - Created Inner Comment
Screenshot 2 - Page Refresh.
Post.Index (model is a post)
{{render "post.comments.index" model}}
Post.Comments.Index (model is a post)
<ol>
{{#each model.comments as |comment|}}
<li>{{render "post.comment.index" comment}}</li>
{{/each}}
</ol>
Post.Comment.Index (model is a comment)
<!--Comment Text, Comment by x User, edit, etc-->
{{render "post.comment.innerComments.index" model}}
Post.Comment.InnerComments.Index (model is a comment)
<ol>
{{#each model.inner_comments as |comment|}}
<li>{{render "post.comment.innerComment.index" comment}}</li>
{{/each}}
</ol>
Post.Comment.InnerComments.Index (model is an innerComment)
<!--InnerComment Text, InnerComment by x User, edit, etc-->
{{model.text}}
One missing line on Serialization has_many :inner_comments was the culprit as it was lost between merging two local branches.
If you have a similar problem, check the developer tools for the network transactions. If one of your XHR requests is missing the expected return payload (the inner_comments for each comment request in my case), then the problem is on your backend...
Related
I am using rails 4 with emberjs. I have an array of users that I get from the server. I need to display the last object on the UI. Also, the user can add new users to the array but only the last/latest-added user should be visible.
My ember version details are as
Ember : 1.7.0-beta.1
Ember Data : 1.0.0-beta.8.2a68c63a
Handlebars : 1.3.0
jQuery : 1.11.1
My current handlebars code is as. After adding the new user to users array, the new user details is displayed.
{{each users}}
//display all the users here.
{{/each}}
What i want is something like this
{{#each users}}
{{#if #last}}
//display user details
{{/if}}
{{/each}}
I tried adding a property in the associated controller and removed the 'each' loop. I displayed the lastUser details which works. But if I add a new user, the UI still displays
the previous user details.
lastUser: (->
return #model.users.get('lastObject')
).property('#model.users')
How can this be acheived? Thanks.
You can simply use the following:
{{users.lastObject.firstName}}
{{users.lastObject.lastName}}
or more cleanly:
{{#with users.lastObject}}
{{firstName}}
{{lastName}}
{{/with}}
With just redefines this within the block to the object provided--in this case the users.lastObject object. Your lastUser computed property is unnecessary because Ember enumerables already provide this for free!
To watch additions to the users array, you need to watch the 'users.#each'
See the following documentation http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/
My Ember.js app is set up roughly like this:
Router:
App.Router.map ->
#resource('site', { path: '/' }, ->
#resource('dashboard')
#resource('account')
#resource('pages', { path: '/:page_slug'}))
Routes:
App.ApplicationRoute = Ember.Route.extend
model: ->
return App.Site.find(1)
App.PagesRoute = Ember.Route.extend
model: (params)->
return App.Page.find(params.page_slug)
EDIT:
Controller (JS not Coffee):
App.PagesController = Ember.ObjectController.extend({
needs: 'ApplicationController',
...
});
I have an ApplicationController and a PagesController. When I'm on a page, I want to call an action to delete the current page. If I place it in the PagesController it works kind of ok, but a navigation menu with a list of pages in the ApplicationView doesn't get updated until I refresh the page. So... I assume I need to place the action in the ApplicationController and add needs: ['ApplicationController'] to my PagesController.
However, when I do that, everything in my Pages template disappears.
I have an {{outlet}} in my Application template and another on in the Site template which ultimately displays the Pages template. Yeah, complicated, I know and there's probably a better way to do it, so any suggestions would be greatly appreciated. Oh and BTW, I'm a real Ember.js newb, so examples need to be spelled out explicitly. (Drawing them with pretty colors in crayon would actually be ideal.)
Thanks in advance for any help.
Here's the refresh issue. In the root resource site I'm loading in one Site model which the Rails backend returns using the url being sent in. Eventually, this app will be used with multiple domains, and each domain will be served it's own website (roughly based on the WP-Multisite concept). Then in the pages route, I'm loading one page of the site based on its slug attribute. That's all working fine. So, now I want to allow the user to be able to add and remove pages as they want.
So, the issue is this. When I delete a page at the PagesController level, the page deletes just fine, but the ApplicationController doesn't get notified. Namely, my nav menu:
<ul class="left">
{{#each page in page}}
<li>{{#link-to 'pages' page.slug}}{{page.menu_name}}{{/link-to}}</li>
{{/each}}
<li id="add-page-button"><a data-tooltip title="Click here to add a new page to your site." href="#" data-reveal-id="addPageModal" data-reveal>+</a></li>
</ul>
doesn't get notified that a page is missing and so it doesn't update the nav menu by removing that page from the list. Adding works fine, the nav list is updated when a new page is added, but I'm doing that at the ApplicationController level like so:
addNewPage: ->
# Get the site
site = #get('model')
# Get all the pages associated with the site
pages = site.get('page')
title = this.get('newTitle')
if (!title.trim())
return
slug = this.get('newSlug')
if (!slug.trim())
return
menu_name = this.get('newMenuName')
if (!menu_name.trim())
return
# Create a new page passing in our title, slug and menu_name
pages.create({title: title, slug: slug, menu_name: menu_name})
# Save the pages
pages.save()
if (pages.isError)
console.log(pages.errors)
else
#set('newTitle', '')
#set('newSlug', '')
#set('newMenuName', '')
$('#addPageModal').foundation('reveal', 'close')
And this is my deletePage code on the PagesController (sorry, I've got a mix of JS and CoffeeScript):
deletePage: function (slug) {
var page = this.get('model');
this.get('ApplicationController').deletePage(page);
this.toggleProperty('isEditingTitle');
this.toggleProperty('isShowingDeleteConfirmation');
this.transitionToRoute('site.index');
}
When I try this, Ember rather helpfully lets me know I need to add needs but again, when I do that, my Pages template is blank.
Oh yes, I have even tried setting the target attribute on the button calling the deletePage action.
I realize what I need to do is somehow access the Site model in my PagesController, but how should I go about that.
This post has taken on epic proportions. Sorry about that. Thanks again for any help.
needs doesn't do anything on the route, if you want to access a controller from a route you can just use this.controllerFor('application')
Additionally when using needs in a controller definition it should be like this needs: 'application' or needs: ['foo', 'application']...
Okay, all, I figured it out. For posterity's sake (and others who go looking for this). Here's how to delete an object in a model that has a belongsTo association.
ApplicationController:
deletePage: function () {
# First we find the Site which all pages belongTo
# Just ignore the hard-coded '1' it's something I have to do to get it to work
site = App.Site.find(1);
# Then we get the current model, i.e., the 'page' we're on and about to delete
page = this.get('model');
# Then, we get all 'pages'
pages = site.get('page');
# We remove the object from the page which updates the navigation menu
pages.removeObject(page);
# We delete the record and save the 'pages' model
page.deleteRecord();
pages.save();
}
Pages template:
{{#if isShowingDeleteConfirmation}}
<div class="large-7 large-offset-5">
<label class="inline left">Are you sure? (Can't be undone)</label>
<button {{action 'deletePage'}} class='button tiny alert'>Yes</button>
<button {{action 'cancelDeletePage'}} class='button tiny'>Cancel</button>
</div>
{{else}}
<div class="large-2 right">
<button {{action 'showDeleteConfirmation'}} class='button tiny alert'>Delete Page</button>
</div>
{{/if}}
I can show you the attributes and logic behind the if...else statement if you need me to.
Basically, the user clicks on the 'Delete' button, showing the other buttons 'yes' and 'cancel'. Once 'yes' is clicked, the deletePage action gets called.
I found very little documentation on the deleteRecord method and basically had to piece this all together myself. Perhaps there's a better way and this can be refactored. Just let me know in the comments.
I'm going over Ember - Getting Started tutorial but I got stuck. Everything was fine until I got to Displaying-Model-Data section:
First, adding:
Todos.TodosRoute = Ember.Route.extend({
model: function () {
return Todos.Todo.find();
}
});
to the router.js file results in blank window, I found this post which helped returning the layout by adding the next line of code before the code above:
Todo.TodosController = Em.ArrayController.extend({});
Second, as I continue one step forward and try to replace the static index.html with handlebars to make it dynamic (by this code):
<ul id="todo-list">
{{#each controller}}
<li>
<input type="checkbox" class="toggle">
<label>{{title}}</label><button class="destroy"></button>
</li>
{{/each}}
</ul>
again my layout disappears and leaving me with a blank window.
I follow this tutorial step-by-step so don't know what could cause that.
(Found other relevant post but nothing was helpful).
After a few debug time, I found out what was the problem, but not exactly sure why.
I used handlebars.js (V 1.0.0) as published in the official site handlebarsjs.com (this is the one which also linked by the Ember Getting started guide in the dependencies section). After replacing it with the one in cloudflare the layout was brought back to life.
Hope it will help someone.
p.s: after this change the addition of
Todo.TodosController = Em.ArrayController.extend({});
is no longer relevant.
I'm seeing some very strange behavior in this jsfiddle.
I am building an accordion control (hopefully eventually a good contribution to ember-bootstrap), and so I built a view class that uses layout to wrap the contents of the view:
Bootstrap.Accordion = Ember.View.extend({
tagName: 'div',
classNames: 'accordion',
layout: Ember.Handlebars.compile('{{yield}}')
});
Then I use it like so, with the {{#view}} helper, and include an {{#each}} block which will eventually include other views to set up the inside of the accordion control. And in one case so far, I do this twice in the same template, to display different information in two different accordion controls, sort of like this:
{{#view Bootstrap.Accordion}}
{{#each content}}
<div><strong>Field 1:</strong> {{field1}}</div>
{{/each}}
{{/view}}
{{#view Bootstrap.Accordion}}
{{#each content}}
<div><strong>Field 2:</strong> {{field2}}</div>
{{/each}}
{{/view}}
But, as you can see in the fiddle, this produces a very unexpected result. Basically, the second instance of the view is an exact copy of the first. Even the static content inside the {{#each}} block is not right:
Field 1: Instance 1 Field 1
Field 1: Instance 2 Field 1
Field 1: Instance 1 Field 1
Field 1: Instance 2 Field 1
However, if I put something between the {{#view...}} and {{#each}} helpers, it behaves as expected:
{{#view Bootstrap.Accordion}}
Fourth try...
{{#each content}}
<div><strong>Field 4:</strong> {{field4}}</div>
{{/each}}
{{/view}}
So, it looks like something about the similarity of the content directly within the {{#view}} helper causes the result to be cached by Handlebars...or something. That's just a wild hypothesis. Can anyone see what's going wrong here?
(Note that the Bootstrap library is not included in the fiddle, so it can't be that Bootstrap is goofing something up.)
This looks like a bug.
This github issue was created today, only with the {{#linkTo}} helper.
Looks like this is occurring with all block helpers.
I'm new at using ember, but already familiar with it, basically following some tutorials here and there and reading the api docs. But tutorials don't go too deep into more complex topics.
These are the details: I already implemented a web page that shows a list of items. The following are the relevant code excerpts from different parts of the app.
// the data model, the view and the controller
App.Item = DS.Model.extend({
name: DS.attr('string')
});
App.ItemsController = Ember.ArrayController.extend();
App.ItemsView = Ember.View.extend({ templateName: 'items' })
// in the router's corresponding route
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('items', App.Item.find())
}
// in the handlebars template
<ul class="items">
{{#each content}}
<li>{{name}}</li>
{{/each}}
</ul>
The data for this list is loaded remotely via ember-data (notice the App.Item.find() call in the route's connectOutlet method above) and a handlebar template displays it, and dynamically updates the list as the data changes. Up to here this is basic ember.
Now I want to have a text field at the top of the list, and when the user types in this text field, the list should be updated, by filtering and showing only the items with a name that matches what the user is typing. The actual definition of what a matching name is, is irrelevant to this question. It could be those names that contain the typed string, or that start with it.
I know my next step is to include a textfield view on top of the list in the handlebars template:
<div class="search-bar">
{{view Ember.TextField}}
</div>
<ul class="items">
{{#each content}}
<li>{{name}}</li>
{{/each}}
</ul>
So my questions at this point are the following:
How do I refer to this text field in javascript code so I can attach a listener to it to detect when it changes?
And more importantly, what do I need to do inside this event listener so the list gets filtered?
I would like to know how to achieve it filtering data loaded locally, but also how to do it by loading the filtering data remotely everytime the user types.
I actually need to implement something slightly more complex than this, but knowing how to do this will help.
You can have a computed property on your controller that filters the content based on a text field.
App.ItemsController = Ember.ArrayController.extend({
// ...
searchedContent: function() {
var regexp = new RegExp(this.get('search'));
return this.get('content').filter(function(item) {
return regexp.test(item.get('name'));
});
}.property('search', 'content.#each.name')
});
Then you just bind your text field to controller.search. Example: http://www.emberplay.com#/workspace/2356272909
To filter data remotely you should have ember data load more items every time search changes. This can be done by sending an event to the router every time there is a change.