UPDATE
You finally decide it's time to post on SO, you do so, and then you find a stupid error. Taken care of. Remove -> from Ember.Table.HeaderCell.extend ->.
I am attempting to customize my header cells for sorting purposes using the example in #183. Unfortunately, The template I'm passing to headerCellViewClass is not being rendered.
I added in a random class and "asdf" just to prove to myself that my booleans weren't the issue. I can access out headerCellName, sorted, and supportSort through the console, and they all have their expected values.
Columns created in a loop in my controller
Ember.Table.ColumnDefinition.create App.SortableColumnMixin, {
columnWidth: 100
textAlign: 'text-align-left'
tableCellViewClass: 'App.ReportValueCell'
headerCellName: columnName
contentPath: columnName
isSortable: yes
}
You'll notice here that I set a custom tableCellViewClass. That one functions perfectly.
SortableColumnMixin
App.SortableColumnMixin = Ember.Object.create(
supportSort: true
sorted: false
headerCellViewClass: 'App.SortableHeaderCell'
)
App.SortableHeaderCell
App.SortableHeaderCell = Ember.Table.HeaderCell.extend ->
templateName: 'table/sortable-header-cell'
table/sortable-header-cell.hbs
<div class="ember-table-content-container" {{action sortByColumn view.content}}>
<span class="ember-table-content" class="21340987235">
asdf
{{view.content.headerCellName}}
{{#if view.content.supportSort}}
<div style="float:right;">
{{#if view.content.sorted}}
{{#if sortAscending}}
<div style="cursor:s-resize;" title="Ascending">
↑
</div>
{{else}}
<div style="cursor:n-resize;" title="Descending">
↓
</div>
{{/if}}
{{else}}
⇅
{{/if}}
</div>
{{/if}}
</span>
</div>
Inspector
Inspector
Does anyone know better than I why this might be?
UPDATE
You finally decide it's time to post on SO, you do so, and then you find a stupid error. Taken care of. Remove -> from Ember.Table.HeaderCell.extend ->.
Related
I have an array of strings passed as an argument to a component, inside the component I am using "each" helper to render each string in a text input. I tried the following approach.
MainComponent.hbs
<Component #model={{model}}/>
//Eg: model.list = ["Jack", "Sparrow"];
Component.hbs
<div>
{{#each this.args.model.list as |name|}}
<div>
<PaperInput #value={{ name }}
#placeholder="Enter a Name"
#onChange={{action (mut name)}}/>
</div>
{{/each}}
</div>
I am running into the error "Uncaught (in promise) Error: Assertion Failed: You can only pass a path to mut". Would really appreciate if anyone can let me know What's going wrong here.
The value that are derived from helpers (each in your case) cannot be mutated using mut helper as the helpers usually don't pass down or hold the values to change the original property.
For instance,
It makes sense if we are mutating a value as below where capitalize is a helper:
<button {{action (mut name) (capitalize name)}}>
Capitalize
</button>
however, below snippet does't fit right as helper returns the value one way!
<button {{action (mut (capitalize name)) name}}>
Capitalize
</button>
the same thing is going on with the each helper and the looped through value cannot be mutated! This code comment might be useful for further digging.
You can change your snippet to handle the onChange in the backing component class instead:
<div>
{{#each this.args.model.list as |name index|}}
<div>
<PaperInput #value={{ name }}
#placeholder="Enter a Name"
#onChange={{this.changeName index}}/>
</div>
{{/each}}
</div>
// component.js
changeName(index, nameToBeUpdated) {
// change the name here...
}
Figured it out. Posting the full implementation for the benefit of others. I passed down the index value to component's action as suggested in Gokul's answer but ran into another problem. There was no straight forward method to change the array's value. So I used the Mutable Array's replace method do that. That again caused another problem, every time I entered a character in the text input it was chaning the array value and re rendering the list which took the focus out of the input. So in "each" helper I had to set key="#index" which tells the helper to rerender only if there is a array index change, not the value.
Component.js
#action
updateName( index, name ) {
this.args.model.list.replace(index, 1, [name]);
}
MainComponent.hbs
<Component #model={{model}}/>
Component.hbs
{{#each this.args.model.list key="#index" as |name index|}}
<div>
<PaperInput #value={{ name }}
#placeholder="Enter a Name"
#onChange={{action this.updateName index}}/>
</div>
{{/each}}
I'm trying to create a custom login only using Facebook and only looking for two endpoints: "name" and "avatar".
For starters I don't know if "avatar" is even a real endpoint name but that's what I'm trying to access.
I have created a test app on FB, I have also installed all of the Meteor packages that I need so the groundwork is done.
I've create the following template:
<template name="Login">
<h2>Login</h2>
{{#if currentUser}}
{{currentUser.services.facebook.name}}
{{currentUser.services.facebook.avatar}}
<button id="logout">Logout</button>
{{else}}
<button id="facebook-login" class="btn btn-default">Login with Facebook</button>
{{/if}}
</template>
and then in the SERVERS directory I have create a .js file to store my API keys.
My questions:
My first question is where to find the names of these endpoints as I've been going the entire documentation on FB and nothing references "name" or "avatar" so the first thing I need to understand is where to find these endpoints as I haven't been able to locate even the "name".
Second question is the API shows JSON objects and that's usually how you would hookup your endpoints but in Meteor since all of that is abstracted it's unclear where this "facebook" object exists to then study more in depth the nested properties like "name" and "avatar" (which again i'm uncertain if that is the correct name for that property). I'm assuming because I'm using Meteor that calling an endpoint like this {{currentUser.services.facebook.name}} is enough, am I thinking about this correctly?
Final question is if I have to call these endpoints like this inside of my template:
{{#if currentUser}}
{{currentUser.services.facebook.name}}
{{currentUser.services.facebook.gender}}
<button id="logout">Logout</button>
{{else}}
<button id="facebook-login" class="btn btn-default">Login with Facebook</button>
{{/if}}
Then even if I wrap my facebook name and gender in their own divs like this:
{{#if currentUser}}
<div class="name">
{{currentUser.services.facebook.name}}
</div>
<div class="avatar">
{{currentUser.services.facebook.avatar}}
</div>
<button id="logout">Logout</button>
{{else}}
<button id="facebook-login" class="btn btn-default">Login with Facebook</button>
{{/if}}
This still doesn't make it very obvious to me how to move it say the header?
So in other words how would I have the user login from the main body of the page, yet after they login have the actually username and avatar up in the header?
There is no obvious way for me to do this.
What am I missing? How would I DOM shuffle to move the .name and .avatar divs to the header when I just logged the user in via the body of the page?
Does this make sense?
My hunch is that I would have to create another template within the header that calls these values?
Anyone play around with this that could offer some insight?
Thank you.
The first part of your question is answered here:
https://stackoverflow.com/a/15019052/1327678
The answer to the second part of your question is in the docs. You could make a template helper to check this:
Template.header.helpers({
currentUser: function(){
if(Meteor.user()){
return true;
}
else{
return false;
}
}
});
And in your template just write:
{{#if currentUser}}
{{!-- your facebook code here --}}
{{/if}}
I have a view template 'person' defined as:
....
{{input type="checkbox" checked=isEditing}}
<table><tbody>
{{#if 'isEditing'}}
<tr><td><strong>Id</strong></td>{{id}}</td></tr>
<tr><td><strong>number</strong></td><td>{{repNumber}}</td></tr>
<tr><td><strong>First Name</strong></td><td>{{firstName}}</td></tr>
<tr><td><strong>Middle Name</strong></td><td>{{middleName}}</td></tr>
<tr><td><strong>Last Name</strong></td><td>{{lastName}}</td></tr>
<tr><td><strong>alias 1</strong></td><td>{{alias1}}</td></tr>
<tr><td><strong>alias 2</strong></td><td>{{alias2}}</td></tr>
{{else}}
<tr><td><strong>Id</strong></td>{{id}}</td></tr>
<tr><td><strong>number</strong></td><td>{{repNumber}}</td></tr>
<tr><td><strong>First Name</strong></td><td>{{input type="text" value=firstName</td></tr>
<!-- input counterparts etc -->
{{/if}}
</tbody></table>
....
And a PersonController defined as such:
App.Editable = Em.Mixin.create({
isEditing: false,
actions: {
edit: function() {
this.toggleProperty('isEditing');
}
}
});
App.PersonController = Em.ObjectController.extend(SS7.Editable);
The route is defined like so:
App.Router.map(function(){
...
this.resource('person', { path: '/person/:person_id'}, function(){});
});
Now every time I first toggle the isEditing value using the checkbox, all of my elements suddenly disappear off the screen. The Ember inspector shows that the viewTree is currently on the person route with the PersonController and the model contains the correct person. however i can now no longer see any of the two outputs (tr,tds) until i hard refresh the page.
Can anyone help to explain this behaviour. I'm unsure how this could be happening.
ok, these type of issues are hard to find, the thing is that you are missing a < td > and that makes your code fail, well, not fail because it doesn't throw any errors, but it fails when building the HTML, the error is in this part:
{{#if isEditing}}
<tr><td><strong>Id</strong></td>{{id}}</td></tr>
<tr><td><strong>number</strong></td><td>{{repNumber}}</td></tr>
<tr><td><strong>First Name</strong></td><td>{{firstName}}</td></tr>
<tr><td><strong>Middle Name</strong></td><td>{{middleName}}</td></tr>
<tr><td><strong>Last Name</strong></td><td>{{lastName}}</td></tr>
<tr><td><strong>alias 1</strong></td><td>{{alias1}}</td></tr>
<tr><td><strong>alias 2</strong></td><td>{{alias2}}</td></tr>
{{else}}
check the first < tr >, you're missing a < td > before {{id}}, it should be like this:
{{#if isEditing}}
<tr><td><strong>Id</strong></td><td>{{id}}</td></tr>
<tr><td><strong>number</strong></td><td>{{repNumber}}</td></tr>
<tr><td><strong>First Name</strong></td><td>{{firstName}}</td></tr>
<tr><td><strong>Middle Name</strong></td><td>{{middleName}}</td></tr>
<tr><td><strong>Last Name</strong></td><td>{{lastName}}</td></tr>
<tr><td><strong>alias 1</strong></td><td>{{alias1}}</td></tr>
<tr><td><strong>alias 2</strong></td><td>{{alias2}}</td></tr>
{{else}}
We're building app that allows users to post messages to various social media outlets. Our designer has created a series of interactions which allow users to change various settings in their application by use of sliding panels. I've done a quick screen cap to illustrate:
http://screencast.com/t/tDlyMud7Yb7e
The question I have is one of architecture. I'm not sure whether I should be using a View or a Controller (or both) to store some of the methods these panels will contain. Here's the HTML for the panels. They're not currently in a script tag or view:
<div id="panel-account-settings" class="panel closed">
<div class="panel-inner">
<i class="icon-cancel"></i>close
<h3>Account Settings</h3>
Google Analytics
Link Shortening
Disconnect Account
</div>
<div id="panel-google-analytics" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Google Analytics</h3>
<div class="toggle">
<label>Off</label>
</div>
<p>We <strong>won't</strong> append stuff to your links, so they <strong>won't</strong> be trackable in your Google Analytics account.</p>
<img src="{{ STATIC_URL }}images/ga-addressbar.png" />
</div>
<div id="panel-disconnect" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Disconnect This Account</h3>
<p>If you disconnect this account you will lose all the metrics we tracked for each message. Are you absolute sure you want to get rid of them?</p>
<div class="button-group">
Disconnect
</div>
</div>
</div>
The gear icon shown in the video is contained with the accounts template
<script type="text/x-handlebars" data-template-name="accounts">
{{#each account in controller}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>#{{account.username}}</p>
<i class="icon-cog" {{action "openPanel" Social.SettingsView account }}></i>
</div>
{{/each}}
</script>
which has a bare bones controller
Social.AccountsController = Ember.ArrayController.extend({
openPanel: function(view,account){
console.log(view,account);
$(this).parents(".item-account").addClass("active");
$("#panel-account-settings").prepareTransition().removeClass("closed");
}
});
as well as a Route and a Model. Given the interaction I'm looking to accomplish, my question is where should I be putting the pieces and parts? At a minimum I need to pass in the current Account model so that I know which account I'll be applying changes to. I thought about creating a mainPanel view which would contain the other view...something like this:
<script type="text/x-handlebars" data-template-name="panelView">
<div id="panel-account-settings" class="panel closed">
{{ partial "panelSettingsView" }}
{{ partial "panelAnalyticsView" }}
{{ partial "panelDisconnectView" }}
</div>
</script>
and then the action helper on the gear icon could pass in the account AND the required view. But I'm not sure if that's the right approach. I'd appreciate some input or suggestions. Thanks.
UPDATE 1
Ideally I'd like to eventually load in the content of each panel via AJAX but that's a want to, not a need to.
UPDATE 2
I tried creating a PanelView which would contain the logic on which panels to load:
Social.PanelView = Ember.View.extend({
tagName: 'div',
classNames: ['panel-inner'],
openPanel: function(view,account){
console.log(view,account);
}
});
But when I tried to call it from the gear icon I got an error. This:
<i class="icon-cog" {{action openPanel target="Social.PanelView" }}></i>
Threw this error:
Uncaught Error: assertion failed: The action 'openPanel' did not exist on Social.PanelView
Isn't that the correct syntax?
UPDATE 3
Adding version information:
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.1
The best practice is to always put any DOM- or UI-related logic into your view, and leave data representation to the controller (i.e., a reference to a 'selected' item in the controller is a common example).
Your Social.AccountsController.openPanel method has logic that touches the DOM, which is entirely a view concern. A good start would be to move that logic into the view (Social. SettingsView ?).
It'd be a bit easier to understand your goals and offer more suggestions if you had a jsfiddle of what you have so far.
EDIT: Another good practice is to decompose things into very small objects. So you could explore having a selectedAccount ObjectController whose content is the currently chosen Account (and a corresponding View for it).
Edit: Problem was with my own local settings and the way I was including the views in my application. Once I fixed those issues, the problem was resolves. The code here is actually correct. The answer Chrixian provided also work.
I am stuck on something that seems rather simple. I want to access some computed properties of my view constructed inside an each loop in handlebars.
<div class='build-buttons-wrapper'>
<button class="list-builds-button" {{action "toggleBuildsList" target="view"}} ></button>
<button class="build-button" {{action "buildApp" on="click" target="view"}} >Build</button>
</div>
<div class='builds-list'>
<h2 class="build-title">Latest builds</h2>
<ul class="builds-list">
{{#each content}}
{{#view Jimux.BuildView buildBinding="this"}}
<span class="build-date">{{createdAt}}</span>
<a {{bindAttr href="srcArchive"}} class="download-button source">Source</a>
{{! *here are different ways I have tried to access "finished" property* }}
{{log build.view.finished}}
{{log view.finished}}
{{log finished}}
{{log this.finished}}
{{log build.finished}}
{{#if build.finished}}
<div class="build-progressbar"></div>
{{else}}
<div class="build-progressbar"><div class="build-percent" style="width:{{unbound percent}}%"></div></div>
{{/if}}
{{/view}}
{{/each}}
</ul>
</div>
Here is the BuildsView which is using this template:
Jimux.BuildsView = Em.View.extend({
templateName: 'builds'
listVisible: false
classNames: ['builds-view']
buildApp: (view, event, ctx) ->
#get('controller').newBuild()
,
hideList: ->
#set 'listVisible', false
this.$(".builds-list").hide("slide", {direction: "up"}, 300)
,
showList: ->
#set 'listVisible', true
this.$(".builds-list").show("slide", {direction: "up"}, 300)
,
toggleBuildsList: (view, event, ctx) ->
if #get 'listVisible' then #hideList() else #showList()
,
didInsertElement: ->
#hideList() if not #get 'listVisible'
})
And here is the BuildView which is created inside the {{#each}} iterator in the template above.
Jimux.BuildView = Ember.View.extend(
tagName: 'div',
classNames: ['build-item'],
#testBinding: true,
sample: true,
finished: ( ->
return true
#return (#get 'percent') == 100
).property('percent')
)
Everything above works as expected. For example I can access percent property of each child view using {{percent}}. But if I define my own properties inside the Jimux.BuildView as show above, I cant seem to find a way to access them within the handlebars {{#each}} iterator. You can see the different ways I have tried inside the handlebars code with {{log}} statements, all those print undefined in the console. What am I missing here?
I'm assuming the percent property you are referring to is a property of each "content" object you are looping over-- if that's the case making finished look like this:
finished: (->
return #get('context.percent') is 100
).property('context.percent')
You should be able to simply use {{finished}} within the {{#view Jimux.BuildView}} .. {{/view}} block