Using the same view twice but with different values - ember.js

I have a view that i want to use twice but with different values.
{{#view App.ColorPickerView sytleName="Background Color" css="bgColor"}}
{{view.sytleName}}
{{/view}}
{{#view App.ColorPickerView sytleName="Text Color" css="textColor"}}
{{view.sytleName}}
{{/view}}
This should be easy.
Please see this jsfiddle http://jsfiddle.net/rmossuk/LUEUV/11/
Basically when you click on Background Color it should change the background color but it is changing the text color as it seems to be using the next instance of the view.
Anyone help me with this ?
Thank you
Rick

The answer is very simple, in the didInsertElement, you declare the view as global, so when instanciating the second ColorPicker, the view in onChange method of the first color picker refers to the second view.
Declaring view with var view = this; makes the code work.

Related

Ember.js How to bind isSelected class to the clicked view?

I have some views which will expand and show details when clicked.
For now, all the views can be clicked and expand, but the question is
How to expand only the latest clicked view?
For example, when I clicked view #1, it expand. So when I clicked view #2, the view #1 will collapse and view #2 expand etc.
I know we can bind a isSelected classname to the clicked view, but how do we tell the view to check "If any other view is selected" ?
Do we use CollectionView ? But how?
FYI this is the working JSBin.
First of all, I would change view to component. Although views have their valid use-cases, you are usually better off with a component.
Also, if you think about it, it makes sense that someone outside of the component would need to know which component was clicked last. That outside actor could be the controller, which could have a property called lastComponentClicked (which initially starts out as null)
App.IndexController = Ember.ArrayController.extend({
lastClickedComponent: null
});
Then, you can pass that property into each component and the property becomes bound between the controller and all the components as in:
<script type="text/x-handlebars" data-template-name="index">
{{#each content in model}}
{{ x-box content=content lastClickedComponent=lastClickedComponent}}
{{/each}}
</script>
So far, so good. Now, for the component itself:
App.XBoxComponent = Em.Component.extend({
classNames: ['box'],
isSelected: function(){
return this.get('lastClickedComponent') === this._uuid;
}.property('lastClickedComponent'),
click: function(){
this.set('lastClickedComponent', this._uuid);
}
});
Every time it is clicked, you can set a lastClickedComponent property, which is bound between ALL the components and the controller and thus will get reset every time. You can just set it to a value unique to the component, for example this._uuid.
isSelected computed property can then just check if lastClickedComponent property is that of THIS component, in which case the content you need to show will be expanded.
<script type="text/x-handlebars" id="components/x-box">
{{content}}
{{# if isSelected }}
<div>LAST SELECTED</div>
{{/if}}
</script>
Working solution here

View click action not returning correct object

I'm having trouble with a click handler in a view. It's not returning the expected member of the collection, but the collection as a whole.
I've created a jsfiddle to demonstrate the issue. I have an ArrayController, whose content I pre-populate. The view for this controller then uses the #each helper for the controller with another view:
{{#each controller}}
{{view App.ActivityListItemView}}
{{/each}}
This works, in that I see the name of the item on the page, and can click it.
The problem is in the click handler - if I #get('content'), the content for the parent controller is returned. How do I get the item that was clicked on? If you have a look at the console output in the jsfiddle you'll see the issue. I assume this is a context issue?
I've tried adding contentBinding="this" to the view:
{{#each controller}}
{{view App.ActivityListItemView contentBinding="this"}}
{{/each}}
but that makes no difference.
thanks,
Martin
How do I get the item that was clicked on? If you have a look at the console output in the jsfiddle you'll see the issue. I assume this is a context issue?
Exactly. You want the view's context instead of it's controller's content. So:
click: (data)->
console.log 'clicked on an activity'
selected = #get('context')
#get('controller').set('selectedActivity', selected)
console.log(#get('controller').get('selectedActivity.name'))
Why?
By default the {{#each}} helper does not create a new controller instance for items in the array. So when you#get('controller')` from the view helper it searches up the view heirarchy until a controller is found - in this case that is the array controller.
If you want to have a separate controller for each item you could provide an itemController attribute to the each helper - see http://emberjs.com/api/classes/Ember.Handlebars.helpers.html#method_each
Right, I've got this working, I think the issue sprang for a lack of understanding of the contentBinding argument. Basically I've changed to using a specific name of 'activityBinding' within the #each block, and then referring explicitly to the activity in the click handler. See jsfiddle for a working demo.
{{#each controller}}
{{view App.ActivityListItemView activityBinding="this"}}
{{/each}}
and
click: ->
console.log 'clicked on an activity'
console.log #get('activity.name')
content = #get('activity')
#get('controller').set('selectedActivity', content)

binding context to action in ember textfield

I've got an ember application that needs to manage multiple chat windows. A window for each active chat is created within an {{#each}} loop. This is straightforward enough. The place that I'm having trouble is sending the chat message when the user presses enter.
The window looks like this
{{#each chats}}
... stuff to display already existing chats...
{{view Ember.TextField valueBinding="text" action="sendChat"}}
<button {{action sendChat this}}> Send </button>
{{/each}}
This works fine for the button, since I can pass this to it. By default the function defined in the textfield view action just gets the text within that textfield, which is not enough in this case. Since there can be multiple chat windows open, I need to know which window the message was typed into. Is it possible to pass this to the textfield action function? (or can you suggest a different way to solve this problem?)
Add contentBinding="this" to the definition of the view, like:
{{view Ember.TextField valueBinding="text" action=sendChat contentBinding="this"}}
EDIT
Ember master already has this change, but the official downloadable verstion still don't.. so you will need to subclass the Ember.TextField and change its insertNewline to achieve required functionality:
App.ActionTextField = Em.TextField.extend({
insertNewline: function(event) {
var controller = this.get('controller'),
action = this.get('action');
if (action) {
controller.send(action, this.get('value'), this);
if (!this.get('bubbles')) {
event.stopPropagation();
}
}
}
});
After that, the action handler will receive additional argument, the view:
{{view App.ActionTextField valueBinding="text" action=sendChat myfieldBinding="this"}}
and in controller:
sendChat: function (text, view) {
var myField = view.get('myfield');
//do stuff with my field
}
You may use ember master instead of subclassing Ember.TextField..
I hope the ember guys will release the next version soon..
I know this question has been answered but I said let me add some information that may help out someone in the situation of actions and TextField. One word "Component". TextField in Ember is a Component so if you think of TextField from that perspective it may help when it comes to sending actions and using TextField in an application.
So when you say App.SomeTextField = Ember.TexField.extend({...});App.SomeTextField is subclassing Ember.TextField (remember which is a component). You could add your logic inside and that works and you could access it from your template such as {{view App.SomeTextField}}
You may be thinking I see the word 'view' this guy sucks, TextField is a View. Well, it is sort of a View because Ember Components are a subclass of Ember.View so they have all that Views have. But there are some important things to keep in mind Components un-like Views do not absorb their surrounding context(information/data), they lock out everything and if you want to send something from the outside surrounding context you must explicitly do so.
So to pass things into App.SomeTextField in your template where you have it you would do something like {{view App.SomeTextField value=foo action="sendChat"}} where you are passing in two things value, and action in this case. You may be able to ride the fine line between View/Component for a bit but things come crashing why is your action not sending?
Now this is where things get a little trippy. Remember TextField is a Component which is subclassed from View but a View is not a Component. Since Components are their own encapsulated element when you are trying to do this.get('controller').send('someAction', someParam), "this" is referring to the Component its self, and the controller is once again the component its self in regards to this code. The action that you are hoping will go to the outside surrounding context and your application will not.
In order to fix this you have to follow the protocol for sending actions from a Component. It would be something like
App.SomeTextField = Ember.TextField.extend({
//this will fire when enter is pressed
insertNewline: function() {
//this is how you send actions from components
//we passed sendChat action in
//Your logic......then send...
this.sendAction('sendChat');
}
});
Now in the controller that is associated with where your SomeTextField component/view element is you would do
App.SomeController = Ember.Controller.extend({
//In actions hash capture action sent from SomeTextField component/view element
actions: {
sendChat: function() {
//Your logic well go here...
}
}
});
Now I said to think of TextField as a Component but I have been riding the tail of the view and declaring {{view AppSomeTextField...}}. Lets do it like a component.
So you would have in your template where you want to use it
//inside some template
`{{some-text-field}}`
Then you get a specfic template for the component with the name:
//template associated with component
<script type="text/x-handlebars" data-template-name="components/some-text-field">
Add what you want
</script>
In your JS declare your component:
//important word 'Component' must be at end
App.SomeTextFieldComponent = Ember.TextField.extend({
//same stuff as above example
});
Since we on a role you could probably get the same functionality using Ember input helpers. They are pretty powerful.
{{input action="sendChat" onEvent="enter"}}
Welp hopefully this information will help someone if they get stuck wondering why is my action not sending from this textField.
This jsBin is a sandBox for Components/Views sending actions etc....Nothing too fancy but it may help someone..
http://emberjs.jsbin.com/suwaqobo/3/
Peace, Im off this...

handling action in view not router

I have a view that has expandable/collapsable content that I'd like to be able to toggle by clicking the on the table row. Before pre1.0, I had this in the template:
<tr {{action "expand"}}>
which was previously handled on my view:
App.ContentRowView = Em.View.extend({
templateName: 'ember/templates/content/row',
expand: function() {
this.set('isExpanded', !this.get('isExpanded'));
},
isExpanded: false
});
However, after upgrading to pre1.0 the action is now fielded directly by the router. This makes sense in a lot of situations, but in this case the expansion is really a view concern. I've tried just replacing this with a click event handler without luck.
Is there a best practice on how to handle a view concern event like this with pre1.0?
Deprecated Answer
Even if the answer of #outside2344 works, I think it's not exactly right.
Indeed parentView does not represent the view, but the parentView of its parentView.
Since 1.0-pre, views preserve their context, so in the template, this represents the parentView, parentView represents parentView.parentView, and view represents the current view.
Here is a fiddle to illustrate this: http://jsfiddle.net/Sly7/cnmJa/
For me the answer is {{action expand target="view"}}
EDIT (answering to #Gal Ben-Haim)
Action helpers behave little different in a router-based application. Quote from the documentation:
In Router-driven applications, if an action is not intercepted by a view, that event will bubble up to the Route in which that view was rendered. If that Route is a sub-route of another Route the transition will be sought there all the way up to the top-level Route definition, our über-container: root.
This bubbling effect allows certain actions to remain private. If certain transitions should only be available for certain sub-sub-states, put the transition on the sub-state and you've achieved a type of scoping.
Basically, for me that means in Router-driven apps if you don't explicitly define a target in the action helper, it is sent to the router.
Updated answer
I think now the guides answer very well to this question. see http://emberjs.com/guides/templates/actions/#toc_specifying-a-target
In pre1.0 you can make the view field the action by adding target="parentView" to the action:
{{action "expand" target="parentView"}}
Events doesn't bubble through the view-hierarchy by default. You can change this (though I can't say I'd recommend it):
(function() {
Ember.View.reopen({
// Let actions bubble to parentView by default.
target: function() {
return this.get('parentView');
}.property('parentView')
});
})();

Ember Views, Handlebars and jQuery Effects

I would like to incorporate jQuery effects (fadeIn, fadeOut, etc...) in parts of my handlebar templates. I think that this can more or less be accomplished with a separate view in which the view's isVisible property is initially false and its didInsertElement method calls something like this.$().fadeIn().
However, what I'd like to do is add a jQuery effect to just a small part of a view - say for purposes of displaying a small block of content that is initially hidden by an {{#if}} statement that evaluates to false and later through user feedback gets toggled to true. See the following http://jsfiddle.net/YeGbF/2/.
Any suggestions?
You could use a view for the stuff which shall be shown faded in, see http://jsfiddle.net/pangratz666/dJMwC/
Handlebars:
{{#view App.FadeInView contentBinding="this"}}
<div>{{content.someAdditionalDetail}}</div>
{{/view}}
JavaScript:
App.FadeInView = Ember.View.extend({
didInsertElement: function(){
this.$().hide().show('slow');
}
});
Also have a look at Deferring removal of a view so it can be animated