Add and remove views - ember.js

I would like to insert in the DOM a view that displays a form with 2 buttons: + and -;
when you click "+" another identical view is inserted, when you press "-" the current view is removed;
I've tried to create a container view and the function for adding a view is simple:
in the template:
{{view Ember.ContainerView elementId="containerView"}}
in the childView's template:
<button class="form-button" {{action "addProduct"}}>+</button>
in the route's controller:
addProduct: function() {
var container = Ember.View.views['containerView'];
var child = container.createChildView(Gmcontrolpanel.InserisciProdottoView);
container.pushObject(child);
}
But i'm not able to manage the "-" function; because for that i need to get the view that the button i'm clicking belongs to in order to remove it, and i don't know how to do this;
All the childviews can have a controller? Because from the childview's button i can only call actions from the route's controller;
Or there is a better way to get this work?

so in that case, have an action in the childview rather controller like this
<button class="form-button" {{action "deleteProduct" target="view"}}>-</button>
in the views actions handle the deleteProduct like this
deleteProduct: function() {
this.destroy();
}
If you want to handle any of the model part then send an event from above method to controller

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

How to send a click even to a view (it already works but I think it is not the Ember way)

I want to style a button for file upload. I already have a working, but it feels like I dont do it the Ember way.
My view:
App.FileUploadView = Ember.TextField.extend({
(....)
});
template:
{{view App.FileUploadView name="big-image" file=big-image id="big-image"}}
<a {{action clickUploadView}} href="#">Upload file</a>
controller action:
clickUploadView: function(){
Ember.$('#big-image').click();
},
I have fiddled around to use the viewName property like this in my template and call it from the controller: {{view App.FileUpload ... viewName="big-image"}} but could not get the click fired.
Any thoughts or is this just correct?
Wrap your anchor tag in the view helper and add the action to the view, then target the view.
{{#view App.SomethingView}}
<a {{action daClick target="view"}} href="#">Upload file</a>
{{/view}}
App.SomethingView = Em.View.extend({
actions:{
daClick: function(){
alert('hello');
}
}
});
http://emberjs.jsbin.com/eHAsexI/1/edit
Or if you just want it to hit the click event of the view, you can just rip out the action all together and leave the anchor tag in the view and it will hit the click event of the view.
http://emberjs.jsbin.com/eHAsexI/2/edit

How to programatically add component via controller action

I have a scenario where I have list of items and each item has a create button. When I click on create, I wanted a component to be appended to the list item. This component uses model data as parameter and also accesses store from within. To access the store in the component I am using targetObject.store
The component works well if I add it to the template manually like:
{{#each}}
<div> blah blah {{my-component data=this.something action="doSomething"}} <button {{action 'add' this}}>Add</button></div>
{{/each}}
I can probably show/hide the component using a flag, and toggle it when we click on Add button, but I rather do it dynamically if possible.
I did try this but didn't work for me because I couldn't access store :
actions: {
add: function(obj){
var view = Ember.View.create({
template: Ember.Handlebars.compile('{{my-component action="addQuestion"}}')
});
view.set('data', obj.get('something'));
Ember.run(function() {
//prolly can get parent view rather than document.body
view.appendTo(document.body);
});
}
}
Thanks.
I think this example answers your question:
http://emberjs.jsbin.com/axUNIJE/1/edit

Specify action for view in template?

I would like to know if it is possible to assign an action to a view like I could assign an action to a HTML tag:
This works:
<button {{action "show2" }}>Test 1</button>
This doesn't:
{{#view NewApp.MenuButton }}
{{action "show3" target="controller"}}
{{/view}}
I know that I could implement the click function in the view. But I would like to use the button as some sort of reusable component.
You typically want to use the Handlebars action helper on an HTML element, not on an Ember.View.
Since you want to attach an event to the NewApp.MenuButton View you, define the event in your view class definition. For example, here we handle the click event:
NewApp.MenuButton = Ember.View.extend({
click: function(event){
// When the user clicks this view,
// this function will be called.
// ... handle the click
App.myController.menuButtonWasClicked();
}
});
If the event you want to attach is not one of the built-in events, you can register your own events. Find the built-in supported events and how to register custom events here: Ember.js - Events
Edit: You say you want to be able to reuse it. You can define a mixin for attaching arbitrary events and targeting arbitrary objects:
Ember.MyEventAttacher = Ember.Mixin.create({
init: function() {
var action = this.get('action');
target = this.get('target'),
targetObj = Ember.getPath(target);
if (action && targetObj) {
var targetEventFnc = targetObj[action];
if (typeof targetEventFnc === 'function') {
var actionFnc = function(event) {
targetEventFnc(event);
}
this.set(action, actionFnc);
}
this._super();
}
});
Include the Mixin in your View:
NewApp.MenuButton = Ember.View.extend(Ember.MyEventAttacher);
And then re-use this view in your templates, making sure to define the action and target properties. Example:
{{#view NewApp.MenuButton action="show3" target="NewApp.myController"}}
<!-- ... -->
{{/view}}
Targeting:
NewApp.myController = Ember.Controller.create({
show3: function(event) {
// the event is sent here!
}
});

How do I pass a view object to be rendered in a child view?

I am trying to build a reusable popup view with it's own template and CSS. In this reusable view, I want to be able to display child views with the content that the parent view wants to put in the popup.
The handlebars template for the popup looks like this:
<div class='popup'>
<a class='open' href='#' {{action toggleVisible}}>
{{view button}}
</a>
<div class='collapse'>
<div class='box'>
<div class='arrow'></div>
<div class='arrow-border'></div>
{{view content}}
</div>
</div>
</div>
In this context, button and content should be Ember views that get rendered inside this popup and those views will be different depending on what parent view is creating and displaying the popup.
The popup View object looks like this:
var popup = Ember.View.extend({
button: null,
content: null,
templateName: 'popbox',
visible: false,
toggleVisible: function() {
var visible = this.get('visible');
this.set('visible', !visible);
if (visible) {
this.$().find('.box').fadeOut(250);
} else {
var pop = this.$().find('.popbox');
var box = this.$().find('.box');
box.css({
'display': 'block',
'top': 10,
'left': ((pop.parent().width()/2) -box.width()/2 )
});
}
}
}
}));
I'm not sure how to pass the templates to be rendered in the popup from the parent view. Right now I'm trying to do something like this, but it doesn't work.
var sendto = Ember.View.extend({
templateName: 'sendto',
popupView: popup.extend({
button: Ember.View.extend({
template: Ember.Handlebars.compile('{{model.selectedRecipients.length}} {{model.peopleOrPersonText}}')}),
content: Ember.View.extend({template: Ember.Handlebars.compile('Test content')})
})
}));
What would be the correct way to pass views to the popup view?
NOTE: This appears to work but it generates a DEPRECATED error message:
Something you did caused a view to re-render after it rendered but before it was inserted into the DOM.
UPDATE: It turns out my issue was due to something else entirely (was accidently including Ember.js twice). The code above actually seems to work just fine.
Use a Ember.ContainerView, and have the internal views each be their own view class with their own template. Then, you can just set the childViews array on the containing view to contain an instance of whatever view you want to display.