How do you assign a static data- attribute to an ember view? - ember.js

I need to assign a static data- attribute to an Ember.View, how do I set that in the View object instead of in the {{view }} tag.
App.MessagesFormView = Ember.View.extend({
tagName: 'div',
classNames: ['modal', 'fade'],
didInsertElement: function() {
this.$().modal('show')
},
willDestroyElement: function() {
this.$().modal('hide')
},
})

Unfortunately, I don't have enough reputation to comment on Ola's answer, but I believe a slightly better way to do this is to not use a string (text in quotation marks) to denote the data attribute property name. Instead, write your property name in camelCase and Ember will automatically bind it to the hyphenated attributebinding. For example:
App.MessagesFormView = Ember.View.extend({
tagName: 'div',
attributeBindings: ['data-backdrop'],
dataBackdrop: 'static', // Binds to data-backdrop. Awesome!
});
I hope that makes sense!

This has to be done using both an attributeBindings and data-backdrop or data-whatever property of the Ember.View object.
App.MessagesFormView = Ember.View.extend({
tagName: 'div',
classNames: ['modal', 'fade'],
// Set a data attribute, of a view, requires both an attribute binding and
// an attribute assignment
attributeBindings: ['data-backdrop'],
'data-backdrop': 'static',
didInsertElement: function() {
this.$().modal('show')
},
willDestroyElement: function() {
this.$().modal('hide')
},
})

Related

Ember.js persist classNameBindings on transition to different routes

