I have an array of Objects in the view that supposed to represent a dynamic number of fields in the .hbs
So the array is:
export default Ember.View.extend({
metadata_queries: [{name: '', type: 'Exists',
disableValue: true, queryValue:''}
/*, {...}, {...} */],
});
The rest of the array elements will be added dynamically
I have a Ember.TextField in the .hbs that needs to be disabled (or hidden - whichever is easier) according to disableValue (that changes by observing the type that is bound to an Ember.Select.
The code:
{{#each view.metadata_queries}}
<div class="form-group">
<div class="col-xs-2 col-xl-2">
{{view Ember.Select content=view.metaTypes selection=type}}
</div>
<div class="col-xs-2 col-xl-2">
{{view Ember.TextField classBinding=":tests-query" value=queryValue disabled=disableValue}}
</div>
</div>
{{/each}}
The thing is that disableValue is not a property - so the view doesn't get updated (I checked - the boolean itself does change)
How can I do that?
Made a JSFiddle to examplify:
http://jsfiddle.net/6Evrq/400/
Well... aparrently I didn't update disableValue properly:
The proper way to do it is:
this.get('metadata_queries').forEach(function(item, index, metaQueries) {
Ember.set(item, "disableValue", item.type === "Exists");
});
to make disableValue a property, it has to be wrapped in an Ember Object, like so
metadata_queries: [Ember.Object.create({name: '', type: 'Exists',
disableValue: true, queryValue:''})
/*, {...}, {...} */],
Related
Currently Im trying to use a function that observes a field of a controller/component in ember template (handlebars).
index.hbs
<div>
{{ input value=model.field1 }}
{{if hasFieldError('field1')}}
<span>This field is required</span>
{{/if}}
</div>
<div>
{{ input value=model.field2 }}
{{if hasFieldError('field2')}}
<span>This field is required</span>
{{/if}}
</div>
index.js
hasFieldError: function(val) {
return true if val is found in an array
}.observes('field1', 'field2'),
But this of course returns a build error:
{#if hasFieldError('compa ----------------------^ Expecting
'CLOSE_RAW_BLOCK', 'CLOSE', 'CLOSE_UNESCAPED', 'OPEN_SEXPR',
'CLOSE_SEXPR', 'ID', 'OPEN_BLOCK_PARAMS', 'STRING', 'NUMBER',
'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', 'SEP', got 'INVALID'
Any idea how to achieve this?
You can't call a function from a template, except using an action. You can only reference properties, such as fields and computed properties.
Observers are generally not a good idea
Are you really trying to determine if the value of field1 is in an array? Let's assume the array is found at array1. Then you could write a helper named contains:
{{#if (contains array1 field1)}}
But someone has already written this. Welcome to the wonderful Ember community of addons! See https://github.com/DockYard/ember-composable-helpers#contains
Just replace your observer function with a computed property:
hasFieldError: Ember.computed('field1', 'field2', function(val) {
return true if val is found in an array
}),
I have checkboxes created in a loop. I set checked to a property name, and want to reach a dynamic value from the checkbox on click. In the property (for key, value) I'm getting the wrong thing - the label of my property instead of the value. Is there a simple way in Ember to get the value out of the checkbox?
Any help is much appreciated.
In HTML:
{{#each url in controllers.application.env.urls}}
<div>
{{view Ember.Checkbox checked=updateServerList valueBinding="url"}}{{url}}
</div>
{{/each}}
In javascript:
updateServerList:function(key,value)
{
if(value!=undefined)
{
console.log("----1 ", key, value);
}
}.property(''),
Per your comment, I started actually playing around with this and realized that it was not as simple as I thought. What I ended up doing was just building a custom component rather than working with Ember.Checkbox.
In your template:
{{#each}}
{{check-box url=url}}
{{/each}}
Component Template:
<script type='text/x-handlebars' id="components/check-box">
{{input type="checkbox" checked=toggleURL}}{{url}}
</script>
The component code:
App.CheckBoxComponent = Ember.Component.extend({
toggleURL: false,
logURL: function() {
console.log(this.url);
// Do something with the URL
}.observes('toggleURL')
});
I'm new to Ember and am finding some of their concepts a bit opaque. I have a app that manages inventory for a company. There is a screen that lists the entirety of their inventory and allows them to edit each inventory item. The text fields are disabled by default and I want to have an 'edit item' button that will set disabled / true to disabled / false. I have created the following which renders out correctly:
Inv.InventoryitemsRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON("/arc/v1/api/inventory_items/" + params.location_id);
}
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" data-id=id}}>edit item</button>
<button {{action "saveInventoryItem" data-id=id}}>save item</button>
</div>
{{/each}}
</script>
So this renders in the UI fine but I am not sure how to access the specific model to change the text input from disabled/true to disabled/false. If I were just doing this as normal jQuery, I would add the id value of that specific model and place an id in the text input so that I could set the textfield. Based upon reading through docs, it seems like I would want a controller - would I want an ArrayController for this model instance or could Ember figure that out on its own?
I'm thinking I want to do something like the following but alerting the id give me undefined:
Inv.InventoryitemsController=Ember.ArrayController.extend({
isEditing: false,
actions: {
editInventoryItem: function(){
var model = this.get('model');
/*
^^^^
should this be a reference to that specific instance of a single model or the list of models provided by the InventoryitemsRoute
*/
alert('you want to edit this:' + model.id); // <-undefined
}
}
});
In the Ember docs, they use a playlist example (here: http://emberjs.com/guides/controllers/representing-multiple-models-with-arraycontroller/) like this:
App.SongsRoute = Ember.Route.extend({
setupController: function(controller, playlist) {
controller.set('model', playlist.get('songs'));
}
});
But this example is a bit confusing (for a couple of reasons) but in this particular case - how would I map their concept of playlist to me trying to edit a single inventory item?
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled="true"}}</p>
<p>{{input type="text" value=detail disabled="true"}}</p>
<button {{action "editInventoryItem" this}}>edit item</button>
<button {{action "saveInventoryItem" this}}>save item</button>
</div>
{{/each}}
</script>
and
actions: {
editInventoryItem: function(object){
alert('you want to edit this:' + object.id);
}
}
Is what you need. But let me explain in a bit more detail:
First of all, terminology: Your "model" is the entire object tied to your controller. When you call this.get('model') on an action within an array controller, you will receive the entire model, in this case an array of inventory items.
The {{#each}} handlebars tag iterates through a selected array (by default it uses your entire model as the selected array). While within the {{#each}} block helper, you can reference the specific object you are currently on by saying this. You could also name the iteration object instead of relying on a this declaration by typing {{#each thing in model}}, within which each object would be referenced as thing.
Lastly, your actions are capable of taking inputs. You can declare these inputs simply by giving the variable name after the action name. Above, I demonstrated this with {{action "saveInventoryItem" this}} which will pass this to the action saveInventoryItem. You also need to add an input parameter to that action in order for it to be accepted.
Ok, that's because as you said, you're just starting with Ember. I would probably do this:
<script type="text/x-handlebars" data-template-name="inventoryitems">
{{#each}}
<div class='row'>
<p>{{input type="text" value=header disabled=headerEnabled}}</p>
<p>{{input type="text" value=detail disabled=detailEnabled}}</p>
<button {{action "editInventoryItem"}}>edit item</button>
<button {{action "saveInventoryItem"}}>save item</button>
</div>
{{/each}}
</script>
with this, you need to define a headerEnabled property in the InventoryitemController(Note that it is singular, not the one that contains all the items), and the same for detailEnabled, and the actions, you can define them also either in the same controller or in the route:
App.InventoryitemController = Ember.ObjectController.extend({
headerEnabled: false,
detailEnabled: false,
actions: {
editInventoryItem: function() {
this.set('headerEnabled', true);
this.set('detailEnabled', true);
}
}
});
that's just an example how you can access the data, in case the same property will enable both text fields, then you only need one, instead of the two that I put . In case the 'each' loop doesn't pick up the right controller, just specify itemController.
Based on dropdown selected i.e if B is selected the template has to be modified for B.
Template -
<script type="text/x-handlebars" id="search">
...
<div class="controls">
<select class="input-medium focused">
<option>Any</option>
<option>A</option>
<option {{action 'chosenB'}} >B</option>
<option>C</option>
<option>D</option>
</select>
</div>
{{#if searchB}}
//Show view specific to B-only
{{/if}}
</script>
Router -
App.Router = Ember.Router.extend({
this.resource('search');
});
SearchController = Ember.ObjectController.extend({
searchB: false,
chosenB: function() {
this.set('searchB', true);
},
});
So, when I goto URL /search, intially B's only view is not shown as IF condition is not met. But, when a user selects B from dropdown, I have called action chosenB of SearchController from template which should have set the 'searchB" to true and B-only view should have appeared which is not happening..
Can you please help finding mistake here ..
Ember - 1.0.0-RC.2
Ember.js has a built-in Select view, please check out this doc http://emberjs.com/api/classes/Ember.Select.html, I think your problem is binding related, using the right component for the job would have the desired behaviour.
hope it helps
Edit: Problem was with my own local settings and the way I was including the views in my application. Once I fixed those issues, the problem was resolves. The code here is actually correct. The answer Chrixian provided also work.
I am stuck on something that seems rather simple. I want to access some computed properties of my view constructed inside an each loop in handlebars.
<div class='build-buttons-wrapper'>
<button class="list-builds-button" {{action "toggleBuildsList" target="view"}} ></button>
<button class="build-button" {{action "buildApp" on="click" target="view"}} >Build</button>
</div>
<div class='builds-list'>
<h2 class="build-title">Latest builds</h2>
<ul class="builds-list">
{{#each content}}
{{#view Jimux.BuildView buildBinding="this"}}
<span class="build-date">{{createdAt}}</span>
<a {{bindAttr href="srcArchive"}} class="download-button source">Source</a>
{{! *here are different ways I have tried to access "finished" property* }}
{{log build.view.finished}}
{{log view.finished}}
{{log finished}}
{{log this.finished}}
{{log build.finished}}
{{#if build.finished}}
<div class="build-progressbar"></div>
{{else}}
<div class="build-progressbar"><div class="build-percent" style="width:{{unbound percent}}%"></div></div>
{{/if}}
{{/view}}
{{/each}}
</ul>
</div>
Here is the BuildsView which is using this template:
Jimux.BuildsView = Em.View.extend({
templateName: 'builds'
listVisible: false
classNames: ['builds-view']
buildApp: (view, event, ctx) ->
#get('controller').newBuild()
,
hideList: ->
#set 'listVisible', false
this.$(".builds-list").hide("slide", {direction: "up"}, 300)
,
showList: ->
#set 'listVisible', true
this.$(".builds-list").show("slide", {direction: "up"}, 300)
,
toggleBuildsList: (view, event, ctx) ->
if #get 'listVisible' then #hideList() else #showList()
,
didInsertElement: ->
#hideList() if not #get 'listVisible'
})
And here is the BuildView which is created inside the {{#each}} iterator in the template above.
Jimux.BuildView = Ember.View.extend(
tagName: 'div',
classNames: ['build-item'],
#testBinding: true,
sample: true,
finished: ( ->
return true
#return (#get 'percent') == 100
).property('percent')
)
Everything above works as expected. For example I can access percent property of each child view using {{percent}}. But if I define my own properties inside the Jimux.BuildView as show above, I cant seem to find a way to access them within the handlebars {{#each}} iterator. You can see the different ways I have tried inside the handlebars code with {{log}} statements, all those print undefined in the console. What am I missing here?
I'm assuming the percent property you are referring to is a property of each "content" object you are looping over-- if that's the case making finished look like this:
finished: (->
return #get('context.percent') is 100
).property('context.percent')
You should be able to simply use {{finished}} within the {{#view Jimux.BuildView}} .. {{/view}} block