force re-render of custom textfield view - ember.js

How to force to re-render a custom textfield view?
I create a:
App.Select2View = Ember.TextField.extend({didInsertElement:function() {//plugin called here}});
It's included in a template form and when the form is open and I select another/different item, this custom textfield view (based on select 2) doesn't re-render but stays the same.
I have nested routes/resources. Company under companies. When I select a company I have readonly info and edit button that if clicked calls action on the Company controller to "isEditing" state, in this state a form is open. If I change the state to false it goes back into read only model (viewing the company info).
It's fine if I set it back to read only mode first, and then open another item and the select2 is rendered with this companies data.
But if it's in the isEditing state, with form open and I navigate to another company item all form input fields change according to model (such as name will change because it's binded to the name key value of the company model), but the select2 stays the same as previous.
I'm not sure how to re-render this in Ember.
This is defined as partial in template under the main company template view:
<script type="text/x-handlebars" id="company/_edit">
<p>{{input type="text" value=name}}</p>
<p>
{{view "select2"
prompt="Search for companies"
resource="results"
displayKey="text"
onSelect="addCompany"
onDeSelect="removeCompany"
}}
</p>
</script>
Any pointers are appreciated.

I'm new to Ember and my answer might not be apt, but based on my experiments the problem is that Ember caches as much as possible. So it won't re-render the view completely with the select2, only when view is in a certain state previous state followed by next state will it automatically re-render.
The solution atleast for now seems to be to add a "valueChange" method with observer on "value" change on the select2 custom TextField and make the same call as in "didInsertElement" (which executes when the view is rendered):
valueChange: function() {
Ember.run.scheduleOnce('afterRender', this, 'setupSelect2');
}.observes('value'),
Now even if I'm in the "edit" state, it will update this select2 as well, not just the standard form inputs tied specifically to the model. I can now navigate to any item in "edit" state which has form open for each item selected and values change correspondingly in the select2.

Related

setting which controller to handle a certain action

I'm using the structure of the Ember version of the TodoMVC app for an Ember app by which I mean that, after setting the application route to products
export default Ember.Route.extend({
redirect: function(){
this.transitionTo('products');
}
});
I then use the ul structure of the todo app, with each product occupying a new list position (as each todo does in the TodoMVC). I also (as with the Todo app) bind some attributes that allow me to set css styles depending on state
<ul id="products">
{{#each product in arrangedContent }}
<li {{bind-attr class="isEditing:editing"}}>
Depending on state, an input box will be visible for a product once a user clicks a button. The problem is that once the user clicks the button, isEditing is set to true for all the products, so all the input boxes are visible i.e. the class for each list element is set to editing <li class='editing'>. The action (which makes the input box visible) is handled on the products controller
showInput: function(item){
this.set('isEditing', true);
},
I only want the input box made visible for the particular product (or list element) where the click event was triggered. In the Todo MVC app, the action to make the input field visible is handled on a (singular) Todo controller, not the TodosController, so in my app, I created a (singular) product controller and put the action there but when the user clicks the button on an individual product in the list, the action on the (single) product controller is not triggered. How can I get the product controller to handle the action, or alternatively, ensure that only one input field is made visible.
You can see how the functionality is supposed to work on the live demo the TodoMVC app by creating a few todo items and then clicking on one of them. Only one input field becomes visible.
Update
In the showInput function of the products controller, I tried to call the corresponding function in the product controller (after specifying the products needs the product controller) needs: ['product'],
showInput: function(){
this.get('controllers.product').send('showInput');
}
this call the function on the product controller but setting it to true does nothing, i.e the input field isn't made visible and none of the list element classes are set to editing <li class="">
showInput: function(item){
this.set('isEditing', true);
},
For what it's worth, the product controller isn't showing in the Ember inspector, although I am able to send the actions to it, just not able to call this.set('isEditing',true) on it.
Change
{{#each product in arrangedContent }}
<li {{bind-attr class="isEditing:editing"}}>
To
{{#each arrangedContent as |product|}}
{{to-do product=product}}
Generate a to-do component via ember g component to-do, and modify it (see below). Set it's template to be whatever went in the li before, accessing the appropriate property via product.<prop> in the template.
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: ['isEditing'],
isEditing: false,
product: null,
actions: {
showInput: function() {
this.setProperty('isEditing', true);
}
}
})

Ember Route transition, prevent templates destroy.

This is what i have in my app.js. Index route with flights & cars as nested routes.
this.resource('index',{ path: '/' }, function(){
this.route('flights');
this.route('cars');
});
application.hbs
<div class="main">
{{outlet}}
</div>
index.hbs
..some html here...
{{outlet}}
..some html...
cars.hbs
{{#some-component1}}
{{/some-component1}}
flights.hbs
{{#some-component2}}
{{/some-component2}}
Cars and flights are kind off tabs(links), when clicked i use link-to to load the corresponding route
domain/app/#/flights
loads flights hbs in index.hbs outlet which in turn is loaded in application.hbs outlet.
When user clicks cars url changes to
domain/app/#/cars
and it loads cars.hbs in index.hbs outlet.
But when this transition happens the flights rendered template is destroyed along with all the components it had and corresponding component class objects and cars template and all its components are rendered. Which is all fine.
Now when user moves back(click on flights link) to flights all the flights components/templates are re created and rendered.
The content of these tabs are expensive to create every time when user moves between tabs(kind off).
Question: Should it be implemented just as a tab panel and not change the url(not use link-to, just actions) when user clicks on these links and showing/hiding content?
OR
should it be done the URL way? If yes, is there a way to make ember not destroy the previously rendered template in the outlet?
Thanks
At this time you can not keep your views/components rendered when route change. You can only render it beforehand and then switch between using ember query params. Take a look at
http://emberjs.com/guides/routing/query-params/ and https://github.com/instructure/ic-tabs

Ember.js: Design pattern for communicating to view from model

What's the design pattern for accessing a model's view from the model?
I have an Item DS.Model, ItemController, ItemsController, and ItemView, linked together with the standard render in the template that iterates over the ItemsController's content:
items.hbs:
{{#each item in content}}
render 'item' item
{{/each}}
I also have a newItem event on the ItemsController, which just creates a new Item object and prepends it into the controller's content array:
items_controller:
App.ItemsController = Ember.ArrayController.extend
actions:
newItem: ->
newItem = #get('store').createRecord 'item'
#get('content').insertAt(0, item, 0)
This works fine: after the next run loop, a new ItemController + ItemView are created and the changes are reflected in the DOM automatically.
However, say I want to set the mouse focus on an element inside the newly inserted ItemView (call a function focusMe), which seems like it reduces to how can I access (or send events to) a model's view from the model? I know in general information flows one-way from model -> controller -> view in Ember, but how else would I accomplish this?
Thoughts:
didInsertElement won't work, because I only want to call focusMe for items created via the newItem action, and I don't want to couple it with every view.
It seems like the most logical place to do this is inside the newItem action definition itself, something like newItem.getView().focusMe(), but of course this requires access to the model's view.
I looked at Get references to EmberJS views created for a given model object?, but I think adding an observer for every single ItemView is overkill, when only newly created items via the newItem action need to be focused.
Also, I tried that solution, and couldn't get it to work (that answer is over a year old).

Modifying template based on record saving

I have a form and handle the submit code in my router. My template has this:
{{#if isSaving}}
Saving...
{{else}}
<form>
inputs...
</form>
{{/if}}
My code for saving within the router is this (ctrl is controller):
events: {
saveCompany: function(record, ctrl){
if (!record.get('isDirty')) return;
ctrl.set('isSaving', true);
record.one('didUpdate', this, function() {
ctrl.set('isSaving', false);
});
record.get('transaction').commit();
},
When user clicks submit, "Saving..." is properly shown and hidden when update is done. But I already handle didCreate, DidUpdate etc. in my model definition - by showing some popups, but I still want to be able to prevent the user from fiddling with my form while data is being saved, that's why I use "record.one(...)". What would be best way to do so in a more "generic" way than the way I did it?
If your controller is an Ember.ObjectController you already have access, transparently, to the properties on the content. If the content is the record you're observing, you can just observe isSaving on the controller without any additional code, bindings, or what have you.

Deleting an Ember.Object created within an Ember.View

Following the documentation, I am able to display the person object in the sayHelloView. Now I am wondering how I can trigger the else condition to see the bindings in action. Is there a way to do delete the person using the browser console or am I barking up the wrong tree?
JsFiddle - http://jsfiddle.net/PhSRx/
You could have an action which sets the view's person to null.
So in your view template you'd have something like:
<a href="#" {{action logout}}>Log out</a>
When that's clicked, it will trigger the logout method for the view and then you can set this.person to null:
logout: function() {
this.set("person", null);
}
I've forked your fiddle and updated it, also adding in a login action which sets the person so you can toggle between and see the bindings in action: http://jsfiddle.net/rlivsey/atzfx/
Instead of the view handling logging in and out, you might want to delegate that out to a controller.
Here's a fiddle with an example of App.sessionController dealing with holding onto the current logged in person, and the view has a binding to the person so the template stays the same even though the architecture has changed - http://jsfiddle.net/rlivsey/QKa3N/