I'm fairly new to ember and I've been trying to tackle this problem for a couple of days but I can't seem to find a solution anywhere online.
I have a page with a list of all posts, each post has one tag (like a hashtag), either 'Fitness', 'Knowledge' or 'Social'. At the top of the page I have 3 view helpers and each view helper represents a tag (fitness, knowledge or social). These will be used to filter out the posts with that particular tag name.
My problem is that when I click on a view helper I toggle the "isSelected" property to true, which adds the "isSelected" class via classNameBindings. But when I transition to a different route on the site and come back, the "isSelected" property is reset back to false and the "isSelected" class has been removed. How do I keep these values persistent and in-tact for when I revisit the route?
Here's my code:
<script type="text/x-handlebars" data-template-name="global">
<ul class="categories">
<li>{{view App.Tag class="label fitness" text="fitness"}}</li>
<li>{{view App.Tag class="label knowledge" text="knowledge"}}</li>
<li>{{view App.Tag class="label social" text="social"}}</li>
</ul>
</script>
View:
"use strict";
App.Tag = Ember.View.extend({
tagName: 'span',
template: Ember.Handlebars.compile('{{view.text}}'),
classNames: ['label'],
classNameBindings: ['isSelected'],
isSelected: false,
click: function () {
this.toggleProperty('isSelected');
}
});
I have also tried using a controller with actions but that way persisted the "isSelected" property but didn't preserve the addition of the class when I revisited the route.
This may not be ideal, but to save the state of the application, you can put the state in the controller. You probably had a simple implementation, but maybe did not specify the isSelected as a property. The below works and you can view the jsbin here
App = Ember.Application.create();
App.Router.map(function() {
this.route('global');
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
App.GlobalController = Ember.Controller.extend({
activeTags: Ember.A()
})
App.Tag = Ember.View.extend({
tagName: 'span',
template: Ember.Handlebars.compile('{{view.text}}'),
classNames: ['label'],
classNameBindings: ['isSelected'],
isSelected: function () {
console.log("ON CHANGE", this.get('controller.activeTags'));
return this.get('controller.activeTags').contains(this.text);
}.property('controller.activeTags.#each'),
click: function () {
var tagArray = this.get('controller.activeTags');
if (tagArray.contains(this.text))
this.set('controller.activeTags', tagArray.without(this.text))
else
tagArray.pushObject(this.text);
}
});

How to get the parent controller for a custom TextField

I have a simple controller
App.UploadController = Ember.Controller.extend({
toUpload: Ember.A([])
});
I have a template backing this w/ a custom text field
<div>
{{view App.UploadFileView name="file" contentBinding="content"}}
</div>
My custom text field in JS is below. The problem I'm having is that in the change event, I need to push an object into the parent controllers "toUpload" array but when I do a get on the parentView.controller it's undefined. How can I get the parent in this scenario?
App.UploadFileView = Ember.TextField.extend({
type: 'file'
change: function() {
var foo = Ember.Object.create();
this.get('parentView.controller').get('toUpload').pushObject(foo);
}
});
The TextField is a component, so the parent controller doesn't exist, you'd need to use sendAction to get things out of it.
Here's my implementation of the upload button that's just a view.
App.UploadFileView = Ember.View.extend({
tagName: 'input',
attributeBindings: ['type'],
type: 'file',
change: function() {
console.log(this.get('controller'));
}
});
http://emberjs.jsbin.com/oQaReMi/1/edit
If you are using an Ember Component (like TextField for example) you would do this like so
App.UploadFileView = Ember.TextField.extend({
change: function() {
console.log(this.get('targetObject'));
}
});
Note- this is in the current version of ember 1.3.x

Sibling attribute binding in Ember.js

I am a relative Ember newbie, so I may be missing something obvious here. I'm trying to build a custom view for a Bootstrap accordion. I'm using relative views for this, and while it is working, I'd like to understand if I can simplify a little bit. Specifically, I'd like to know if it's possible to make the href computed property of the embedded toggleView dependent on the elementId of the embedded bodyView. Here's the top level view, which is a collection:
App.AccordionView = Em.CollectionView.extend({
headingTemplateName: 'accordion-heading',
innerTemplateName: 'accordion-inner',
tagName: 'div',
classNames: ['accordion'],
contentBinding: 'controller.content',
The item view corresponds to the DOM element with the accordion-group css class.
itemViewClass: Em.View.extend({
tagName: 'div',
classNames: ['accordion-group'],
target: null,
template: Em.Handlebars.compile("{{view view.headingView contentBinding='view.content'}}{{view view.bodyView viewName='bodyViewInstance' contentBinding='view.content'}}"),
headingView: Em.View.extend({
tagName: 'div',
template: Em.Handlebars.compile("{{view view.toggleView contentBinding='view.content'}}"),
classNames: ['accordion-heading'],
toggleView: Em.View.extend({
tagName: 'div', // Can be an 'a'
templateNameBinding: 'parentView.parentView.parentView.headingTemplateName',
classNames: ['accordion-toggle'],
attributeBindings: ['dataToggle:data-toggle', 'dataParent:data-parent', 'href'],
dataToggle: 'collapse',
dataParent: function() {
return this.get('parentView.parentView.parentView.elementId');
}.property(),
Note that I've made the href property dependent on the target field in the item view.
href: function() {
return "#" + this.get('parentView.parentView.target');
}.property('parentView.parentView.target')
})
}),
bodyView: Em.View.extend({
tagName: 'div',
template: Em.Handlebars.compile("{{view view.innerView contentBinding='view.content'}}"),
classNames: ['accordion-body', 'collapse'],
This field is set when the bodyView embedded view instance is inserted into the DOM.
didInsertElement: function() {
this.get('parentView').set('target', this.get('elementId'));
},
innerView: Em.View.extend({
tagName: 'div',
classNames: ['accordion-inner'],
templateNameBinding: "parentView.parentView.parentView.innerTemplateName"
})
})
})
});
That all works. However, I'd like to do something like this in the toggleView:
href: function() {
return "#" + this.get('parentView.parentView.bodyViewInstance.elementId');
}.property('parentView.parentView.bodyViewInstance.elementId')
When I do, I get the following error, and I'm not sure I understand why:
Error: Something you did caused a view to re-render after it rendered but before it was inserted into the DOM.
Is there any way to accomplish what I'm looking to do?

Ember CollectionView - Removing an ItemView

I'm having a hard time finding an answer to this, so perhaps I'm doing it all wrong. I have a collection view with an item view class. Each item has a delete button, which refers to the action "removeItem." This does remove the item, but I need to refer back to the collection view content in order to post the updated array with an API call.
App.WatchlistTilesView.Collection = Ember.CollectionView.extend({
tagName: "ul",
itemViewClass: Ember.View.extend({
attributeBindings: "role",
role: "tile",
classNames: ["tile"],
removeItem: function() {
this.remove();
},
templateStock: Ember.Handlebars.compile("
<button {{action removeItem}}>Delete</button>
...
"),
})
});
EDIT:
I found the answer to this particular problem. There was a property on the collection view that needed to be accessed by the item class. I was able to access it with this.get("parentView"). Also, I had to add the view as the target for the removeItem action.
It looks something like this:
App.WatchlistTilesView.Collection = Ember.CollectionView.extend({
tagName: "ul",
itemViewClass: Ember.View.extend({
attributeBindings: "role",
role: "tile",
classNames: ["tile"],
watchlistBinding: "parentView.watchlist"
removeItem: ->
#remove()
watchlist = #get("watchlist")
#get("controller").removeSymbol(#content, watchlist)
templateStock: Ember.Handlebars.compile("
<button {{action removeItem target="view"}}>Delete</button>
...
"),
})
});
App.WatchlistTilesController = Ember.Controller.extend({
account: null,
watchlists: null,
removeSymbol: function(entry, watchlist) {
watchlist.entries.removeObject(entry);
return WatchlistService.edit(this.account, watchlist);
}
});

EmberJs: component template

I try to wrap a colorPicker component from http://www.eyecon.ro/bootstrap-colorpicker/
I need to fill the "background-color css attribute" of the template with the "value" attribute of the view...
I have tried to set {{value}} but it does not work...
Any advice ?
JavaScript:
App.ColorPicker = Em.View.extend({
classNames: 'input-append color',
attributeBindings: ['name', 'value'],
value: '',
template: Ember.Handlebars.compile('{{view Ember.TextField valueBinding="this.value"}}<span class="add-on"><i style="background-color: {{value}}"></i></span>'),
didInsertElement: function() {
$('#backgroundColor').colorpicker({format: "rgb"});
}
})
;
html:
{{view App.ColorPicker placeholder="Background color" name="backgroundColor" valueBinding="App.controller" classNames="input-large" id="backgroundColor"}}
You need to look into the {{bindAttr}} helper. There is an old but good post on it here
Basically, you can't just drop bound values into HTML tags as it then inserts the wrapper script tags and breaks everything.
What you're looking for is:
App.ColorPicker = Em.View.extend({
classNames: 'input-append color',
attributeBindings: ['name', 'value'],
value: '',
template: Ember.Handlebars.compile('{{view Ember.TextField valueBinding="this.value"}}<span class="add-on"><i {{bindAttr style="view.iStyle"}}></i></span>'),
iStyle: function() {
return "background-color:" + this.get('value');
}.property('value'),
didInsertElement: function() {
$('#backgroundColor').colorpicker({format: "rgb"});
}
});