Ember, disable input based on condition - ember.js

I have an Ember form, where I want to disable input's based on the user.
I get an array of modifiable fields from my back end.
How do I get this to work with Ember?
Ideal scenario would be to do this,
{{input type="text" disabled=isModifiable("middleName") value=middleName}}
I know that isn't possible.
Fairly new to Ember, advice would be appreciated. Thanks.

Define a property in you Controller or Component like this:
fields:[],
loadFields : function(){
var that = this;
$.get(FIELDS_API).done(function(serverResponse){
//process server response in an array with objects with this form:
// [ field:"middleName", isDisabled:true}]
that.set("fields",fieldsArray);
})
}
isModifiable : function(fieldName){
var field = this.get("fields").findBy("field",fieldName);
return (field) ? field.isDisabled: false;
},
isMiddleNameInputDisabled : function(){
return this.isModifiable("middleName");
}.property("fields.#each")
Use that property in your template like this:
{{input type="text" disabled=isMiddleNameInputDisabled value=middleName}}

Related

Ember Checkbox -- Accessing model property from different controller

I have a checkbox on a modal popup and need it to be checked based on the value on an unrelated model. The model is Team and the property I need to access is called syncAccount (boolean), so the input helper would likely look something like this:
{{input type="checkbox" name="syncAccount" checked=team.syncAccount }}
How can I access or bind to team.syncAccount from the modal? I have a ModalImportController, but no associated route. Is there some way in this controller I can assign a property that looks up or binds to the value of syncAccount for the current team (and is updated as they toggle it)?
Similarly, I need toggling the checkbox to send an update request for this field. Will this need to be an ajax request, or is there some way to set the model used by the checkbox to point to a team so that I can call #model.save()?
To get access to a property from another controller you first need to include that via needs like so:
App.ModalImportController = Ember.ArrayController.extend({
needs: "team",
teamController: Ember.computed.alias("controllers.team"),
});
then you would have access to it's properties like so:
// still in ModalImportController
syncAccount: function() {
return this.get('teamController.syncAccount');
}.property('teamController.syncAccount')
I haven't tested it now, but that's the way I did it in a slightly different setup.
source:
[http://guides.emberjs.com/v1.13.0/controllers/dependencies-between-controllers/][1]
For toggeling to send an update request I use:
syncAccount: Ember.computed('model.syncAccount', {
get: function() {
return this.get('model.syncAccount');
},
set: function(key, value) {
this.set('model.syncAccount', value);
// do all you need to do via AJAX here
return value;
}
})
note, that you also GET the value from here, so change your input-helper to:
{{input type="checkbox" name="syncAccount" value=syncAccount }}

Accessing model data in router when using a view

I am looking for a way to access model data in a route when using a view to display model attributes.
Example
Template
<h2>New post</h2>
<form {{action save model on="submit"}}>
<label>Title</label>
{{input type="text" value=title placeholder="title" id="title"}}
<label>Text</label>
{{view "tinymce" value=text }}
<button>Post</button>
</form>
View Template
<textarea id="tinymce">
</textarea>
View
export default Ember.View.extend({
templateName: 'views/tinymce-textarea',
didInsertElement: function() {
tinymce.EditorManager.execCommand('mceRemoveEditor',true, 'tinymce');
tinymce.EditorManager.execCommand('mceAddEditor',true, 'tinymce');
}
});
Router
export default Ember.Route.extend({
....
actions : {
save : function(model) {
if (!model.get('title').trim() || !model.get('text').trim()) {
return;
}
model.save().then(this.onSuccessfulSave.bind(this), this.onFailedSave.bind(this));
}
}
});
Now, obviously this doesn't work, since model.text is never bound in the view, like it would be if I were to use the textarea template helper:
{{textarea value=text placeholder="text" id="text"}}
But this is just one of many (many) ways I have tried to get this to work, and I am at a complete loss as how one would access model attributes in the route when using a view. And it does seem like a pretty common usecase to me too.
I have failed to find information regarding this on SO or anywhere else, so if anyone is able to help me, thanks in advance! / AS.
So one of the main things that you're missing out is binding the view to the controller. This is actually really straight forward to do, but without it Ember doesn't know that it should propagate changes between the two. The first thing I would do is this:
{{view "tinymce" valueBinding="text" }}
This says that the views value will be binded to the controller's text value. Whenever view's value is updated, it will propogate to the controller and vice versa.
The next item to take care of is actually binding the value in the view. All you need to do is tell the input to bind it's value to the view's value. This can be done like this
{{textarea value="view.value" placeholder="text" id="text"}}
Try this out, and you can use this jsbin that I created as an example:
http://emberjs.jsbin.com/qanudu/26/
If you have any other questions just let me know, but this should solve your issues!

Reset check boxes after form submission

I've created a simple form to add items to my database. Each item hasMany skills so I've set up a #each to iterate over each skill and add checkbox into the form. I'm doing this by using a render helper in my form because a different controller contains the skills. So I have this in my items/new template:
{{render 'skills'}}
And then my skills template looks like this:
<script type="text/x-handlebars" data-template-name="skills">
<div class="form-group">
{{#each skill in model itemController="skillCheckbox"}}
<div class="checkbox">
<label>
{{input type="checkbox" checked=selected}}
{{skill.name}}
</label>
</div>
{{/each}}
</div>
</script>
My itemController is like this:
App.SkillCheckboxController = Ember.ObjectController.extend({
needs:['itemsNew'],
selected:function() {
var skill = this.get('content');
var skills = this.get('controllers.itemsNew.model.skills');
return skills.contains(skill);
}.property('controllers.itemsNew.model.skills'),
selectedChanged:function() {
var skill = this.get('content');
var controller = this.get('controllers.itemsNew.model.skills');
if(this.get('selected')) {
controller.addObject(skill);
} else {
controller.removeObject(skill);
}
}.observes('selected')
});
Everything works fine and the new item is submitted to server properly. The problem is that I'm going to be entering in a lot of items so I have the form setup to automatically reset after a new item is saved. I do this by created a new record and setting it as the model:
actions:{
save:function() {
var self = this;
var model = this.get('model');
var promise = model.save();
promise.then(function(){
var newModel = self.store.createRecord('item');
self.set('model',newModel);
});
}
}
All the other form fields reset as normal, except for the skill checkboxes and I can't figure out why. I've tried adding the itemsNew model to the observer or removing the skills from the model manually, but it doesn't update in the browser.
Any ideas on why this isn't working or suggestions on how to do it better/correctly? Any help would be appreciated. Thanks!
You're breaking the computed property when people click the check box. Computed properties are just that, a computed property. It's a property that's derived based on the other properties (and generally just a getter). It isn't a property that's derived on other properties, then you set it, and then it decides to start being a computed property again.
That's essentially what's happening here, you're overwriting the computed property with just a value (true/false).
That being said, you'll want to try another approach, where selected is just a property, and another property set's it to start (and when they click it of course).
App.SkillCheckboxController = Ember.ObjectController.extend({
needs:['itemsNew'],
selected:false,
initialSetup: function(){
var skill = this.get('content'),
skills = this.get('controllers.itemsNew.model.skills');
this.set('selected', skills.contains(skill));
}.observes('model', 'controllers.itemsNew.model.skills')
});

EmberJS: Property Scopes in an ArrayController?

this is probably a grossly simple question to answer, so I apologize if I am cluttering this forum in advance.
I am displaying a list of items that share the same model and controller.
I made these items editable via a <button {{ action 'edit' }}> next to each item which toggles a boolean value of a property "isEditable" in the controller.
However clicking this button causes all items in the list to become editable because they all share the controller property "isEditable". The desired effect is to make a single item editable at a time instead of all items at once.
A simplified version of my template looks like this:
{{#if isEditing}}
<p>{{input type="text" value=title}}</p>
<button {{action 'doneEditing'}}>Done</button>
{{else}}
<span class="title">{{title}}</span>
<button {{action 'edit'}}><span class="edit"</span></button>
{{/if}}
and the controller looks like this
App.ItemController = Ember.ArrayController.extend({
isEditing : false,
actions : {
edit : function(){
this.set('isEditing', true);
},
doneEditing : function(){
this.set('isEditing', false);
},
}
});
Anybody know how to accomplish this? Is it because each item shares the "isEditable" property? If so, how do I get around this? I don't want to put this into the model because it's purely a display thing, even though I know I can get it to work doing that.
Thanks :)
By default the controller lookup within an {{#each}} block will be the controller of the template where the {{#each}} was used. If each item needs to be presented by a custom controller (to hold it's own state for example) you can provide a itemController option which references a controller by lookup name. Each item in the loop will be then wrapped in an instance of this controller and the item itself will be set to the content property of that controller.
So, I assume you are displaying the list of items using the {{#each}} helper. Therefore you can specify an itemController in the {{#each}} helper to hold the isEditable state on a per item basis. This would look something like this:
{{#each item in controller itemController="item"}}
...
{{/each}}
Moreover you should define the defined itemController of type Ember.ObjectController like:
App.ItemController = Ember.ObjectController.extend({
isEditing : false,
actions : {
edit : function(){
this.set('isEditing', true);
},
doneEditing : function(){
this.set('isEditing', false);
},
}
});
And for the list you should then have an App.ItemsController of type Ember.ArrayController:
App.ItemsController = Ember.ArrayController.extend({});
See here for more info on the mentioned itemController support for the {{#each}} helper: http://emberjs.com/api/classes/Ember.Handlebars.helpers.html#method_each
Hope it helps.

Ember TextField valueBinding with dynamic property

I'm trying to write a generic view that handles custom fields in my app, but I'm having a hard time getting this to work. Here's the scenario - I have a fieldDef object which defines the custom fields, and a valueObject which has an array, customFields, which has the values. What I'm trying to do is something like this:
{{view Ember.TextField valueBinding="valueObject.customFields.[fieldDef.name]"}}
Obviously that doesn't work because it treats fieldDef.name as a literal. I've tried overriding the TextField class, but can't seem to get it to bind.
Any suggestions on how to accomplish this?
Thanks,
Scott
Ember can't bind to an array index, so you'll have to work around it. One solution is to limit yourself to a one-way binding, where your textfield updates the values hash. If you're planning to submit the form after the user presses a button, this should do the trick.
Define an array of field ids in your controller and a hash for their values to go in.
App.ApplicationController = Ember.Controller.extend({
fieldIds: ['name', 'email', 'whatever'],
fieldValues: {} // {name: 'user', email: 'user#...', ...}
});
Now extend Ember.TextField to update your values hash when a text field changes. You'll need to pass each instance a fieldId and a reference to the values hash from your controller.
App.TextField = Ember.TextField.extend({
fieldId: null,
values: null,
valueChange: function() {
var fieldId = this.get('fieldId');
var values = this.get('values');
if (values && fieldId) values[fieldId] = this.get('value');
}.observes('value')
});
The template is simple.
{{#each fieldId in fieldIds}}
<label>{{fieldId}}</label>
{{view App.TextField fieldIdBinding="fieldId" valuesBinding="fieldValues"}}
<br/>
{{/each}}
Here it is fleshed out in a jsfiddle.
#ahmacleod great answer man. Just in case anyone is interested it works great extending select too:
import Ember from 'ember';
export default Ember.Select.extend({
fieldId: null,
values: null,
valueChange: function() {
var fieldId = this.get('fieldId');
var values = this.get('values');
if (values && fieldId) values[fieldId] = this.get('value');
}.observes('value')
});
Call it as an normal component (components/dynamic-select.js)
{{#each id in fieldIds}}
{{dynamic-select content=fieldIds fieldIdBinding="header"
valuesBinding="fields"}}
{{/each}}
You can bind input values with dynamic keys(variables) of objects with help of mut helper now.
https://guides.emberjs.com/v2.6.0/templates/input-helpers/#toc_binding-dynamic-attribute
You can access it like this,
var Object = {};
var key = "firstName";
We can bind key in input helper like this,
{{input value=(mut (get Object key))}}
{{Object.firstName}} // Prints the dynamic value in textbox