I’m trying to have a reactive bootstrap selectpicker but I have a weird behaviour:
An event has to change the options of the select but it works only after the first event which is not working because the app doesn’t go into the onRendered part…
This is the onRendered part:
var renderTimeout = false;
Template.printerselect.onRendered( function(){
if (renderTimeout !== false) {
Meteor.clearTimeout(renderTimeout);
}
renderTimeout = Meteor.setTimeout(function() {
$('#printerName').selectpicker("refresh");
renderTimeout = false;
}, 10);
});
Template.printerSetupForm.onRendered( function(){
$('.selectpicker').selectpicker();
});
here, the templates :
<div class="col-lg-3 col-md-4 col-xs-12 form-group">
<label>COMPANY</label>
<select id="printerCompany" class="form-control selectpicker" name="printer[company]">
<option selected="true" disabled="disabled" value={{company}}>
Please Select A Printer Company
</option>
{{#each Companies}}
<option value="{{company}}" {{printerCompanySelected company}}> {{company}}</option>
{{/each}}
</select>
</div>
<div class="col-lg-3 col-md-4 col-xs-12 form-group">
<label>PRINTER NAME</label>
<select id="printerName" class="form-control selectpicker" name="printer[name]">
<option selected="true" disabled="disabled" value={{name}}>
Please Select A Printer
</option>
{{#each Names}}
<!--<option value="{{name}}" {{printerNameSelected name}}>
{{name}}
</option>-->
{{>printerselect}}
{{/each}}
</select>
</div>
<template name="printerselect">
<option value="{{name}}"> {{name}} </option>
</template>
The refresh is called when the page is rendered.
Then I change the company which changes some Session variables and reactively to change the name select options but the refresh is not called so that doesn’t come to the onrendered part.
But when I change again the company it’s working, the onRendered part is called.
Weird thing is that even if this is not displaying the right names, when i’m choosing a name which doesn’t match with the company, the select chooses the right one.
Sorry for my english in advance, I did my best.
Thanks you in advance !
EDIT: Names helper :
Names: () => {
company=Session.get('printer').company;
if(typeof company!='undefined') {
return Printers.find({company: company}).fetch();
} else {
return Printers.find().fetch();
}
}
I set the Session variable 'printers' with an empty field. When I select a company, it's now working but without company, I don't get any printers because I don't have to test company with 'undefined' but with empty field.
So I changed to test with empty field and it isn't working anymore...
So weird that a test could cancel a onRendered, this must be an issue with the Mongo stuff.
I will continue to search on it.
Related
I am trying to pass the selected values from the tag and pass it to a button’s click event. However, I was not able to get the data in the onclick function. See the code below.
I am getting undefined in the alert.
Please help.
<div class=“form-group”>
<div class=“form-group” align=“center”>
<label for=“select-Year”>Select CMS Fiscal Year :</label>
<select class=“form-control input-sm” style=“width:120px” id=“select-Year”>
<option v-for=“yr in years” value=“yr”>{{ yr }}</option>
</select>
</div>
<br/><br/>
<div class=“form-group” style=“align:center”>
<button id=“btnSubmit” class=“btn btn-primary” style=“align:center” v-on:click=“loadData(yr, $event)”>Open</button>
</div>
</div>
methods: {
`loadData`: function(yr, event){
alert(yr);
}
}
I tried to do this for you, go to the page and see <https://jsfiddle.net/Lr3psu2y/>.
Hope it with help for you.
The yr variable only exists within the scope of option when you used v-for on it.
That's why it's causing an error when you try to pass it to the event handler of your button which is located outside the scope of v-for.
How can I get selected option?
One way to get the year selected is to declare a year variable under your component data attribute and use the v-model directive on your select field to form a two-way binding.
data: function() {
return {
year: null
}
}
And in your select and button tags,
<select class="form-control input-sm" style="width:120px" id="select-Year" v-model="year">
<option v-for="yr in years" value="yr">{{ yr }}</option>
</select>
<button id="btnSubmit" class="btn btn-primary" style="align:center" v-on:click="loadData($event)">Open</button>
In this way, you can get access to the year in loadData,
loadData(event) {
console.log(this.year, event);
}
Tried to insert a "select" component under a tab. Basically, the tab just displays some dynamic forms. The select is a list of string for user selection Looks like my definition is correct. Do not know why it messed up the whole angular/clarity UI page.
<clr-tab>
<button clrTabLink>Submission Form</button>
<clr-tab-content>
<br>
<label class="required" for="uniqueCertIDs">Unique Cert IDs</label>
<div class="select">
<select id="partnercertIDs" formControlName="EEPortalProductDetails">
<option *ngFor="let ucertID of uniquecertIDs" [value]="ucertID.UniqueCertId">{{ucertID.UniqueCertId}} </option>
</select>
</div>
Very likely, the scope of the select portion is not correct. Failed finding related documentation.
It's difficult to say whats happening without a running example.
I created a simple form with a select in a tab and it seems to be ok.
Here is the template code for my tabs:
<clr-tabs>
<clr-tab>
<button clrTabLink id="link1">Tab1</button>
<clr-tab-content id="content1" *clrIfActive>
<h3>Form 1</h3>
<form>
<div class="form-group">
<label for="selects_1">This is a select box</label>
<div class="select">
<select id="selects_1">
<option>Select</option>
<option *ngFor="let user of users" [value]="user">{{user}} </option>
</select>
</div>
</div>
</form>
</clr-tab-content>
</clr-tab>
</clr-tabs>
You can see this running here: https://stackblitz.com/edit/so-tabs-form-select
If this doesn't solve your issue, can you fork the StackBlitz and recreate it there?
<form [formGroup]="partnersForm" >
<div class="form-group">
<label class="required" for="selects_1">Unique Cert IDs</label>
<div class="select" >
<select id="selects_1" formControlName="partnerFormCertIDs" ng-change="selectAction()">
<option *ngFor="let ucertID of uniquecertIDs" [value]="ucertID.UniqueCertId" >{{ucertID.UniqueCertId}}</option>
</select>
</div>
</div>
</form>
and in my Session.ts file. Debug using console, the alert never shows up
selectAction() {
alert("selected value changed !!!!");
}
#nextgen-ui Regarding your second issue, the data binding syntax should be either on-change or (change); ng-change is an AngularJS directive.
Please see this Stackblitz.
#Jose Gomes
The event is defined as updateCertData()
<form [formGroup]="partnersForm" >
<div class="form-group">
<label class="required" for="selects_1">Unique Cert IDs</label>
<div class="select">
<select id="selects_1" formControlName="partnerFormCertIDs" (change)="updateCertData()">
<option *ngFor="let ucertID of uniquecertIDs" [value]="ucertID.UniqueCertId">{{ucertID.UniqueCertId}}</option>
</select>
</div>
</div>
</form>
And I defined a POST API in the event
updateCertData(){
let selectedCertID = this.partnersForm.get('partnerFormCertIDs').value;
for ( let partnerInfo of this.uniquecertIDs) {
if(partnerInfo.UniqueCertId == selectedCertID){
this.extractSubmit(this.cert);
this.submit[0].Option[0].value = partnerInfo.ProductId;
this.submit[0].Option[1].value = partnerInfo.ProductName;
this.certsService.setSubmissionProduct(this.certId, this.submit);
break;
}
}
}
this.certsService.setSubmissionProduct is a POST API, sending data back to UI Server
setSubmissionProduct(sessionId: string, info:any){
let body = {'submitConfig': info};
let url = GLOBALS.baseUrl + "session/" + sessionId + "/submitproduct";
return this.post(url, body);
}
The this.post never sends message to the controller successfully. I tried several other post methods, which works well in other logic part, but never sends message successfully if it is put within this "change" event.
I want to set the value of an HTML <select> element reactively, without changing the various options for the element. I have found a solution, but it in not elegant.
To test, create a barebones Meteor app with create meteor select and change the contents of the select.html and select.js files to the following:
select.html
<body>
{{> select}}
</body>
<template name="select">
<label for="select">{{category}}</label>
<select id="select">
<option value='' disabled selected style='display:none;'>Select...</option>
<option value="Animal">Animal</option>
<option value="Vegetable">Vegetable</option>
<option value="Mineral">Mineral</option>
</select>
</template>
select.js
if (Meteor.isClient) {
Session.set("category", "")
Session.set("label", "Category")
Template.select.onRendered(function () {
setSelectValue()
})
Template.select.helpers({
category: function () {
setSelectValue()
return Session.get("label")
}
});
function setSelectValue() {
var select = $("#select")[0]
if (select) {
select.value = Session.get("category")
}
}
}
Now launch your app. In the browser console, you can change the value of the category Session variable:
Session.set("category", "Animal")
However, the select element will not update until you change the label:
Session.set("label", "category") // was "Category'
Now the select element updates, and any subsequent change the category Session variable will also update the select element.
Session.set("category", "Vegetable") // Now the select element updates
Is it possible to achieve the same effect directly, without using this clumsy workaround?
Yes. You can do it as follows:
<select id="select">
<option value="Animal" {{animalSelected}}>Animal</option>
<option value="Vegetable" {{vegetableSelected}}>Vegetable</option>
<option value="Mineral" {{mineralSelected}}>Mineral</option>
</select>
And helpers that looks something like this:
Template.select.helpers({
animalSelected: function () {
return (someCondition === true) ? 'selected' : '';
},
vegetableSelected: function () {
return (someOtherCondition) ? 'selected' : '';
}
});
A better way might be something like this though:
<select id="select">
{{#each options}}
<option value="{{value}}" {{selected}}>{{label}}</option>
{{/each}}
</select>
And then you can use this in the helpers to decide what is selected and what isn't.
Another option is just using standard jQuery to change the select box. Something like this:
$('[name=options]').val( 3 );
Or this SO answer.
If you want the selection set dynamically I usually use a handlebar helper like this
Template.newtask.helpers({
filler: function(element){
return Session.get(element);
}
});
then in html
<select class="browser-default" id="selectedService" name="jobtype">
<option id="zero" value="{{filler 'type'}}">{{filler 'type'}}</option>
So I'm trying to be efficient and clean in my Spacebars templates as I work with Meteor. But I'm stumped by the way in which checkboxes and select options are to be dealt with. Suppose I want to have a checkbox set as checked or not depending on a flag that is in a document in one of my collections. I don't appear to be able to do the following:
<input type='checkbox' id='item-{{this.item_id}}' {{#if checked}}checked{{/if}} />
When I try this, I get the following error:
A template tag of type BLOCKOPEN is not allowed here.
If I try the following options, though, they all result in the checkbox being checked even when the flag is false:
<input type='checkbox' id='item-{{this.item_id}}' checked='{{#if checked}}true{{/if}}' />
<input type='checkbox' id='item-{{this.item_id}}' checked='{{#if checked}}true{{else}}false{{/if}}' />
I have the same trouble with selected in my select options, so I end up doing something like the following to get around it, which seems verbose and error-prone:
<select id='option-{{this.item_id}}'>
{{#if option_60}}
<option value='60' selected>1 hour</option>
{{else}}
<option value='60'>1 hour</option>
{{/if}}
{{#if option_90}}
<option value='90' selected>90 mins</option>
{{else}}
<option value='90'>90 mins</option>
{{/if}}
{{#if option_120}}
<option value='120' selected>2 hours</option>
{{else}}
<option value='120'>2 hours</option>
{{/if}}
</select>
You can use non-block helpers for placing such arguments:
UI.registerHelper('checkedIf', function(val) {
return val ? 'checked' : '';
});
<input type="checkbox" {{checkedIf checked}}>
Here is an example of the code I use to solve this problem, this should be pretty straightforward.
JS
Template.myTemplate.helpers({
checked:function(){
// assumes that this.checked is the flag in your collection
return this.checked?"checked":"";
},
options:function(){
// store options in a helper to iterate over in the template
// could even use http://momentjs.com/docs/#/durations/humanize/ in this case ?
return [{
value:60,
text:"1 hour"
},{
value:90,
text:"90 mins"
},{
value:120,
text:"2 hours"
}];
},
selected:function(value){
// compare the current option value (this.value) with the parameter
// the parameter is the value from the collection in this case
return this.value==value?"selected":"";
}
});
Template.parent.helpers({
dataContext:function(){
// dummy data, should come from a collection in a real application
return {
checked:true,
value:90
};
}
});
HTML
<template name="myTemplate">
<input type="checkbox" {{checked}}>
<select>
{{#each options}}
{{! ../ syntax is used to access the parent data context which is the collection}}
<option value="{{value}}" {{selected ../value}}>{{text}}</option>
{{/each}}
</select>
</template>
<template name="parent">
{{> myTemplate dataContext}}
</template>
EDIT : using universal helpers as Hubert OG hinted at :
JS
Template.registerHelper("checkedIf",function(value){
return value?"checked":"";
});
Template.registerHelper("selectedIfEquals",function(left,right){
return left==right?"selected":"";
});
HTML
<template name="myTemplate">
<input type="checkbox" {{checkedIf checked}}>
<select>
{{#each options}}
<option value="{{value}}" {{selectedIfEquals value ../value}}>{{text}}</option>
{{/each}}
</select>
</template>
The best, most efficient and effective way to accomplish this is to setup global template helpers, one each for determining checked and selected values. For documentation on creating global template helpers, see this documentation.
For checked, I suggest implementing it in this way:
Template.registerHelper('isChecked', function(someValue) {
return someValue ? 'checked' : '';
});
For selected, I suggest implementing it in this way:
Template.registerHelper('isSelected', function(someValue) {
return someValue ? 'selected' : '';
});
With these two global template helpers implemented, you can use them in any of your templates within your application like this:
<template name="someTemplate">
<input type="checkbox" {{isChecked someValue}}>
<select>
{{#each someOptions}}
<option {{isSelected someValue}}>{{someDisplayValue}}</option>
{{/each}}
</select>
</template>
I'm having problems trying to make a reactive form. When I insert data into one collection, the other select is refreshed also. This happens with input fields also, everything gets cleared when I update one of the bound collections.
Is this supposed to happen?
My test code:
<template name="test">
<form class="form-horizontal well" id="test-form">
<select class="input-xlarge" name="item_id">
{{#each types}}
<option value="{{_id}}">{{name}}</option>
{{/each}}
</select>
<select class="input-xlarge" name="category_id">
{{#each categories}}
<option value="{{_id}}">{{name}}</option>
{{/each}}
</select>
</form>
</template>
And my helpers are:
Template.accounts.type = ->
Types.find({}).fetch()
Template.accounts.categories = ->
Categories.find({}).fetch()
I think this is a limitation of Meteor right now, hopefully it'll be resolved in the future.
As discussed on irc, some ways to get around it:
Separate them into different templates
Wrap them in a helper that calls Meteor.ui.chunk.
Use a session variable to track the currently selected state. For example:
Template.accounts.events = {
'change #category_id': function() {
Session.set('selected_category_id', $(this).val());
}
}
(and code to initially select the value in Session.get('selected_category_id') if not undefined in the template).