Ember.js binding array element to view - ember.js

I have the following code in a ember.js template. userController is an ArrayController, with multipe "users" within.
{{#each CollaborativeEditor.userController}}
{{#view CollaborativeEditor.OnlineUserView userBinding="this"}}
<div class="avatar">
<div class="avatar_name">{{name}}</div>
<div class="avatar_status">{{status}}</div>
</div>
<div id="dropdown-1">
<ul>
<li><a href="#" {{action startChat target="onlineUser"}}>Talk to </a></li>
</ul>
</div>
{{/view}}
{{/each}}
This is the code of the respective view:
CollaborativeEditor.OnlineUserView = Ember.View.extend({
tagName: 'li',
startChat : function() {
console.log(this.get('user'));
}
});
Although, the name and status is set correctly for each user, the startChat action attached to the link always prints the first user of the array to the console.
What is wrong with the binding ?

Thanks a lot for your request, to put it in a jsfiddle !
While I was trying to reproduce the error there, I realized the problem, and it has nothing to do with ember.
The div with id="dropdown-1" was called from another link and it was always the same id, therefore always the same action with its user-binding.
Now I've bound the Id to the user-object and it works perfectly.

Related

Why didInsertElement hook trigger before elements rendered inside {{#each}} block?

I'm new to Ember, I want to add some query DOM manipulation code to the element in the {{#each}} block. So I google it up and found the solution from this guide:
views/products/index.js
import Spinner from 'appkit/utils/someJqueryCode';
Ember.View.reopen({
didInsertElement : function(){
this._super();
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
},
afterRenderEvent : function(){
// implement this hook in your own subclasses and run your jQuery logic there
}
});
export default Ember.View.extend({
afterRenderEvent: function() {
Spinner();
}
});
templates/products/index.hbs
<div class='panel panel-default products'>
<div class='panel-heading'>
<h2 class='panel-title'>Our Prodcuts</h2>
</div>
<div class='panel-body'>
<ul class='row'>
{{#each}}
<li class='col-md-4'>
<div class='thumbnail'>
<img {{bind-attr src=url alt=alt}} />
</div>
<div class='caption'>
<h3 class='name-me'>{{name}}</h3>
<p>{{description}}</p>
<div class='row no-gutter'>
<div class='col-xs-3'>
<button class='btn btn-primary'>Buy</button>
</div>
</div>
</div>
</li>
{{/each}}
</li>
</div>
</div>
But I seems after the point when afterRenderEvent() is triggered, all the elements in the {{#each}} block hasn't been rendered to the DOM yet, thus, the jQuery code return undefined
What's the right way to do it?
Your view's didInsertElement hook will fire as soon as the application route is rendered, which happens before the index route. You might think that putting it in the index.js file will work, but it's going to just extend the default application view behavior.
You need to create a more focused view that lives within your index.hbs file. One that is only concerned with your spinner jQuery doohickey. That, and an each/else conditional could work nicely here. For example:
{{#each}}
{{#view App.SpinnerDoohickeyView}}
<li class='col-md-4'>
<div class='thumbnail'>
<img {{bind-attr src=url alt=alt}} />
</div>
<div class='caption'>
<h3 class='name-me'>{{name}}</h3>
<p>{{description}}</p>
<div class='row no-gutter'>
<div class='col-xs-3'>
<button class='btn btn-primary'>Buy</button>
</div>
</div>
</div>
</li>
{{/view}}
{{else}}
<li>Empty collection!</li>
{{/each}}
Notice that I've wrapped each list item in its own view. You could wrap the whole ul if you wanted... this is just an example. The idea is that you are only creating views when you have a model.
And now you can define the view, and simply use the didInsertElement hook to work with jQuery:
App.SpinnerDoohickeyView = Ember.View.extend({
didInsertElement: function () {
this.$('li').css('background', 'blue');
}
});
If you have a model to render, jQuery should be able to safely access it this way. Good luck!
Here's some further reading and some code from the Ember folks that looks like what I've shown you here: http://emberjs.com/guides/views/handling-events/

Blank entries added to Ember Data

I'm currently learning EmberJS as I feel single page apps using a JS framework and consuming a backend API are the way things are going to be moving. I've recently learned Laravel without any particular problem, but I'm finding Ember remarkably difficult.
Most of my issues I've worked through, but I'm having one that I don't even know how to debug.
Essentially, my interface displays a list of clients. Clicking on a name goes to that client. All of this is connected to a PHP/Laravel based API, which is working entirely fine and in the right format. Data is not the problem here.
That said, I should point out that my data does not have an "id" strictly, but connects using something more like a guid: 912ec803b2 instead of 231. The API is built to handle that, and it's a requirement. It also means there is no "id" in the data, but there is a "hash" which may potentially be confusing ember in some way?
This basic functionality just doesn't seem to work correctly. When a user clicks the Client link, it does navigate to the client. But it's adding a blank row to the clients list page, then going to the client page. Returning to the clients page shows the extra blank row, and the same thing will recur if clicked again.
Checking the Ember inspector in Chrome shows the data collection increasing, with blank data being added. I'm not sure why.
I've posted the code here. This should be all that's necessary. Is this behaviour a common trap for young players, of have I done something unusual?
Apologies for the length, just didn't want to omit something relevant.
// app.js
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function() {
this.resource('clients', function() {
this.route('new');
});
this.resource('client', { path: 'clients/:client_hash' }, function(){
});
});
App.Store = DS.Store.extend({
url: 'http://myclientinfo'
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api'
});
App.Client = DS.Model.extend({
name: DS.attr('string'),
hash: DS.attr('string')
});
App.ClientsRoute = Ember.Route.extend({
model: function(){
return this.store.find('client');
}
});
App.ClientIndexRoute = Ember.Route.extend({
model: function(params){
return this.store.find('client', params.client_hash);
}
});
// templates
<script type="text/x-handlebars">
<div class="container">
<ul class="nav nav-pills">
<li>{{#link-to 'clients'}}Clients{{/link-to}}</li>
</ul>
{{outlet }}
</div>
</script>
<script type="text/x-handlebars" id="client">
client outer
{{outlet}}
</script>
<script type="text/x-handlebars" id="clients">
client outer
{{outlet}}
</script>
<script type="text/x-handlebars" id="client/index">
client index
<h1>{{name}}</h1>
</script>
<script type="text/x-handlebars" id="clients/index">
<div class="row">
<div class="col-md-12 col-lg-12">
<h1>All Clients</h1>
</div>
</div>
<div class="row"><div class="col-md-12 col-lg-12">
{{#link-to 'clients.new' class="btn btn-info add-btn"}}
<i class="fa fa-plus"> Add new client</i>
{{/link-to}}
</div></div>
<div class="row">
<div class="col-md-12 col-lg-12">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
{{#each item in model}}
<tr>
<td>{{#link-to 'client' item.hash}}{{item.name}}{{/link-to}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</script>
Ok, just following this up since I've sorta kinda fixed it.
this.resource('client', { path: 'clients/:client_hash' }, function(){
This line did more than I thought. I thought it was more of a defining placeholder, like in Laravel.
Route::get('user/{id}', function($id)
The name used, whether it's {id} or {hash} or {poopypants} doesn't matter because it's being defined and used at that time.
I changed it to this:
this.resource('client', { path: 'clients/:client_id' }, function(){
That lets the default link-to helpers work, rather than simply getting "undefined" like they were for everything else I tried. The name of that segment is actually meaningful, it is used to derive the actual values for the segment.
With this done I was able to refactor the loop code slightly, fixing the helper.
{{#each item in model}}
<tr>
<td>{{#link-to 'client' item}}{{item.name}}{{/link-to}}</td>
This could also be refactored down more.
{{#each}}
<tr>
<td>{{#link-to 'client' this}}{{name}}{{/link-to}}</td>
Note that {{#link-to 'client' this}} works, while {{#link-to 'client' this.id}} mostly works, but gets the bug stated in the OP. A bug that I believe has taken me three DAYS to fix.

How can I click to edit on a text field with ArrayController?

I was following the tutorial but the tutorial is for the object controller. In an Array controller how do I properly pass in the object for the text field so it triggers the update for that model object?
Right now I can double click, and then type in some value, and if I hit enter I get the value plus undefined method set.
Uncaught TypeError: Object asdasdasdasdasd has no method 'set'
I guess it's passing the raw value into the controller and then trying to run methods off of that. How do I get it to pass the actual model?
View:
<ul>
{{#each}}
<li {{bind-attr class="isEditing:editing"}} {{action "editWorkout" this on="doubleClick"}}>
{{#if isEditing}}
{{view Ember.TextField class='edit' action="updateWorkout"}}
{{else}}
{{#link-to 'workout' this}} {{title}} {{/link-to}}
{{/if}}
</li>
{{/each}}
<li>
{{newWorkoutName}}
</li>
</ul>
Controller:
EmberWorkouts.WorkoutsController = Ember.ArrayController.extend
actions:
editWorkout: (workout) ->
workout.set('isEditing', true)
createWorkout: ->
title = #get('newWorkoutName')
workout = #store.createRecord('workout', title: title)
#set('newWorkoutName', '')
workout.save()
updateWorkout: (workout) ->
workout.set('isEditing', false)
workout.save()
isEditing: false
Repo here if you want to investigate: https://github.com/ecl1pse/ember-workouts/tree/master/app
You can specify an itemController in your each and then use an ObjectController for each item in your list.
{{#each itemController="workout"}}
<li {{action editWorkout on="doubleClick"}}>
<!-- Other stuff goes here -->
</li>
{{/each}}
EmberWorkouts.WorkoutsController = Ember.ObjectController.extend({
editWorkout : function(){
this.set('isEditing', true);
}
});
Here's a JSBin of the general idea : http://jsbin.com/ucanam/1038/edit

Ember.js: Toggle Nested Views

I have a header with some login/signup forms that popup when you click the respective buttons.
While it was working fine using just jQuery, I've now started to integrate Ember into the application and I'm running into some trouble with some simple toggle functionality.
Here's the basic HTML markup:
<header>
<h1>Page Title<h1>
<nav>
<a id="toggles-login" class="button {{active_class_binding}}">Login</a>
<a id="toggles-signup" class="button {{active_class_binding}}">Signup</a>
</nav>
<div id="popup-forms">
<div id="login-form"></div>
<div id="signup-form"></div>
</div>
<header>
I'm completely new to Ember and I really have no idea how to set this up. The only thing I want is to be able to set the popup forms up as Ember.View objects and toggle them with some action helpers.
I really am lost on this one.
A simple solution would be to trigger simple actions to show the respective forms:
<a id="toggles-login" class="button {{active_class_binding}}" {{action showLoginForm target="view"}}>Login</a>
<a id="toggles-signup" class="button {{active_class_binding}}" {{action showSignupForm target="view"}}>Signup</a>
The corresponding view would have to implement both actions:
App.YourView = Ember.View.extend({
showLoginForm : function(){
this.$("#login-form").toggle();
},
showSignupForm : function(){
this.$("#signup-form").toggle();
}
});

Bootstrap accordion with ember

Hi there i have a small question that belonging to my small ember application.
JSFiddle upload is here. I used bootstrap accordion to visualize my tickets. When i click on the "click" it adds another accordion into my view. But sadly it cannot be opened or used. Every accordion i dynamically created cannot be opened or closed. There is no error or exception thrown and from my point of view everything should work fine. My click-function looks like this:
click: function() {
this.counter++,
name = this.name+this.counter.toString(),
tre = App.Ticket.create({
Text: "try",
id: name
});
this.pushObject(tre);
}});
The belonging html is here:
<div class="accordion-group">
{{#each content}}
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" {{bindAttr href="id"}}>
Ticket ID/Störfall
</a>
</div>
<div {{bindAttr id="id"}} class="accordion-body collapse in ">
<div class="accordion-inner">
{{Text}}
</div>
</div>
{{/each}}
</div>
I hope you can help me.
You can add an action helper to the accordion link title
<a class="accordion-toggle" data-toggle="collapse"
data-parent="#accordion2"
{{bindAttr href="item.id"}}
{{action "click" item target="view"}}>
Ticket ID/Störfall
</a>
Then implement a click handler event in your view
App.TicketView = Em.View.extend({
click:function(context) {
var el = this.$('a[href='+context.get('id')+']');
el.toggleClass('collapsed');
this.$('#'+el.attr('href')).toggleClass('in');
}
});
Here's a working fiddle