How to get access to outside properties in a nested {{#each}}? - javascript

I have an {{#each}} block that runs through a list of stops that a driver must make. Each stop needs to have a select run and the select draws in from another collection. So I have the select in another {{#each}}.
Now I need to programmatically check if a driver was already selected in the db for this particular stop as the select is running and have it get selected.
My problem is that I need to access info from the external {{#each}} to do the comparison in the internal {{#each}}.
Below is the code that I have but when I run it the stopNum is undefined.
Any help here is greatly appreciated.
<td class="text-left">
{{#if notEquals stopNum 0}}
<select class="form-control driverName clearForm" id="driverName{{stopNum}}" name="driverName{{stopNum}}">
<option value="" ></option>
{{#each drivers}}
{{#if dispatchDriverSelected driversName stopNum}}
<option value="{{driversName}}" selected>{{driversName}}</option>
{{else}}
<option value="{{driversName}}">{{driversName}}</option>
{{/if}}
{{/each}}
</select>
{{/if}}
</td>

Within #each, the context is set to the current element.
If you want to get an external item/property, you can get the parent context using .. in the template code or using Template.parentData(numLevels) in helpers:
{{#each drivers}}
{{#if dispatchDriverSelected driversName ../stopNum}}
<option value="{{driversName}}" selected>{{driversName}}</option>
{{else}}
<option value="{{driversName}}">{{driversName}}</option>
{{/if}}
{{/each}}
Here is a simple example:
Template structure:
<template name="example">
{{#with datum}}
<div class="wrapper">
{{outer}}
<div class="items">
{{#each inner}}
<div class="inner">
{{prop}} {{someFunc ../outer}}
</div>
{{/each}}
</div>
</div>
{{/with}}
</template>
Helpers:
Template.example.helpers({
datum() {
return {
outer: 'outer 1',
inner: [{
prop: 'inner 1'
},{
prop: 'inner 2'
}]
}
},
someFunc(datum) {
return `processed ${datum}`;
}
});
renders to:
<div class="wrapper">
outer 1
<div class="items">
<div class="inner">
inner 1 processed outer 1
</div>
<div class="inner">
inner 2 processed outer 1
</div>
</div>
</div>

Related

handlebars, access to parent each loop variable

I have code like here. I would like to set input name to #index from previous each loop.
Can I access #previousIndex somehow? Or can I assign inputs to some kind of group created before second each loop that will set all input names inside?
Data I receive:
questions:[
{
question:"How old are you?",
answers:[
'16',
'18',
'20',
'25',
'other'
]
},
{
question:"How many kids do you have?",
answers:[
'0',
'1',
'2',
'other'
]
}
]
hbs code:
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{#previousIndex}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{/each}}
When you are using #each then you get the index in #index and if you want previousIndex why not just do parseInt(#index) - 1
Here's how you do it:
var Handlebars = require('handlebars');
Handlebars.registerHelper("previous", function(value, options)
{
return parseInt(value) - 1;
});
Now update your hbs code as:
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{previous #index}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{/each}}
Hope this works for you!
I can't give you a 100% working code (environment is not currently setup) but from my previous experience with handlebars I will try to guide you to a possible solution.
{{assign "previousIndex" 0}}
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{../previousIndex}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{assign "../previousIndex" "{{#index}}"}}
{{/each}}
I had to create two helper functions like here. Get is here because I wasn`t able to access variable directly from .hbs file by simply writing {{myVariableName}}.
Hbs file looks now like here.
<div class='flexbox'>
{{assign "parentIndex" #index }}
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{get "parentIndex"}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
helpers
assign: function(varName, varValue, options) {
options.data.root[varName] = varValue;
}
get: function(varName, options) {
return options.data.root[varName]
}

Ember js pushObject overriding the array

I am working on Ember.js
and i have a select element which is set to send value "onchange" i am receiving the value without any problem ..
But i want to save every selection into an array and display it on the fly in labels
Here is my template.hbs
<div class="panel-body">
<div class="form-group">
<select name="services" id="services" class="form-control" onchange={{action 'setValue' value="target.value"}}>
<option> Select Services </option>
{{#each services as |service|}}
<option value="{{service.name}}">{{service.name}}</option>
{{/each}}
</select>
</div>
<div class="form-group">
{{selectedService}}
{{#each selectedService as |selection index|}}
<span class="label label-primary">{{index}} {{selection}}</span>
{{/each}}
</div>
</div>
and here is my controller
actions: {
setValue(ser){
var selectedService = [];
this.set('selectedService', ser);
selectedService.pushObject(ser);
console.log(selectedService);
}
i tried to log "selectedService[1]" and select two items but it's not working it keeps on overriding the first item :(
The reason with your case is you are constantly setting the selectedService to a new array. Instead of that; you need to get the selectedService of the controller and push to it. I mean the following:
export default Ember.Controller.extend({
selectedService: [],
actions: {
setValue(ser){
this.get('selectedService').pushObject(ser);
}
}
});
You initialize the selectedService once for the controller and can constantly push to the same array.
Can you please check the following twiddle, I guess this is close to what you want.

Setting the data context for a template defined by a package

Here's the relevant code template code:
<!-- display a list of users -->
<template name="available_user_list">
<h2 class="cha_heading">Choose someone to chat with:</h2>
<div class="row">
{{#each users}}
{{> available_user}}
{{/each}}
</div>
</template>
<!-- display an individual user -->
<template name="available_user">
<div class="col-md-2">
<div class="user_avatar">
{{#if isMyUser _id}}
<div class="bg-success">
{{> avatar user=this shape="circle"}}
<div class="user_name">{{getUsername _id}} (YOU)</div>
</div>
{{else}}
<a href="/chat/{{_id}}">
{{> avatar user=this shape="circle"}}
<div class="user_name">{{getUsername _id}}</div>
</a>
{{/if}}
</div>
</div>
</template>
<template name="chat_page">
<h2>Type in the box below to send a message!</h2>
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
{{#each messages}}
{{> chat_message}}
{{/each}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form class="js-send-chat">
<input class="input" type="text" name="chat" placeholder="type a message here...">
<button class="btn btn-default">send</button>
</form>
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="chat_message">
<div class="chat-message">
{{> avatar user=user size="small" shape="circle"}} <div class="chat-center">{{user}}: {{text}}</div>
</div>
</template>
and the template helpers:
Template.available_user_list.helpers({
users:function(){
return Meteor.users.find();
}
})
Template.available_user.helpers({
getUsername:function(userId){
user = Meteor.users.findOne({_id:userId});
return user.username;
},
isMyUser:function(userId){
if (userId == Meteor.userId()){
return true;
}
else {
return false;
}
}
})
Template.chat_page.helpers({
messages:function(){
var chat = Chats.findOne({_id:Session.get("chatId")});
return chat.messages;
},
other_user:function(){
return ""
},
})
Template.chat_message.helpers({
user: Meteor.user()
})
I'm making an website where users can log in and chat with each other. I've downloaded the utilities:avatar package to show the avatars of the users. The avatar image is based on the first initial of the username of the user. When I render the avatar template in the template available_user with the code {{> avatar user=this shape="circle"}}, it shows the initials in the avatars fine because it's with the context of the users collection.
I also want to show the avatar when a user sends a message but the template chat_message is within the data context of an array inside the chats collection. So it only shows the default avatar without the initial.
the documentation for the package doesn't quite specify how I can set the template parameter for user or userId. Can someone please help with this issue?
I figured it out. I included the user id into each object in the messages array under the field userId and in the template I set it as {{> avatar userId=userId size="small" shape="circle"}}

How to get model data on view in Ember JS

How can i access model data in my view template in Ember JS ?
Is it saved in a global variable?
Here's my template:
<script type="text/x-handlebars" data-template-name="todo-list">
{{#if length}}
<section id="main">
{{#if canToggle}}
{{input type="checkbox" id="toggle-all" checked=allTodos.allAreDone}}
{{/if}}
<ul id="todo-list">
{{#each}}
<li {{bind-attr class="isCompleted:completed isEditing:editing"}}>
{{#if isEditing}}
{{todo-input type="text" class="edit" value=bufferedTitle focus-out="doneEditing" insert-newline="doneEditing" escape-press="cancelEditing"}}
{{else}}
{{input type="checkbox" class="toggle" checked=isCompleted}}
<label {{action "editTodo" on="doubleClick"}}>{{title}}</label>
<button {{action "removeTodo"}} class="destroy"></button>
{{/if}}
</li>
{{/each}}
</ul>
</section>
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="todos">
<section id="todoapp">
<header id="header">
<h1>todos</h1>
{{todo-input id="new-todo" type="text" value=newTitle action="createTodo" placeholder="What needs to be done?"}}
</header>
{{outlet}}
{{#if length}}
<footer id="footer">
<span id="todo-count"><strong>{{remaining.length}}</strong> {{pluralize 'item' remaining.length}} left</span>
<ul id="filters">
<li>
{{#link-to "todos.index" activeClass="selected"}}All{{/link-to}}
</li>
<li>
{{#link-to "todos.active" activeClass="selected"}}Active{{/link-to}}
</li>
<li>
{{#link-to "todos.completed" activeClass="selected"}}Completed{{/link-to}}
</li>
</ul>
{{#if completed.length}}
<button id="clear-completed" {{action "clearCompleted"}}>Clear completed</button>
{{/if}}
</footer>
{{/if}}
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
</footer>
</script>
Thank you!
The Route sets up a "model" variable that is accessible by your template via:
{{model.myProperty}} or
{{myProperty}} (Before Ember2 this way would look up into your Controller Computed Properties. If none exists, then it would proxy through model.myProperty. See this deprecation)
In order to make the model data accessible "outisde of Ember" your can:
Create a Component
From the template, call it and pass the data down to it
Use onDidInsertElement and read the property. From now on, you can make it accessible to "the outisde world"
OBS: I'm not sure that's a good practice though.
JS BIN: http://emberjs.jsbin.com/dapidu/3/edit?html,js,output
If you want to use it in a different template (which is linked to a different controller I guess?), you can create an alias in the other controller like this :
//Include the first controller
needs: ['todoController'],
length: Ember.computed.alias('controllers.todoController.length'),
I don't have much precisions on how you built your application, but if you can't access it from the other template, it means your changing the application context (firstController -> secondController).
Do you have 2 controllers?

Meteor.js affecting only newly inserted child template

These are templates:
<template name="postsList">
{{#each posts}}
{{>postItem}}
{{/each}}
</template>
<template name="postItem">
<div class="more-less">
<div class="more-block">
<p>{{{text}}}</p>
</div>
<p class="continued">…</p>
[ + ]
</div>
</template>
When posts is updated with new post, postItem is inserted to postsLists. How can I apply this only for new inserted postItem template:
$('.more-less .more-block').css('height', 20).css('overflow', 'hidden');
Template.postItem.rendered affects all posts in page, but I need to affect only newly inserted post without affecting existing ones.
I'm going to assume new posts are added to the top of the list? if so...
<template name="postsList">
<div class="posts-list">
{{#each posts}}
{{>postItem}}
{{/each}}
</div>
</template>
<template name="postItem">
<div class="more-less">
<div class="more-block">
<p>{{{text}}}</p>
</div>
<p class="continued">…</p>
[ + ]
</div>
</template>
You can simply achieve this with CSS:
.posts-list .more-less:first-child .more-block {
height: 20px;
overflow: hidden;
}
No need to use JavaScript and worry about reactivity and DOM mutation.

Categories

Resources