I am having a very strange problem with Ember that I haven't had before. The scenario is quite a lot more complicated than I can outline here so I'll do my best to give a simplified version below.
I have a component which dynamically renders forms from a schema Ember.object(). Something like the below stored as formSchema on the component, where md-text-input is a normal text input field:
[
{
component: "md-text-input",
value: "some value here",
label: "Text input"
},
{
component: "md-number-input",
value: "9",
label: "Number input"
}
]
The component template then looks like:
{{#each formSchema as |field|}}
{{field.value}}
{{component field.type value=field.value label=field.label}}
{{/each}}
The formSchema object is changed in the component which causes the form displayed to change.
The first form that renders, the binding works correctly and typing in the input field will update the {{field.value}}. However, once the formSchema updates the binding no longer works and although the input field shows the initial value, typing into it will not update field.value.
Any help is greatly appreciated as I can't seem to get my head around why Ember is losing this binding.
Thanks in advance!
Related
Let's say I have a model called "Article" with only one property called "title"...
I could write hanblebars to edit the article property like this:
<span>Title</span><span>{{input value=title}}</span>
And Ember/handlebars magically binds the value of that input box to the "title" property value.
No problem there. But I am working on a project in which I will need to generate the handlebars code dynamically, based on a model definition model.
For example, I will not know there is a property called "title", but would have instead to loop into a "modelFields" list in the model definition model.
So, the handlebars will be something like this:
Looking at the code below:
{{#each modelField in modelFields}}
<span>modelField.displayName</span><span>{{input value=modelField.propertyName}}</span>
{{/each}}
The result HTML for the "title" property will be:
<span>Title</span><span><input value="title"></span>
Now here is my question, is there a way to have the value coming dynamically from propertyName (title, in this example) to be handled by ember as a binding property title, instead of a string title?
To clarify, is there a way for the result of this:
{{#each modelField in modelFields}}
<span>modelField.displayName</span><span>{{input value=modelField.propertyName}}</span>
{{/each}}
to be treated as this (title is a binding property):
<span>Title</span><span>{{input value=title}}</span>
instead of this (title is a string):
<span>Title</span><span><input value="title"></span>
?
I tried with views,components, with no luck.
I've found answer in another post that help me find the solution. The post is:
Ember.js: TextField with dynamic binding
Although, for my purposes, I had to tweak his/her solution a little bit:
Ember.Handlebars.helper('dataTextField', function (key, options) {
options.hash.valueBinding = 'controller.' + key;
return Ember.Handlebars.helpers.input.apply(this, [options]);
});
and in the template I call:
{{#each modelField in modelFields}}
<span>{{modelField.displayName}}</span>
<span>
{{dataTextField modelField.name}}
</span>
{{/each}}
I have an array of strings that I render as a list of inputs.
Then there is a way to add a new item (new input) into this list.
However when change a text in the inputs and then add a new item, all the changes that were previously made to the old inputs are gone - and also the array of strings in my controller does not change.
Here is a code:
http://ember-twiddle.com/653be725890f234931dd
how can I preserve changes that are made to the "old inputs"?
I am quite suprised by this behaviour, since in angular this is a pretty common thing to do and it works out of the box... so I guess I must be doing something terribly wrong here :(
I tested a way that solved the problem, but maybe there's a better way, but it can create a function to perform the change in the value index and the report that there was a change in the controller, sorry the English Google translation.
Template:
{{#each valueList key="#index" as |value index|}}
<div>{{input value=value change=(action 'updateItem' index)}}</div>
{{/each}}
Controller:
actions : {
updateItem : function( index ) {
this.get( 'valueList' )[ index ] = event.target.value;
this.notifyPropertyChange( 'valueList' );
},
},
Im trying to split search results into bulks of 5 so each bulk could be presented desperately.
my data:
Wacky.Video = Ember.Object.extend({
DATA: null
presenter : null
});
The presenter holds the page number on which the data should appear - e.g model[0-5] hold val. 1, model[6-10] hold val. 2 and so on...
my controller:
Wacky.SearchController = Ember.Controller.extend({
...
resPageNum: 1,
...
This property will de/increment whenever a page number will be changed (by button click).
my HTML code:
{{#each res in model}}
{{#if pagePresentor}}
<div class="results">
//DO STUFF WITH RES. OBJECT
</div>
{{/if}}
{{/each}}
finally "pagePresentor" is a property that needs to determine which bulk to present in the current iteration.
So far Iv got this:
pagePresentor: function(value){
return value.presenter == this.get('resPageNum');
}.property('resPageNum','#each.presentor')
But I guess Im using it wrong, because nothing is getting printed at all.
Could anyone please explain how to set this working or at least the base principal for what am I doing wrong here?
Thanks in advance.
pagePresentor: function(value){
return value.presenter == this.get('resPageNum');
}.property('resPageNum','#each.presentor')
Here #each.presentor is a mistake. You should specify observed property which is in the array in the following format: arrayName.#each.propertyName according to http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/. So, here the name of array which contains the objects with presentor property is missing.
I'm trying to render a bunch of dynamic <select> boxes, supplied by a model. The user is supposed to fill these out, after which the data is saved. Currently I'm looping over the availableParameters object and rendering an Ember.Select view for each of the pairs in the object. This works alright. However, I'm having trouble setting up the valueBinding in such a way that I can retrieve the values in the controller, because I can't seem to interpolate the label variable in the passed argument to the view.
{{#each availableParameters}}
{{label}}
{{view Ember.Select
contentBinding="values"
valueBinding="controller.param.[label]"
}}
{{/each}}
The availableParameters object looks like this:
[
{
label: "label1",
values: [
"value1",
"value2",
"value3"
]
},
...
]
Is there a way to dynamically set the value of the valueBinding to controller.param.[label1] (using the array of objects above) or is there a better way of achieving what I'm looking for?
No, you need to do:
selectedValue: function() {
this.get('values')[this.get('label')]
}.property('values.[]', 'label')
This property can be on the itemController for the {{#each}} helper if you need it to be (an itemController is a good way to approach this problem)
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.