I need to implement 2 dependent selects in my meteor app. I am new with web development in general and very new with meteor. However I have done something that works but I would like to know if that is the correct way or if there is any other better way to do it. Why I am wondering this is because I have seen some code on the web which I can understand very well yet so I prefer to do it as I think it should be.
The selects are one with car brands and the second one with the models of the selected brand.
I am using the Session in meteor 1.3 (although I've read a little bit that there is something call reactive vars which can be used as well).
Please do not take into account security issues for now.
Thanks in advance.
Here is my code:
const VEHICLE_MODELS = "vehicleModels";
function setVehicleModels(vehicleModels){
Session.set(VEHICLE_MODELS, vehicleModels);
}
function getVehicleModels(){
return Session.get(VEHICLE_MODELS);
}
Template.startJob.events({
'change #carBrands': function(e){
e.preventDefault();
var selectedBrand = $("#carBrands").val();
var vehicleData = Vehicles.findOne({"make":selectedBrand})
setVehicleModels(vehicleData.models);
}
});
Template.startJob.helpers({
carBrands(){
return Vehicles.find();
},
carModels(){
return getVehicleModels();
}
});
<div class="form-group">
<label for="carBrands" class="col-sm-2 control-label">
Make
</label>
<div class="col-sm-10">
<select id="carBrands" class="form-control select2">
<option value="">Select a brand...</option>
{{#each carBrands}}
<option value="{{make}}">{{make}}</option>
{{/each}}
</select>
</div>
</div>
<div class="form-group">
<label for="carModels" class="col-sm-2 control-label">
Model
</label>
<div class="col-sm-10">
<select id="carModels" class="form-control select2">
<option value="">Select a model...</option>
{{#each carModels}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
</div>
The classic pattern for this in Meteor is to use a Session variable which is set by an event and drives the data context of the dependent menu. This simplifies your code a tiny bit since only one Session variable (or alternatively a reactive variable) is required.
Template.startJob.events({
'change #carBrands': function(e){
e.preventDefault();
var selectedBrand = $("#carBrands").val();
var vehicleData = Vehicles.findOne({ make: selectedBrand })
Session.set('brandId', selectedBrand._id);
}
});
Template.startJob.helpers({
carBrands(){
return Vehicles.find();
},
carModels(){
return VehicleModels.find({ brandId: Session.get('brandId') });
}
});
With no changes to your html.
Related
I have a Google Library with HTML form, process form script addNewItemand script to run the form in popup window addItem.
function addItem()
{
var html = HtmlService.createHtmlOutputFromFile('input/form.html');
SpreadsheetApp.getUi()
.showModalDialog(html, 'Add New Recipe');
}
function addNewItem(form_data)
{
var url = "SPREADSHEET_URL_TO_DATA_COLLECTION";
var ss = SpreadsheetApp.openByUrl(url);
var sheet = ss.getSheetByName('List');
var asiaTime = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd");
var dishName = form_data.dish_name;
var cuisineName = form_data.cuisine_name;
var placeName = form_data.place_name;
var categoryName = form_data.category_name;
var num = sheet.getRange(sheet.getLastRow(), 1).getValue() + 1 || sheet.getLastRow();
sheet.appendRow([num, dishName, cuisineName, placeName, categoryName, asiaTime]);
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<form id="myform">
<div class="block form-group">
<label for="dish_name">Dish name</label>
<input type='text' name='dish_name' id="dish_name" required="required"/>
</div>
<div class="block form-group">
<label for="place_name">Place</label>
<select id="place_name" name="place_name" type="text" required>
<option value="LL4H">LL4H</option>
<option value="LL4T">LL4T</option>
</select>
</div>
<div class="block form-group">
<label for="cuisine_name">Cuisine</label>
<select id="cuisine_name" name="cuisine_name" type="text" required>
<option value="Asian">Asian</option>
<option value="Western">Western</option>
</select>
</div>
<div class="block form-group">
<label for="category_name">Category</label>
<input id="category_name" name="category_name" type="text" list="quote-choices" required>
<datalist id="quote-choices">
<option value="Starter">Starter</option>
<option value="Main course">Main course</option>
<option value="Veggi side">Veggi side</option>
<option value="Carbs side">Carbs side</option>
<option value="Dessert">Dessert</option>
<option value="Dough">Dough</option>
<option value="Sauce">Sauce</option>
<option value="Drink">Drink</option>
<option value="Other">Other</option>
</datalist>
</div>
<div class="block">
<button type="submit" class="action">Submit</button>
</div>
</form>
<script>
document.querySelector("#myform").addEventListener("submit",
function(e)
{
e.preventDefault(); //stop form from submitting
console.log(this)
google.script.run.withSuccessHandler(()=> google.script.host.close()).addNewItem(this);
}
);
</script>
</body>
</html>
I connected this Library with the Google Spreadsheet and declared new function to run library script to open the form.
function createRecipe() {
RecipeBuilder.addItem();
}
function addNewItem(form_data) {
RecipeBuilder.addNewItem(form_data);
}
Form appears in popup window well.
I click Submit to submit my data from the form, but serverside process does not start.
How to run this form correct? Where I'm wrong? How to fix it?
UPDATED
It's still does not work with library but works well if I put it in bound-container script of some spreadsheet. Unfortunately, I can't use it in bound-container because full code of addNewItem(form_data) function must replicate current spreadsheet. Finally it will be 1000+ Google Spreadsheets with same numbers of bound-containers. It will be super complicated to update it
I believe your goal and situation as follows.
You want to use addItem(), addNewItem(form_data) and input/form.html as a GAS library.
When you call this library from the client side, when the submit button is clicked, the data is not sent.
You want to remove this issue.
The library name is RecipeBuilder.
For this, how about this modification?
Modification points:
I think that the reason of your issue is google.script.run.addNewItem(this);. In this case, when addNewItem(this) is run, this function is searched from the GAS project. By this, an error occurs like Uncaught TypeError: google.script.run.addNewItem is not a function. I think that this is the reason of your issue.
In order to remove this issue, how about the following modification? In this modification, one more function is added to the client side.
Modified script:
function createRecipe() {
RecipeBuilder.addItem();
}
// Added
function addNewItem(form_data) {
RecipeBuilder.addNewItem(form_data);
}
By this, when google.script.run.addNewItem(this); is run at the library side, addNewItem of client side is run, and then, RecipeBuilder.addNewItem of the library side is run.
Note:
In your Javascript of library side, google.script.run.addNewItem(this); and google.script.host.close(); are run in order. But google.script.run works with the asynchronous process. So if the process of addNewItem(this) is slow, the dialog might be closed before addNewItem(this) is run. So I think that the following modification might be also used.
From
google.script.run.addNewItem(this);
google.script.host.close();//close this dialogbox
To
google.script.run.withSuccessHandler(()=> google.script.host.close()).addNewItem(this);
Reference:
Class google.script.run
I recommend you put your scripts into:
document.addEventListener('DOMContentLoaded', event => {
// your code here
});
I have multiple select with different values. When the user selects the options that he wishes, press add button and must be added as elements to an array and that new line of added options be displayed in the view.
This does not work correctly, since each new selection steps on the values of the previous one. Only the last chosen option prevails. Example of my wrong array of values:
[{"product":"Product3","type":"A"},{"product":"Product3","type":"A"},{"type":"Product3","type":"A"}]
Here is my code:
<div class="col-md-3">
<label for="product" class="control-label">Product</label>
<select class="form-control pull-right" name="product" ng-model="data.product" >
<option ng-repeat='(key,value) in productList' value="{{key}}">{{value}}</option>
</select>
</div>
<div class="col-md-3">
<label for="type" class="control-label">Type</label>
<select class="form-control pull-right" name="type" ng-model="data.type" >
<option ng-repeat='i in typeList' value="{{i}}">{{i}}</option>
</select>
</div>
<button type="button" class="btn" ng-click="addElements()" >Add</button>
<div class="col-md-3">{{elementsList}}</div>
And in my angular controller:
$scope.elementsList = [];
$scope.addElements = function() {
$scope.elementsList.push($scope.data);
}
What's my problem? I am new in angular and js and it is difficult for me to see it ... how can I add and remove elements dynamically? Thanks!
When you add the $scope.data object to the array in addElements, it adds a reference to that object because that's how Javascript objects are stored: by reference. What that means is that any time that $scope.data changes in the future, the value you see in the array will also change. So you can do something like this instead:
$scope.addElements = function() {
const clone = Object.assign({}, $scope.data);
$scope.elementsList.push(clone);
}
This creates a new object clone each time.
I am new to angular and I'm confused over this thing. I'm trying to populate a select box based on object inside an array. I want selectbox by using ng-repeat for that array... but initially i need to show only one selectbox after clicking add() next selectbox has to come. for ref:[initially one selectbox has to come]
HTML
<div class="col-lg-12" id="variant1" style="margin-top:10px" ng-repeat="variant in variants">
<div class="col-lg-4" style="text-align:right;padding-top:2px;padding-right: 20px" >
<label for="variant1name">Variant Name</label>
</div>
<div class="col-lg-6" >
<div >
<select class="form-control" ng-model="filterFormInputs.apps" ng-options="app.Application for app in variants" >
<option value="" disabled selected>Select an Application</option>
</select>
<label ng-repeat="checkbox in filterFormInputs.apps.details">
<input class="ecomtexttype1" type="checkbox" ng-model="checkbox.checked"> {{checkbox.name}}
</label>
</div>
</div>
</div>
Controller:
$scope.variants =
[
{"Application": "Color", "details":[{"name":"red"},{"name":"blue"},{"name":"black"}]},
{"Application": "Color", "details":[{"name":"red"},{"name":"blue"},{"name":"black"}]},
{"Application": "Color", "details":[{"name":"red"},{"name":"blue"},{"name":"black"}]}
]
I think that you can just have your add() function update the array... if things are configured correctly the new row should render due to the binding on the array.
As mentioned in comments you did not provide enough source code, so here is the assumed pseudo-code:
in html you might have
<button ng-click="$scope.add()">Add</button>
so in the controller
$scope.variants = [
// array of whatever you are displaying
{"foo":"bar1"},
{"foo":"bar2"}
];
$scope.add = function() {
variants.push({"foo":"bar_new"});
}
Have you considered using a Directive for this? That may work better, depending on your situation. Look into this: https://docs.angularjs.org/guide/directive
I am generating dropdown at runtime using AngularJs. I also want to bind corresponding options with it which will be generated based on dropdown type being created.
<div class="form-group" ng-repeat="attrib in col1Attribs">
<label class="control-label" for="txtCode">{{attrib.displayText}}</label>
<select class="form-control"
ng-options="item.configValue for item in configOptions(attrib.configType)" />
</div>
My controller has following method.
$scope.configOptions = function (type){
return SmartCache.get(type); //SmartCache is my cacheFactory
}
EDIT:
Here is my data when parameter type = 'Status'
[{"$id":"1","people":[],"configId":"STAT001","configValue":"Active","startDate":"2014-01-31T00:00:00","endDate":"9999-01-01T00:00:00","description":"Active","status":1,"parentId":null,"isSelectable":true,"timestamp":"AAAAAAAAKHM=","isSystemData":true,"children":[],"parent":null,"personAttributes":[],"items1":[]},{"$id":"2","people":[],"configId":"STAT002","configValue":"Suspended","startDate":"2014-01-31T00:00:00","endDate":"9999-01-01T00:00:00","description":"Suspended","status":1,"parentId":null,"isSelectable":true,"timestamp":"AAAAAAAAKHQ=","isSystemData":true,"children":[],"parent":null,"personAttributes":[],"items1":[]},{"$id":"3","people":[],"configId":"STAT003","configValue":"Terminated","startDate":"2014-01-31T00:00:00","endDate":"9999-01-01T00:00:00","description":"Terminated","status":1,"parentId":null,"isSelectable":true,"timestamp":"AAAAAAAAKHU=","isSystemData":true,"children":[],"parent":null,"personAttributes":[],"items1":[]},{"$id":"4","people":[],"configId":"STAT004","configValue":"Deleted","startDate":"2014-01-31T00:00:00","endDate":"9999-01-01T00:00:00","description":"Deleted","status":1,"parentId":null,"isSelectable":true,"timestamp":"AAAAAAAAKHY=","isSystemData":true,"children":[],"parent":null,"personAttributes":[],"items1":[]}]
I am able to achieve my task using code below but not using ngOptions.
<div class="form-group" ng-repeat="attrib in col1Attribs">
<label class="control-label" for="txtCode">{{attrib.displayText}}</label>
<select class="form-control">
<option ng-repeat="c in configOptions(attrib.configType)" value="{{c.configId}}">
{{c.configValue}}</option>
</div>
</div>
Try this jsFiddle
I can't see the rest of your code. Here is a snippet of how to create the dropdown:
<select data-ng-options="e.description for e in configOptions" data-ng-model="selectedOption" />
Demo:http://plnkr.co/edit/YcuqZ1gs4iDAAgfLrZPO?p=preview
Probably you are trying this
Use as
<select class="form-control"
ng-options="item.configId as item.configValue for item in configOptions(attrib.configType)" />
configId adds as option value and configValue as display value.
The following code is exhibiting a very strange behavior...
I have two html selects: one for regions and another one for departments bearing in mind that for one selected region I am trying to load the corresponding departments.
The first html select (region) is populated upon normal page load and the second html select (departments) by an ajax call retrieving JSON from the server.
I use jquery chosen.
The strange behavior I am referring to is as follows:
upon a first change of the region select (say region A is selected), the department select is not populated.
Upon a second change of the region select (say region B is selected), the department select is populated by region A's departments!!!!
Javascript:
$(document).ready(function() {
$(".chzn-select").chosen({no_results_text: "No results matched"});
var geolocationRegionSelect = $("#geolocationRegionSelect");
var geolocationDepartmentSelect = $("#geolocationDepartmentSelect");
geolocationRegionSelect.bind('change', function(event) {
$.get("/kadjoukor/geolocations/findDepartmentsByRegion?regionId="+$(this).val(), function(result){
geolocationDepartmentSelect.empty();
$.each(result, function() {
geolocationDepartmentSelect.append($("<option />").val(this.id).text(this.department));
});
}, 'json');
$(".chzn-select").trigger("liszt:updated");
});
});
Here is the corresponding html:
<div class="control-group">
<label class="control-label" for="geolocationRegionSelect">geolocation region</label>
<div class="controls">
<select id="geolocationRegionSelect">
<option th:value="''" th:text="'Non renseigne'"></option>
<option th:each="geolocationRegion: ${geolocationRegions}" th:value="${geolocationRegion.id}" th:text="${geolocationRegion.region}"></option>
</select>
</div>
<div class="controls">
<select id="geolocationDepartmentSelect" th:field="*{geolocationDepartments}" data-placeholder="Choose a department" multiple="multiple" class="chzn-select">
</select>
</div>
</div>
Can anyone please advise?
EDIT: Generated HTML:
<label class="control-label" for="geolocationRegionSelect">geolocation region</label>
<div class="controls">
<select id="geolocationRegionSelect">
<option value="">Non renseigne</option>
<option value="1">Alsace</option><option value="2">Aquitaine</option><option value="3">Auvergne</option><option value="4">Basse-Normandie</option><option value="5">Bourgogne</option><option value="6">Bretagne</option><option value="7">Centre</option><option value="8">Champagne-Ardenne</option><option value="9">Corse</option><option value="10">DOM-TOM</option><option value="11">Franche-Comté</option><option value="12">Haute-Normandie</option><option value="13">Ile-de-France</option><option value="14">Languedoc-Roussillon</option><option value="15">Limousin</option><option value="16">Lorraine</option><option value="17">Midi-Pyrénées</option><option value="18">Nord-Pas-de-Calais</option><option value="19">Pays de la Loire</option><option value="20">Picardie</option><option value="21">Poitou-Charentes</option><option value="22">Provence-Alpes-Côte d'Azur</option><option value="23">Rhône-Alpes</option>
</select>
</div>
<div class="controls">
<select id="geolocationDepartmentSelect" data-placeholder="Choose a department" multiple="multiple" class="chzn-select">
</select>
</div>
You should trigger update after element updated, inside handler...
$(document).ready(function() {
$(".chzn-select").chosen({no_results_text: "No results matched"});
var geolocationRegionSelect = $("#geolocationRegionSelect");
var geolocationDepartmentSelect = $("#geolocationDepartmentSelect");
geolocationRegionSelect.bind('change', function(event) {
$.get("/kadjoukor/geolocations/findDepartmentsByRegion?regionId="+$(this).val(), function(result){
geolocationDepartmentSelect.empty();
$.each(result, function() {
geolocationDepartmentSelect.append($("<option />").val(this.id).text(this.department));
geolocationDepartmentSelect.trigger("liszt:updated"); // <--TO HERE
});
}, 'json');
//<-FROM HERE
});
});
http://jsfiddle.net/oceog/rQJeX/
You not get epected results because $.get() here is asynchronous, get the analogy:
You ask your wife to go to the post office and ask them how much you should pay for your father's annual magazine subscription, you tell her - "when you back leave note on that table, and remove old note from here" (on table already the note for 100$, subscription is 50$).
After she leaves you ask your mother to check a note on the table and give it to your father to make him pay...
your question is equal to that - "why my father payed 100$" ?
the answer is equal to that - you had to ask your wife to tell your mother to bring note to your father, when note on the table will be refreshed by your wife.
sorry for english, feel free to edit that if needed.