How can View properties be used in its template? - javascript

App.FolderListItemView = Ember.View.extend({
templateName: 'folder-list-item',
tagName: 'li',
classNames: ['folder'],
classNameBindings: ['opened'],
opened: false,
click: function (e) {
this.set('opened', !this.get('opened'));
}
});
<script type="text/x-handlebars" data-template-name="folder-list-item">
<i {{bind-attr class="opened:icon-content-plus:icon-content-minus"}}></i>
...
</script>
I would like to change the icon (plus/minus) according to the value of 'opened' of the view.
The bind-attr does not work. How should I deal with this?

You need to use view.opened property in your template
<script type="text/x-handlebars" data-template-name="folder-list-item">
<i {{bind-attr class="view.opened:icon-content-plus:icon-content-minus"}}></i>
...
</script>

Related

Calling code after {{#each}} has rendered in ember.js?

I am trying to call some code after an Ember {{#each}} tag has finished looping through its items. I have seen other questions that looked similar and the answer always implemented didInsertElement on the view. This does not seem to work for me as I am trying to access html objects that are not rendered with the view because they are in the {{#each}}.
Here is what my html looks like.
<script type="text/x-handlebars" id="user">
{{#if isEditing}}
<div class="well">
{{partial 'user/edit'}}
<button {{action 'doneEditing'}} class="btn btn-default">Save</button>
<button {{action 'cancelEditing'}} class="btn btn-default">Cancel</button>
</div>
{{else}}
<button {{action 'edit'}} class="btn btn-default">Edit</button>
{{/if}}
</script>
<script type="text/x-handlebars" id="user/edit">
{{#view 'editor'}}
<div id="sList" class="btn-group-vertical" role="group">
{{#each s in model}}
<button class="btn btn-default">
{{s.theme}}
</button>
{{/each}}
</div>
{{/view}}
</script>
And my javascript
App.UserRoute = Ember.Route.extend({
model: function(params) {
return this.store.all('strength')
}
});
App.UserController = Ember.ObjectController.extend({
isEditing: false,
actions: {
edit: function(){
this.set("isEditing", true);
},
doneEditing: function(){
this.set("isEditing", false);
},
cancelEditing: function(){
this.set("isEditing", false);
}
}
});
App.EditorView = Ember.View.extend({
didInsertElement: function() {
//Do something to the button elements
}
});
When I try to run this, as soon as I hit the edit button and the partial loads, I get an error in the console after didInsertElement tried to access the button elements. It as if the elements in the div have not rendered yet. So how can I tell if the {{#each}} is done inserting elements into the html? I know this may be confusing but any and all help is appreciated.
Schedule your code in the afterRender queue to run after the contents of the view has been rendered.
App.EditorView = Ember.View.extend({
didInsertElement: function() {
Ember.run.schedule('afterRender', function() {
this.$().find('button').each(function() {
// $(this) is a button in this function
});
}.bind(this));
}
});

Backbone/marionette: adding an element before adding the collection

I'm trying to use twitter bootstrap, backbone and marionette for a form. I want a label and a select next to it. I was able to display the select but now am having trouble adding a label before the select element.
var app = new Marionette.Application();
app.addRegions({
region1: "#region1"
});
//.. model and collection here
app.typeView = Marionette.ItemView.extend({
tagName: "option",
template: "#item",
initialize: function(options){
this.$el.attr('value', options.model.get('id'));
}
});
/* COLLECTION VIEWS */
app.listview = Marionette.CollectionView.extend({
tagName: "select",
id: 'type-select',
itemView: app.typeView,
});
app.on("initialize:after", function() {
//somelist is defined here
var lv = new app.listview({
collection: somelist
});
app.region1.show(lv);
});
Here is the html
<div id="region1" class="container">
</div>
<script type="text/template" id="item">
<%= name %>
</script>
I want it to look like:
<div id="region1" class="container">
<label for="type-select">Name</label>
<select id="type-select"><option>...</option></select>
</div>
But it always looks like:
<div id="region1" class="container">
<select id="type-select"><option>...</option></select>
</div>
Try using a composite view:
<script type="text/template" id="select-field">
<label for="type-select">Name</label>
<select id="type-select"></select>
</script>
app.listview = Marionette.CompositeView.extend({
template: "#select-field",
itemView: app.typeView,
itemViewContainer: "select"
});

emberjs append works but raises Assertion Failed error

I'm new to ember I am trying to append a template to another and it seems to work but it raises an error, can you please explain why?
The error:
Assertion failed: You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead
This is the code in app.js
App.NewStickie = Ember.View.extend({
click: function(evt){
var stickie = Ember.View.create({
templateName: 'stickie',
content: 'write your notes here'
});
stickie.appendTo('#stickies');
}
});
These are the contents of index.html
<script type="text/x-handlebars">
{{#view App.NewStickie}}
<button type="button" class="btn btn-success">
New
</button>
{{/view}}
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
<div id="stickies">
{{#each item in model}}
<div class="stickie" contenteditable="true">
{{#view App.DeleteStickie}}
<span class="glyphicon glyphicon-trash"></span>
{{/view}}
<div contenteditable="true">
{{item.content}}
</div>
</div>
{{/each}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="stickie">
<div class="stickie">
{{#view App.DeleteStickie}}
<span class="glyphicon glyphicon-trash"></span>
{{/view}}
<div contenteditable="true">
{{view.content}}
</div>
</div>
</script>
Each view in ember have a template, for example:
foo_view.js
App.FooView = Ember.View.extend({
templateName: 'foo'
})
foo template
<script type="text/x-handlebars" data-template-name="index">
<div id=myFooView>Foo</div>
</script>
You are trying to insert a view inside of other in that way:
App.BarView.create().appendTo('#myFooView')
This isn't allowed. You can use the {{view}} handlebars helper to render a view inside other like that:
foo template
<script type="text/x-handlebars" data-template-name="index">
<div id=myFooView>
Foo
{{view App.BarView}}
</div>
</script>
But I think that you want this working dynamically. So you can use the ContainerView, like described by the error message:
App.StickiesView = Ember.ContainerView.extend({
click: function() {
var stickie = Ember.View.create({
templateName: 'stickie',
content: 'write your notes here'
});
this.pushObject(stickie);
}
})
I see in your code a lot of views with the click event, ember encourage you to use actions, this give more flexibility, error/loading handling etc. I think is a good idea to use it.
I hope it helps
You should probably read this guide that explains that ContainerView is. Also, I don't think it's necessary to create another View to append a template to another template.

populating select box using emberjs select view

I working on a toy app to learn ember. I have recipes that belong to styles. A style just consists of a name attribute (and the implicit id as well). I am having trouble setting up a select field in the new template to select which style the recipe is.
Here's what I currently have for my routes, controllers and templates:
Routes:
App.RecipesNewRoute = Ember.Route.extend({
model: function() {
return App.Recipe.createRecord({
title: '',
description: '',
instructions: ''
});
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
Controllers:
App.RecipesController = Ember.ArrayController.extend({});
App.RecipesNewController = Ember.ObjectController.extend({
create: function() {
this.transitionToRoute('recipes.show', this.content);
},
cancel: function() {
this.content.deleteRecord();
this.transitionToRoute('recipes.index');
},
buttonTitle: 'Add Beer Recipe'
});
Templates:
recipes/new and form partial:
<script type="text/x-handlebars" data-template-name="recipes/new">
{{ partial 'recipes/form' }}
</script>
<script type="text/x-handlebars" data-template-name="recipes/_form">
{{ title }}
<form>
<fieldset>
<div>
<label {{bindAttr for="titleField.elementId"}}>Title</label>
{{view Ember.TextField valueBinding='title' name='title' viewName='titleField'}}
</div>
<div>
<label>Style</label>
{{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='App.Style.find()'}}
</div>
<div>
<label {{bindAttr for='descriptionField.elementId'}}>Description</label>
{{view Ember.TextArea valueBinding='description' name='description' viewName='descriptionField'}}
</div>
<div>
<label {{bindAttr for='instructionsField.elementId'}}>Instructions</label>
{{view Ember.TextArea valueBinding='instructions' name='instructions' viewName='instructionsField'}}
</div>
<a href='#' {{action create target='controller'}}>{{buttonTitle}}</a>
</fieldset>
</form>
<a href='#' {{action cancel target='controller'}}>Cancel</a>
</script>
The main takeaway being: {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='App.Style.find()'}}
I've also tried setting up a styles variable in the controller:
In RecipesNewController: styles: App.Style.find() and in the view {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='styles' optionValuePath='styles.id' optionLabelPath='styles.name'}}
as well as in the route's setupController: controller.set('styles', App.Style.find()) (with the same view code as with setting it in the controller).
Any help would be great, I'm sure it's something simple.
Thanks
Update
So I think I've almost got it.
In my RecipesNewRoute's setupController I have controller.set('styles', App.Style.find());. Then in my view I have {{view Ember.Select valueBinding='style' name='style' viewName='styleField' contentBinding='controller.styles' contentValuePath='content.id' contentLabelPath='content.name'}}. Now my problem is I have the select box being populated, except the label and value are set to <App.Style:ember407:100> instead of the id and the name.
The Ember Select uses optionValuePath and optionLabelPath, and it appears you are using contentValuePath and contentLabelPath.
An example jsFiddle here: http://jsfiddle.net/nrionfx/BJwLR/2/
From the ember docs: http://emberjs.com/api/classes/Ember.Select.html
{{view Ember.Select
contentBinding="App.programmers"
optionValuePath="content.id"
optionLabelPath="content.firstName"}}

How to show/hide a View in EmberJS

I want a View to be hidden on load, then when a user clicks on a link it will display the view. Can someone review my code and let me know what I have done wrong?
App.parentView = Em.View.extend({
click: function() {
App.childView.set('isVisible', true);
}
});
App.childView = Em.View.extend({
isVisible: false
});
Here is the jsfiddle: http://jsfiddle.net/stevenng/uxyrw/5/
I would create a simple isVisibleBinding to the view you want to hide/show, see
http://jsfiddle.net/pangratz666/dTV6q/:
Handlebars:
<script type="text/x-handlebars" >
{{#view App.ParentView}}
<h1>Parent</h1>
<div>
<a href="#" {{action "toggle"}}>hide/show</a>
</div>
{{#view App.ChildView isVisibleBinding="isChildVisible" }}
{{view Em.TextArea rows="2" cols="20"}}
{{/view}}
{{/view}}
</script>​
JavaScript:
App.ParentView = Em.View.extend({
isChildVisible: true,
toggle: function(){
this.toggleProperty('isChildVisible');
}
});
App.ChildView = Ember.View.extend();
A note about your naming conventions: classes should be named UpperCase and instances lowerCase. See blog post about this.
​
Valuebinding for some reasons didnt work for me so observing parentView property inside childView did the trick for me
Handlebar:
<script type="text/x-handlebars" >
{{#view App.ParentView}}
<h1>Parent</h1>
<div>
<a href="#" {{action "toggle"}}>hide/show</a>
</div>
{{#view App.ChildView }}
{{view Em.TextArea rows="2" cols="20"}}
{{/view}}
{{/view}}
</script>​
Coffeescript:
App.ParentView = Em.View.extend
isChildVisible: true
toggle: ->
#toggleProperty 'isChildVisible'
App.ChildView = Em.View.extend
isVisible: (->
#get('parentView.isChildVisible')
).property '_parentView.isChildVisible'

Categories

Resources