what is the best way to define and use dates , datetime and datetime-tz in emberjs ?
I can put
App.Customer = DS.Model.extend({
testDate: DS.attr('date', {defaultValue: '2013-09-13'),
testDateTime: DS.attr('date', {defaultValue: '2013-09-13T20:47:20+01:00'),
into the model, but it seems to me that I can only put
{{input type="datetime-local"
into the template. this allows me to update the field according to the local time (which may not be what I want), but has a nice drop-down calendar and allows me to enter the time
Any other type (datetime-tz for example) seems to only show as a text input.
thanks for your time
datetime-tz is not a valid input type. http://www.w3.org/TR/html-markup/input.html
The way that datetime-local is styled is not part of the spec and is left up to the browser.
You might want to use something like the jQueryUI datepicker to ensure that things work the same across browsers. In that case you could extend the Ember.TextField view to handle setting up and tearing down the datepicker widget.
App.DatePicker = Ember.TextField.extend({
didInsertElement : function(){
this.$().datepicker();
},
willDestroyElement : function(){
this.$().datepicker('destroy');
}
});
{{view App.DatePicker valueBinding="date"}}
Here's a JSBin : http://jsbin.com/ucanam/1054/edit
Related
I have a route that displays 5 categories. Each category is intended to only have one child. I created a child route and pass the selected category type id as queryparamter. In the child route model() I use the parameter with store.queryRecord() to query the backend to either return the record that matches that type OR nothing. This seems to work fine as long as a record exists. The problem I am running into is if I select a category that doesn’t have a child record. When nothing is returned from queryRecord the template continues to display the previous data. I can see the network request completing successfully and it returns an empty array. If I refresh the page the template correctly shows that there is no model data.
I’ve been struggling all day trying to find a way to refresh the template when the model no longer has a record. I have a feeling I am going at this backwards, I would be grateful for any pointers.
Parent:
export default Ember.Route.extend(AuthenticatedRouteMixin,{
user: Ember.inject.service('user'),
model() {
var user = this.get('user');
return this.store.findAll('strategic-priority',{ location: user.get('selectedLocationId'), year: user.get('selectedYearId') });
}
});
HBS
{{#each model as |strategic-priority|}}
{{#link-to 'priority-area.goal' (query-params priorityArea=strategic-priority.id) class="list-group-item"}} {{strategic-priority.label}} - {{strategic-priority.text}} {{/link-to}}
{{/each}}
Child:
export default Ember.Route.extend({
user: Ember.inject.service('user'),
queryParams: {
priorityArea: {
refreshModel: true,
replace: false,
}
},
model(params) {
Ember.Logger.debug(params); //I see this is in the console so I know this code is being called each time
var user = this.get('user');
return this.store.queryRecord('goal',{ location: user.get('selectedLocationId'), year: user.get('selectedYearId'),priority: params.priorityArea});
}
});
What you could try is to wrap the template that displays a category with {{#if hasModel}}. So something like
{{#if hasModel}}
... your template ...
{{/if}}
and then in the controller for your route
hasModel: Ember.computed.notEmpty('model')
I don’t know if this is the correct answer but my workaround was to change the model to use store.query rather than queryRecord. In my template I did a {{#each model as |xx|}} even though I only expected one record.
I was also able to avoid the situation in most cases by using hasMany. It is a bit cumbersome to gather the data on the backend (at least at my skill level with php and zf2) but in the end it seems to work pretty nice.
Thanks for advice.
Posted this on the emberjs forums, but SO seems more appropriate.
Hi! I have two routes called classyears and classyear. They're nested like so:
this.resource('classyears', function(){
this.resource('classyear', { path: '/classyear/:classyear_id'});
});
Posterkiosk.ClassyearsRoute = Ember.Route.extend({
model: function() {
return Posterkiosk.Classyear.find();
}
});
Posterkiosk.ClassyearRoute = Ember.Route.extend({
model: function(model) {
return Posterkiosk.Classyear.find(model.classyear_id);
}
});
My templates are:
Classyears:
<div class="yearList">
{{#each item in model}}
{{#linkTo 'classyear' item}}{{item.id}}{{/linkTo}}
{{/each}}
</div>
{{outlet}}
Classyear:
<div class="transformContainer">
{{trigger sizeComposites}}
{{name}}
{{#each students}}
{{partial student}}
{{/each}}
</div>
(The "trigger" helper is from another SO post. The issue was happening prior to adding it, though)
I'm using the Ember-model RESTAdapter. When I load /classyear/:classyear_id, it looks like classyear is rendering its data twice. Once with the correctly-loaded data, and once with no data loaded. The order appears to be random. If the no-data option happens last, it wipes out the correctly-loaded data, leaving a blank page. Vice-versa, and the page content displays just fine.
Any thoughts?
/edit 2: More info:
It looks as though the 0-record reply is from classyears loading. So, it's likely that the zero-record reply is actually just zero records in my hasMany field "students".
If I load /classyears (no class year specified), it only loads once, to get the class year options. If I then click on a class year, it doesn't reload classyears unless I refresh the page, at which time, it loads both, and if the classyears load (a findall) finishes second, it displays no data on the page (other than the classyears template, correctly populated, at the top).
So... maybe my classyears model isn't handling the hasMany field correctly?
I feel like I'm getting closer, but still not sure what's up.
First of all you need to specify a model for a Student, like so:
Posterkiosk.Student = Ember.Model.extend({
id: Ember.attr(),
name: Ember.attr(),
imageUrl: Ember.attr(),
gradyear: Ember.attr()
});
Posterkiosk.Student.adapter = fixtureAdapter;
Now, in your example you are setting the key for the has many to students, but students is an array of objects, not id's, so create a property called student_ids, and pass an array of ids, now that is your key.
Posterkiosk.Classyear = Ember.Model.extend({
students: Ember.hasMany('Posterkiosk.Student', {key: 'student_ids'})
});
If you set embedded: true, then your Classyears server response should come back like this:
{
classyears: [
{..},
{..}
],
students: [
{..},
{..}
]
}
Otherwise, EM would make a separate call to the endpoint on the Student model, and get that data based on the student_ids property.
See the working jsbin.
Tip: RC.7+ removed the underscore from partials, plus the partial name should be in quotes..
I have spent about 10 days on a simple problem I could have solved in about 10 minutes with Dojo. I hope I am missing something simple - I'm a noob who would like to use Ember.
I am simply trying to populate the content of an Ember.Select with data from another Controller using EmberData. Consider the case: being able to select a FoodGroup for a Raw Ingredient.
It seemed clear to use a 'needs' dependency between RawIngredientController and FoodGroupsController, and bind on the content.
This is the closest I have gotten to success and it does not look sufficient to me - I will explain.
<script type="text/x-handlebars" data-template-name="rawIngredients">
{{#each item in controller itemController="rawIngredient" }}
{{! view Ember.Select
contentBinding="controllers.foodGroups.content"
optionValuePath="content.id"
optionLabelPath="content.name"
prompt="select Food Group" }}
{{/each}}
</script>
Cook.RawIngredientController = Ember.ObjectController.extend({
isEditing: false,
needs: ['foodGroups'],
...
});
Cook.RawIngredient = DS.Model.extend({
name: DS.attr('string'),
nameKey: DS.attr('string'),
foodGroup: DS.belongsTo('Cook.FoodGroup')
});
Cook.FoodGroupsRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', Cook.FoodGroup.all()); // have also tried find()
}
});
Cook.FoodGroup = DS.Model.extend({
name: DS.attr('string'),
nameKey: DS.attr('string')
});
This actually renders all RawIngredients with no errors, but the Selects are empty because the FoodGroups store is empty. I can navigate to the FoodGroups screen which loads the store, come back to RawIngredients, and the selects are populated with FoodGroups.
I've seen a number of posts on this issue but none sufficiently address the issue when EmberData is involved. There are plenty of post-loaded examples, like this clever one from pangratz http://jsfiddle.net/pangratz/hcxrJ/ - but I haven't found any using EmberData to lazy-load the content.
This post is pretty close
Ember.js: Ember.Select valueBinding and lazy loading
and contains a workaround using an observer, which I couldn't get to work. I ended up binding to a binding which never invoked the actual loading of the FoodGroup data. Here's an example, maybe not the best, of one such attempt (of probably 20+):
Cook.FoodGroupsController = Ember.ArrayController.extend({
...
// NOTE: This seems redundant
values: function() {
return this.get('content');
}.property(),
...
});
Cook.RawIngredientController = Ember.ObjectController.extend({
...
// this should be the instance; also tried the object name FoodGroupsController....also tried using needs + 'controllers.foodGroups.values' in the binding
allFoodGroups: Ember.Binding.oneWay('Cook.foodGroupsController.values'), '
/*
// ... or rather ....??
allFoodGroups: function() {
this.get('foodGroupsBinding');
},
foodGroupsBinding: "Cook.foodGroupsController.values",
*/
...
});
{{view Ember.Select
contentBinding="allFoodGroups"
optionValuePath="content.id"
optionLabelPath="content.name"
prompt="select Food Group" }}
but it errors saying 'allFoodGroups' is providing a Binding not an Ember.Array. I think I am lost in a sea of swirling naming conventions. I could show my other attempts at this, but all had errors of undefined content.
Ugh. So backing up a bit... at least using my original solution and pre-loading the FoodGroups store should provide a workaround, however I cannot seem to get EmberData to go out and load the data, programmatically. I tried an init() function in the RawIngredientController like
init: function() {
var item = this.get('controllers.foodGroups.content.firstObject');
},
but I haven't found the right combination there either. And even if I do, it seems like the wrong approach because this will set up a reference for each RawIngredient rather than use a single reference from RawIngredients (or FoodGroups?) - but that seems like a different topic.
This is my attempt at a fiddle describing the problem http://jsfiddle.net/jockor/Xphhg/13/
Has anyone figured out an efficient, effective way to load and use stores defined in other controllers, using EmberData to lazy-load the associated content?
Your basic problem seems to be that you're not accessing the foodGroups route, so its setupController() never gets executed, thus the content of the select controller never gets set.
In your example, when adding a link to the route in question and clicking it, the route gets initialized and the bindings work.
I tried to update your JSFiddle, but it is linking to the "latest" version of ember-data from Github which has not been updated in a while (you're supposed to build it yourself until they make an official release), and it's also using an old version of Ember, so I was getting some weird errors.
So here is a version with the latest Ember and Ember-data: http://jsfiddle.net/tPsp5/
Notice what happens when you click the Countries link. I left behind some debugger; statements that may help you understand what gets invoked when.
As a design note, your "parent" controller should probably not depend on "child" controllers.
I have a model Currencies. Fields in the model are name:string and default:boolean.
On the backend only one of records in database can have default:true. I want it to be selected by default in select in Ember js.
Please give me an example how to make such select and use it
Example:
name: type1 default: false
name: type2 default: true
name: type3 default: false
I want to generate such select :
<selected>
<option>type1</option
<option selected=selected>type2</option
<option>type3</option
</selected>
Route.js
#route 'addincome', { path: 'operations/addincome' }
EmberMoney.AddincomeRoute = Ember.Route.extend
model: ->
EmberMoney.IncomeOperation.createRecord()
setupController: (controller, model) ->
controller.set('currencies', EmberMoney.Currency.find())
controller.set('content', model)
addincome.handlebars
// Some output with Incomes records
{{view EmberMoney.Select viewName="select"
contentBinding="controller.currencies"
optionLabelPath="content.name"
optionValuePath="content.id"
selectionBinding="controller.defaultType"}}
addincome_controller.js
EmberMoney.AddincomeController = Ember.ObjectController.extend({
setDefaultCurrency: function(){
if(this.get('currencies.isLoaded')){
this.set('defaultType', this.get('currencies').findProperty('default'));
}
}.observes('currencies.isLoaded')
})
My answer builds upon that one already posted. The answer is to use the selectionBinding and a clever way to set it up:
{{view Ember.Select
contentBinding="controller.types"
optionLabelPath="content.name"
optionValuePath="content.id"
selectionBinding="controller.selectedCurrency"
}}
EmberMoney.IncomesController = Ember.ArrayController.extend({
selectedCurrency : null,
currenciesObserver: function(){
var currencies = this.get("currencies");
var default = currencies.findProperty("default");
if(default)
this.set("selectedCurrency", default);
}.observes('currencies.#each')
});
How does the solution work?
We set up a selectionBinding (two way) between the Select control and your controller. If you set a currency in your controller in the property 'selectedCurrency', it will be the selected value in the Select control. Vice versa if you select a currency in the Select Box, it will be set in your controller.
I am doing the initial setup with the helper of an observer. The observer is called each time the watched currencies array changes.
Every time it is called, it tries to find a default currency. If it finds one, it sets it in your selectedCurrency property. Now the selectionBinding kicks and does its magic for you.
An additional advantage of this solution is, that you can grab the selected value easily from the property, if the user chooses another currency.
Use selectionBinding for the selection, and ComputedProperty to find the appropriate item
Update
EmberMoney.AddincomeController = Ember.ObjectController.extend({
setDefaultCurrency: function(){
if(this.get('currencies.isLoaded')){
this.set('defaultType', this.get('currencies').findProperty('default'));
}
}.observes('currencies.isLoaded')
})
The advantage of using isLoaded over #each for the observer is that the observer wont get fired everytime a currency is added instead it makes sure to set the value once the entire collection is loaded, Again it is upto the use case to determine which to use when
Details about findProperty
Hope this helps
Screenshot
I'm having trouble figuring out how to properly populate and accept an update from an Ember form under RC1. I've boiled it down to the bare essentials in this jsfiddle. I've made it far enough to display the form for a particular entity (user with first and last name) and the current values populate in the fields. However, as the user types, the fields actually update with each keystroke, and clicking the back button reveals that the data has already been changed without clicking the update button. I'd prefer to keep some logic in between the updates and only confirm an update after the user clicks the update button.
{{#view App.PersonFormView}}
First name: {{view Ember.TextField valueBinding="firstName"}}
Last name: {{view Ember.TextField valueBinding="lastName"}}
<button {{action "updatePerson"}}>Update</button>
{{/view}}
In the form template, I was trying to follow one of the Ember.js examples, but doing so resulted in a long delay and a monstrous deprecation warning using RC1. I think the examples are still being updated. I'd prefer a more handlebars-elegant way of coding the form if it existed.
The second problem is that I cannot capture the submit event itself, either on the form view or the controller. I don't know where this event is going.
App.PersonFormController = Ember.ObjectController.extend({
updatePerson: function(params){
// this doesn't get triggered as I would have expected
console.log('controller updatePerson: '+params);
}
});
App.PersonFormView = Ember.View.extend({
tagName: 'form',
updatePerson: function(params){
// this doesn't get triggered either!
console.log('updatePerson params: '+params);
}
});
In summary, I need to:
populate the input fields with the values without having them linked directly back to the model's data while the user is typing
catch the submit button's (or other control would be fine) clicked event along with the fields - and the entity's id - so that I can set them back on the model's data manually
There are several things:
I cannot capture the submit event itself
Events are fired in the controller and the route, not the view. The reason why your controller PersonFormController wasn't catching the event, is because the name is wrong. The controller should be named after the route: EditPersonController.
It's generally good to pass the model along with the action:
<button {{action "updatePerson" content}}>Update</button>
Here is an updated version that catches the event: http://jsfiddle.net/teddyzeenny/L9HMm/5/
populate the input fields with the values without having them linked directly back to the model's data
It's generally good practice to bind the fields directly to the model, to avoid code duplication.
Your problem is not that the fields are bound directly to the model, it's that you have no control over what is happening (saved, not saved, left the route...)
To have solid control, it's best to put your updating logic in your route. That way you can act accordingly when the user enters/leaves the route.
To catch your events in the route:
App.EditPersonRoute = Ember.Route.extend({
events: {
updatePerson: function(record) {
record.one('didUpdate', this, function() {
this.transitionTo('index');
});
record.get('transaction').commit();
}
}
});
To rollback changes if the user doesn't click on Update, use the deactivate callback in the route:
App.EditPersonRoute = Ember.Route.extend({
deactivate: function() {
this.modelFor('editPerson').get('transaction').rollback();
},
events: {
updatePerson: function(record) {
record.one('didUpdate', this, function() {
this.transitionTo('index');
});
record.get('transaction').commit();
}
}
});
Now these won't work in the fiddle since you are not using ember-data models.