How to access parent item in handlebars helpers? - javascript

I have a template.
{{#each dbModel in model}}
<h2>Database Name : {{dbModel.databaseName}}</h2>
<h3>Select Table:
{{view Ember.Select
content=dbModel.tables
optionValuePath="content.tableName"
optionLabelPath="content.tableName"
valueBinding = "dbModel.selectedTable"
selectBinding = "dbModel.selectedTable"
}}
</h3>
<h2>Selected Table is : {{dbModel.selectedTable}}</h2>
{{#each table in dbModel.tables}}
{{dbModel.selectedTable}}
{{#matchTable table.tableName dbModel.selectedTable}}
//Get fields is selected table match with table name
{{/matchTable}}
{{/each}}
Now In the matchTable helper I am getting value of table.tableName but for dbModel.selectedTable is undefined.
dbModel.selectedTable is not part of actual model, I have added this to controller as follows.
App.DatabaseController = Ember.ArrayController.extend({
selectedTable:[],
actions: {
cancel: function () {
// something
}
}
});
When I change the value of select it automatically updates the information in <h2> tag. So it means that the value is setting and got bind properly. But for helper when I try to pass it it simply shows undefined. I searched and found that can use ../dbModel.selectedTable. Still it is undefined. Anyone please guide.
What is the way to pass the parent to the helper in each loop?

Your #each loop drops you into the scope of the tables array. ../ places you in the scope of the dbModel object. To access selectedTable within the loop, use ../selectedTable

Related

Ember JS and Iterating without effecting the count of the results

I'm using Ember JS and i don't know if my approach to achieve the thing i want is true or maybe there is another way. If there is a better way please tell me.
Now I have a model named "visit" among the data it stores is the "time" of the visit
Now the time is set through a hard-coded service loop in select tag.
I want the user to select the date of the visit "a query is sent to the database with the selected date to get the visits in that date" then I want to compare the time in the results of the query with the array provided in my service and remove the similar one's. Ergo the pre-selected time isn't available.
I tried to loop in the template for the service data then another loop for the "visit" query and added an "if" statement in the class of the time display to add "disabled" if the two values equal each other.
The problem here is that the loop shows me the data twice now or maybe thrice affected by the results found in the query.
I think there is another approach by simply handling the data in the "visit" query to simply remove the matched data from the service and the results, but I'm not sure how to do so.
Here is my template
{{#each formData.time as |time|}}
{{#each disabledTime as |disabled|}}
{{#if (eq disabled.value time.value)}}
<button class="disabled">{{time.time}}</button>
{{else}}
<button {{action 'addTime' time}} class="btn-success">{{time.time}}</button>
{{/if}}
{{/each}}
{{/each}}
And Here is my controller
formData : Ember.inject.service(),
store : Ember.inject.service(),
disabledTime : Ember.computed("model.issueDate", function(){
return this.get('store').query('visit', { where : { issueDate : this.get('model.issueDate') } } );
}),
Is there a better way to handle the data in the "disabledTime" so I take the data from the service "formData.time" and remove the similar data then return the data that doesn't match. Because this way it looks simpler and I can make the loop in the template through a "select" tag instead of the buttons.
If your query return an array, and your FormatDate.time too, what about setDiff ?
you would have something like :
formData: Ember.inject.service(),
store: Ember.inject.service(),
disabledTime: Ember.computed("model.issueDate", function(){
return this.get('store').query('visit', { where : { issueDate : this.get('model.issueDate') } } );
}),
availableTime: Ember.setDiff('formData.times', 'disabledTime')
and use it in your template
<select>
{{#each availableTimes as |time|}}
<option value=time.time>{{time.time}}</option>
{{/each}}
</select>
As i understand, you have two arrays of objects, which share a property value. You now want to filter the formData.time list to objects with a value not present in the disabledTime list.
Instead of looping always completely over the second list, for each object in the first list, you could filter formData.time beforehand:
// computed based on contents in formData.time and disabledTime
filteredTimes: Ember.computed('{disabledTime.[],formData.time.[]}', function() {
let disabled = Ember.get(this, 'disabledTimes');
// return a filtered formData.time
return Ember.get(this, 'formData.time').filter(time => {
let value = Ember.get(time, 'value');
// inclued only if value not present in disabledTime list
return !disabled.isAny('value', value));
});
})
This assumes that if an object exists only in disabledItem it can be ignored. If not so, you'd have to merge, not filter the lists (i.e. return a new list with disabled flag).

How to refer parent template in quickform attribute _id

Consider Below Code.
{{#each assignments}}
{{#with eachClientDetails}}
{{#quickRemoveButton collection=assignment _id=this._id }}
Delete
{{/quickRemoveButton}}
{{/with}}
{{/each}}
In above code, I am iterating each assignment and each assignment has single Client Detail. With each Client Detail I am adding Delete button.
Helper:
eachClientDetails(){
var client = Clients.find({_id: this.clientId}).fetch()[0];
console.log(client);
return client;
}
But the problem is, while assigning attributes to _id of quickForm, I can only assign data from current context(i.e. this._id). All I need is to access context of assignment (desired like _id=../_id). But I get below error,
Can only use `this` at the beginning of a path.
Instead of `foo.this` or `../this`, just write `foo` or `..`.
Is it possible using any helper and stuff to get the parent templates _id
Your issue with overwriting contexts could be solved by using #each in and #let instead of #each and #with:
{{#each assignment in assignments}}
{{#let client=(eachClientDetails assignment)}}
{{#quickRemoveButton collection=assignment _id=assignment._id }}
Delete {{client.name}}
{{/quickRemoveButton}}
{{/let}}
{{/each}}
Here, {{client.name}} has been added just to show how to access client's fields.
And helper's code:
eachClientDetails(assignment){
var client = Clients.findOne({_id: assignment.clientId});
console.log(client);
return client;
}

Meteor #each - How to get each single item out of a variable and render it on a page?

I am using Meteor and tried to use #each, but I have a problem with it. I have some values stored in a var like:
var a = [465,77987,2132,2];
I wanted to render each value of a on HTML. I thought #each would be the right way to do it? Actually I don´t know how to use #each and the docs I found don´t really help me. I have written the following code:
JS:
Template.page.helpers({
values: function() {
return a;
}
});
HTML:
{{#each values}}
{{> page}}
{{/each}}
But this is obviously wrong, because I get the this Error in the client console:
Error: {{#each}} currently only accepts arrays, cursors or falsey values.
I thought it is an array or not?
Update
So now I have used the #each in approach, however I still get the same error-message:
Error: {{#each}} currently only accepts arrays, cursors or falsey
values. at badSequenceError (observe-sequence.js:183) at
observe-sequence.js:148 at Object.Tracker.nonreactive (tracker.js:631)
at observe-sequence.js:125 at Tracker.Computation._compute
(tracker.js:339) at Tracker.Computation._recompute (tracker.js:358) at
Object.Tracker._runFlush (tracker.js:523) at onGlobalMessage
(meteor.js:401)
JS:
Template.page.helpers({
values: function() {
var a = [465,77987,2132,2]
return a;
}
});
HTML:
{{#each a in values }}
{{> a}}
{{/each}}
Update 2
I know now where this error comes from. I'm sorry, actually my first post was not correct. I forgot, that these values [465,77987,2132,2] are not directly stored in var a.
They are stored in a Session-Variable and var a = Session.get('values') and I think that´s the point why I get this error...So, I don´t think that this can work with #each ? Maybe I have to save it to my MongoDB first and than render it on the HTML or something like that.
try the each/in construct:
{{#each value in values}}
{{value}}
{{/each}}
c.f. http://blazejs.org/guide/spacebars.html#Each-in
you specified the entire template inside the each, which is what it was complaining about.
I'll put all the code in an example. Consider the 'import' only if you are using the recommended application structure. The files must be in the same directory. First in page.js:
import './page.html';
Template.page.helpers({
values: function() {
var a = [465,77987,2132,2];
return a;
}
});
Second in page.html:
<template name="page">
{{#each a in values}}
{{a}}
{{/each}}
</template>

Get Object attribute with array Notation inside ember template

Just trying something but not sure how to do it.
Scenario:- I'm trying to render a page which have a configuration data and view data, so basically My template loop on the configuration data to render page and fetch value from view data to display.
so my model have something like
model:{'confData':[{'displayName':'value1','fieldName':'Field1'},
{repeat similar object}
],
'viewData':{'Field1':'value1','Field2':'value2',....}
}
Now in template I'm trying to render like:
{{#each confData}}
this.displayName {{input type='text' value=viewData[this.fieldName] }}
{{/each}}
but it does not accept it. Any suggestion on how I can deal with this sort of problem.
PS: the confdata and view data in actual application will be api call which i can't change.
I saw somthing like http://jsfiddle.net/GRaa5/4/ but when I tried changing as http://jsfiddle.net/weyzay5v/ it failed.
Thanks.
There is no way to do what you want purely with Handlebars. Remember that Handlebars templates are logic-less and for presentation only. My suggestion would be to make a computed property in your controller:
data: function() {
var confData = this.get('confData');
var viewData = this.get('viewData');
return confData.map(function(data) {
return {
displayName: data.displayName,
value: viewData[data.filedName]
};
});
}.property('confData.[]', 'viewData')
Then in your template:
{{#each data}}
{{displayName}} {{input type='text' value=value}}
{{/each}}

Emberjs: How to render a view with the view name in variable?

i would like to add some cards to a board, all cards are very different. so i want to create different view for each card that can bind different events and template. I set a 'type' property in the card model which to distinguish the cards.
the board template look like below:
{{#each card in cards}}
{{render card.type card class="card"}}
{{/each}}
However, the first argument for the render help can not be a variable, it can only be the card view name string.
anyone know how to achieve this?
As you already mentioned the built-in render helper only accepts a string to lookup the template to render, therefore one possible solution would be to write your own custom render helper, which then after getting the correct name with card.get('type') delegates the rendering to the built-in render helper. This could look something like this:
Ember.Handlebars.registerBoundHelper('renderCard', function(context, card, options) {
return Ember.Handlebars.helpers.render.call(context, card.get('type'), 'card', options);
});
After that, you could use it like this in your template:
{{#each card in cards}}
{{renderCard this card}}
{{/each}}
Edit
I've also added a basic jsbin that shows it working.
Edit 2
Edited the jsbin again, using object data to be rendered into the template, see here.
Edit 3
Lamentably the DS.FixtureAdapter does not support embedded records which is what you need to make it work. But you could configure you orignal DS.RESTAdapter like this:
App.Adapter = DS.RESTAdapter.extend();
App.Adapter.map('App.Dashboard',
cards: {embedded: 'always'}
);
App.Store = DS.Store.extend({
adapter: App.Adapter
});
This way the card records are always loaded with the parent record. I guess doing this change would make the call to card.get('type') in you handlebar helper return the value rather then undefined.
Hope it helps.
For Ember 1.9 solution above doesn't work. Use another way to register helper:
Ember.Handlebars.registerHelper 'renderDynamic', (context, model, options)->
contextString = options.hash.contextString
model = options.data.view.getStream(model).value()
return Ember.Handlebars.helpers.render(model.get('type'), contextString, options)
And in the template:
= hb 'renderDynamic "this" currentModel contextString="curremtModel"
or in handlebars:
{{renderDynamic "this" currentModel contextString="curremtModel"}}

Categories