I am still learning Ember and have encountered a problem with keep consistent behavior when showing/hidding certain elements in the template. I have the following controller
import Ember from 'ember';
export default Ember.ArrayController.extend({
actions: {
newCalendar: function() {
this.set('showCalendarForm', true);
},
hideNewCalendar: function() {
this.set('showCalendarForm', false);
this.set('calendarName', '');
},
showCalendarForm: false,
createCalendar: function() {
var name = this.get('calendarName');
if (!name) { return; }
if (!name.trim()) { return; }
var calendar = this.store.createRecord('calendar', {
name: name
});
this.set('calendarName', '');
this.set('showCalendarForm', false);
calendar.save();
},
}
});
and a template
{{#if showCalendarForm}}
<div class="input-group">
{{input
class = 'form-control'
id = 'newCalendar'
type = 'text'
placeholder = 'New calendar'
value = calendarName
autofocus = 'autofocus'
focus-out = 'hideNewCalendar'
action = 'createCalendar'
}}
</div>
{{else}}
<button class="btn btn-sm btn-primary" {{action "newCalendar"}}>New</button>
{{/if}}
Problem is that the input field only gets autofocused the first time I click the button, and on subsequent clicks, the input gets displayed, but not autofocused. How can i fix this?
Related
I created a auto complete search bar in ember.js. The problem that i am having is be able to to press the down key and and go ever the listing that the autocomplete div is giving me. Thank you in advance...
<li {{bind-attr class="opacitySet:border:field"}}>
<i class="icon-search"></i>
{{input type="search" placeholder="Search Keyword" focus-out="focusOut" focus-in="focusIn" value=searchText}}
<div {{bind-attr class=":ee opacitySet::hide"}}>
{{#each searchResults}}
<h4><i class="icon-doc-text" style="padding:0;"></i>{{this}}<img src="assets/images/Arrow.png" class="pull_right"></h4>
{{/each}}
</div>
</li>
===========controller=============
App.FaqController = Ember.Controller.extend({
showAllAnswers: false,
opacitySet: false,
searchText: '',
searchResults: function(){
var searchText = this.get('searchText');
if(!searchText){ return;}
var regex = new RegExp(searchText, 'i');
return question.filter(function(name) {
return name.trim();
return name.match(regex);
});
}.property('searchText'),
actions:{
toggleBox: function(){
this.toggleProperty('showMessageBox');
},
hideBox: function(){
this.set('showMessageBox', false);
},
toggleAllAnswers: function(){
this.toggleProperty('showAllAnswers');
},
focusOut: function(){
this.set('opacitySet', false);
},
focusIn: function(){
this.set('opacitySet', true);
},
}
});
The following works. I can use my component to save new addresses. When the success promise is resolved, it transitions to the same route: _this.transitionToRoute('checkout.address.index')
The issue is, the form still contains the same values of the new address. I need to form to be cleared. How do I go about that?
// Controller
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
save: function(address) {
var _this = this;
this.store.createRecord('address', address.getProperties('address1', 'address2', 'city', 'postalCode')).save().then(function(){
_this.transitionToRoute('checkout.address.index');
}, function() {
// Need this promise, so we can render errors, if any, in the form
});
}
}
});
// Template
{{address-form action='save'}}
// Component object
import Ember from 'ember';
export default Ember.Component.extend({
address: function() {
return Ember.Object.create();
}.property(),
actions: {
save: function() {
this.sendAction('action', this.get('address'));
}
}
});
// Component template
<form {{action 'save' on='submit'}}>
<p>
<label>Address:
{{input value=address.address1 placeholder='11 Mars Street'}}
</label>
{{#each error in errors.address1}}
<br />{{error.message}}
{{/each}}
</p>
<p>
{{input value=address.address2 placeholder='Bel Air 1 Village'}}
{{#each error in errors.address2}}
<br />{{error.message}}
{{/each}}
</p>
<p>
<label>City:
{{input value=address.city placeholder='Makati'}}
</label>
{{#each error in errors.city}}
<br />{{error.message}}
{{/each}}
</p>
<p>
<label>Postal code:
{{input value=address.postalCode placeholder='1209'}}
</label>
{{#each error in errors.postalCode}}
<br />{{error.message}}
{{/each}}
</p>
<input type='submit' value='Next'/>
<button {{action 'cancel'}}>Cancel</button>
</form>
I'd suggest something like this (note, edited the code a little bit for readability):
export default Ember.Controller.extend({
actions: {
save: function(address, component) {
var controller = this;
var addressProperties = address.getProperties('address1', 'address2', 'city', 'postalCode');
var newAddress = controller.store.createRecord('address', addressProperties);
function onSuccess() {
controller.transitionToRoute('checkout.address.index');
component.reset());
}
function onFailure() {
// Need this promise, so we can render errors, if any, in the form
}
newAddress.save().then(onSuccess, onFailure);
}
}
});
// Component object
import Ember from 'ember';
export default Ember.Component.extend({
address: function() {
return Ember.Object.create();
}.property(),
reset: function() {
this.set('address', Ember.Object.create());
},
actions: {
save: function() {
var component = this;
component.sendAction('action', component.get('address'), component);
}
}
});
Im new to Ember and I am trying to create a record by submitting a form. This is the code I've written so far:
App.CharactersNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('character', {name: '', race: ''});
}
});
<form {{action "createCharacter" on="submit"}}>
<div class="form-group">
<label>Name</label>
{{input value=characterName class="form-control"}}
</div>
<div class="form-group">
<label>Race</label>
{{input id=characterRace class="form-control"}}
</div>
{{#link-to 'characters'}}<button class="btn btn-default">Back</button>{{/link-to}}
<button class="btn btn-default" type="submit">Create</button>
</form>
App.CharactersNewController = Ember.ObjectController.extend({
actions: {
createCharacter: function() {
var name = this.get('characterName'),
race = this.get('characterRace');
if (!name || !race) { return false }
// create new character
var character = this.store.createRecord('character', {
name: name,
race: race
});
this.set('characterName', '');
this.set('characterRace', '');
character.save();
}
}
})
Right now, the code stops at the var character = this.store.createRecord line in the controller, but no errors are raised in the console
Thanks
and I'm currently using Ember.select for a drop down menu, and while there's a default value showing, the object does't appear to actually be selected. When IO render the page, the modal open up but the value for selected_funding_instrument is undefined until I click on the drop down menu. Any tips?
debit-customer-modal.hbs:
<div {{bindAttr class=":control-group model.validationErrors.source_uri:error"}}>
<label class="control-label">Account number</label>
<div class="controls">
{{view Ember.Select
contentBinding="customer.debitable_funding_instruments"
valueBinding="model.source_uri"
optionValuePath="content.uri"
optionLabelPath="content.description_with_type"
class="span8"
}}
</div>
</div>
<div class="control-group">
<label class="control-label">Account holder's name</label>
<div class="controls">
<span class="label1a">{{selected_funding_instrument.name}}</span>
</div>
</div>
debit_customer_modal.js
require('app/components/modal');
Balanced.DebitCustomerModalComponent = Balanced.ModalComponent.extend({
submitAction: 'submitDebitCustomer',
dollar_amount: null,
actions: {
open: function() {
var fundingInstruments = this.get('customer.debitable_funding_instruments');
var debitUri = (fundingInstruments && fundingInstruments.length > 0) ? fundingInstruments[0].get('debits_uri') : null;
var debit = Balanced.Debit.create({
uri: debitUri,
amount: null,
order: this.get('order.href')
});
this.set('dollar_amount', null);
var selfie = this.get('selected_funding_instrument');
this._super(debit);
},
save: function() {
if (this.get('model.isSaving')) {
return;
}
var debit = this.get('model');
var selfie = this.get('selected_funding_instrument');
if (selfie) {
debit.set('uri', selfie.get('debits_uri'));
}
var cents = null;
try {
cents = Balanced.Utils.dollarsToCents(this.get('dollar_amount'));
} catch (error) {
debit.set('validationErrors', {
'amount': error
});
return;
}
debit.set('amount', cents);
this._super(debit);
}
},
selected_funding_instrument: function() {
var sourceUri = this.get('model.source_uri');
if (sourceUri) {
return this.get('customer.debitable_funding_instruments').find(function(fundingInstrument) {
return sourceUri === fundingInstrument.get('uri');
});
}
}.property('model.source_uri', 'customer.debitable_funding_instruments'),
can_debit: function() {
return this.get('customer.debitable_funding_instruments.length') > 0;
}.property('customer.debitable_funding_instruments')
});
You need to fire up selected_funding_instrument
Balanced.DebitCustomerModalComponent = Balanced.ModalComponent.extend({
init: function () {
this.selected_funding_instrument();
return this._super();
}
}):
It would be nice if you can create fiddle.
I'm having the following problem.
In my app I have a screen to make a new Site. But when I save the new site via an action on the controller, the languages-property isn't sent with the POST-request to the server.
The template for adding a new Site is this:
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="name">Name</label>
<div class="controls">
{{view Ember.TextField valueBinding="name"}}
</div>
</div>
<div class="control-group">
<label class="control-label" for="languages">Languages</label>
<div class="controls">
{{view Ember.Select contentBinding="controllers.languages" selectionBinding="languages" optionValuePath="content.id" optionLabelPath="content.description" multiple="true"}}
</div>
</div>
<div class="form-actions">
<button {{ action "createSite" }} class="btn btn-primary">Save</button>
</div>
I defined my Store like this:
App.Store = DS.Store.extend({
revision : 12,
adapter : DS.RESTAdapter.extend({
namespace : 'rest'
})
});
This is my controller:
App.SitesNewController = Em.ObjectController.extend({
needs: ['languages'],
name: null,
languages: null,
createSite : function() {
var self = this;
var name = this.get('name');
var languages = this.get('languages');
// Create the new Site model
var s = App.Site.createRecord({
name : name
});
$.each(languages,function(i,lang) {
s.get('languages').addObject(lang);
});
this.get('store').commit();
}
});
This is the Site-model
App.Site = DS.Model.extend({
name : DS.attr('string'),
languages : DS.hasMany('App.Language')
});
Language-model:
App.Language = DS.Model.extend({
description : DS.attr('string')
});
The POST-request data sent to my server is this:
{
"site":{"name":"test"}
}
So I miss the language-property. Actually I expect a language_ids property with an array of id's.
When I edit my RESTAdapter-configuration like this:
DS.RESTAdapter.map('App.Site', {
languages: { embedded: 'always' }
});
Now the POST-request data is:
{
"site": {
"name":"test",
"languages":[{
"id":2,"description":"English"
},{
"id":3,"description":"Deutsch"
}]
}
}
The languages are know embedded in the request-data. This is no problem, at the backend I get the id before I save it. But know it expects the language-data to be embedded in the GET-responses also.
What is the way to send just the id's in the POST-data? I want it to be something like this:
{
"site": {
"name":"test",
"languages":[2,3]
}
}
This answer is largely derived from this other StackOverflow answer.
App.Store = DS.Store.extend({
revision : 12,
adapter : DS.RESTAdapter.extend({
namespace : 'rest',
serializer: DS.RESTSerializer.extend({
addHasMany: function (hash, record, key, relationship) {
var type = record.constructor,
name = relationship.key,
serializedHasMany = [],
manyArray, embeddedType;
embeddedType = this.embeddedType(type, name);
if (embeddedType !== 'always') { return; }
manyArray = record.get(name);
manyArray.forEach(function (record) {
serializedHasMany.push(record.get('id'));
}, this);
hash[this.singularize(key) + '_ids'] = serializedHasMany;
}
})
})
});
For reference, you might review the JSONSerializer, which the RESTSerializer mostly inherits from. The code for what addHasMany does can be found here.
Note that, for the above snippet, the only lines that really differ are the last several. Rather than serializing embedded records, ids are pushed to the hash under the singularized key (I would have used RESTSerializer#keyForHasMany if it didn't have its own check for embedded types.