Dependent dropdown menu in different collection - javascript

I'm new in Meteor. I'm trying to make dropdown menu dependent in other dropdown. The first one for client name in Customers collection & the second for the client address in Addresses collection. I've 2 collections Customers & Addresses. This is my code but don't know what to do next.
EDIT: i put both templates in another template called new order
HTML:
<template name="selectClient">
Client Name :
<select class="select">
<option selected disabled>Choose client name</option>
{{#each custom}}
<option>{{clientName}}</option>
{{/each}}
</select>
</template>
<template name="selectAddress">
Address:
<select class="select" name="Choose the address">
<option selected disabled>Choose the address</option>
{{#each address}}
<option>{{addressName}}</option>
{{/each}}
</select>
</template>
main.js
Template.selectClient.helpers({
'custom': function(){
return Customers.find();
}
});
Template.selectAddress.helpers({
'address': function(){
return Addresses.find();
}
});
var clientName= $('[name="newName"]').val();
var mobNumber = $('[name="newMob"]').val();
var age = $('[name="age"]').val();
var radioValue= $('[name="gender"]').val();
Customers.insert({
clientName: clientName,
age: age,
radioValue:gender,
createdAt: new Date()
});
var addressName = $('[name="addressName"]').val();
var detail = $('[name= details]').val();
Addresses.insert({
addressName: addressName,
detail: detail,
createdAt: new Date()
});
Customers = new Mongo.Collection('customers');
Addresses = new Mongo.Collection('addresses');
Mobile = new Mongo.Collection('mobile');

Since you are using two templates in parallel (and not in a parent-child relation) you may use a ReactiveVar to cache the current selected client name:
const selectedCustomer = new ReactiveVar()
Note, that it needs to be accessible for both templates. Either you declare it with both templates in one file or your use import / export to provide access over many files.
Now your customers select needs to have a value assigned to each option, so we can cache it on selection change:
<template name="selectClient">
Client Name :
<select class="select-customer">
<option selected disabled>Choose client name</option>
{{#each custom}}
<option value="clientName" selected="{{#if selectedClient clientName}}selected{{/if}}">{{clientName}}</option>
{{/each}}
</select>
</template>
I renamed it's class, to prevent confusion in naming, to select-customter. Noticed the {{#if selectedClient}}... code? We will use a helper to restore the last selected state on the dropdown. Otherwise your dropdown selection will reset on the next rendering cycle:
Template.selectClient.helpers({
'custom': function(){
return Customers.find();
},
selectedClient(name) {
const selected = selectedCustomer.get()
return selected && selected.clientName === name
}
});
We get the selected customer from the cache and check if the current option's value is the same. If true we can flag the option as selected.
Now you still need an event that covers the selected client:
Template.selectClient.events({
'change .select'(event, templateInstance) {
// get the value using jQuery
const value = templateInstance.$(event.currentTarget).val()
// update the ReactiveVar
selectedCustomer.set({ clientName: value })
}
})
It saves the selected value (currently the clientName) in a query-able format. Now in your adresses you just need to query all Adresses documents using the cached selected client:
Template.selectAddress.helpers({
'address': function(){
const query = selectedCustomer.get() || {}
return Addresses.find(query);
}
});
If a client is selected it will server as the query, otherwise all adresses will be returned.
The good thing is, that this ReactiveVar already provides you the capability to trigger a new rendering cycle if it updates, since your helpers' code relies on it and Blaze automatically resolves this for you.
Modificatopns
This code assumes, that Adresses have a relation to Customers by a field named clientName. If you have stored the relation by using other fields, such as _id - clientId you need to modify your code accordingly.
You could also hide the second dropdown and only display it, if there is a value in selectedCustomer.

Related

How to display only the text in datalist and not the value?

Let suppose that we have the following datalist, and a js variable var carID = '':
<input list="options" #change='${carID = e.target.value}'>
<datalist id="options">
<option value="ID_1">Ferrari</option>
<option value="ID_2">Lamborghini</option>
<option value="ID_3">Jeep</option>
</datalist>
I'd like to show ONLY the car names in my options, and NOT the option values (that are the IDs of the cars), and have the ID of the selected car (the value of the selected option) stored in the variable, not the car name.
I tried different solutions, I post 2 of them (one totally wrong and one right but not complete, I 've found this one in other stack overflow questions):
wrong: it simply doesn't work, e.target.carID is ''.
<input list="options" #change="${carID = e.target.carID}">
<datalist id="options">
<option carID="ID_1" value="Ferrari"></option>
<option carID="ID_2" value="Lamborghini"></option>
<option carID="ID_3" value="Jeep"></option>
</datalist>
Ok it's working, but what if I have 2 cars with the same name and different id? Yes, the second car is ignored and if I select the 2nd car I store the 1st car's ID.
<input id='inputID' list="options" #change='${this.getValue}'>
<datalist id="options">
<option data-value="ID_1" value="Ferrari"></option>
<option data-value="ID_2" value="Lamborghini"></option>
<option data-value="ID_3" value="Jeep"></option>
<option data-value="ID_4" value="Jeep"></option>
</datalist>
js:
getValue(){
let shownValue = this.shadowRoot.getElementById('inputID').value;
let rightValue =
this.shadowRoot.querySelector("#options[value='"+shownValue+"']").dataset.value;
carID = rightValue;
}
I cannot use JQuery. Do you have any solutions? Thanks!
Your code #change='${carID = e.target.carID}' cannot work, as the right hand side of the event handler binding is not callable. You need to wrap it inside an anonymous function, e.g. like so: #change=${(e) => { this.carID = e.target.value }}
That being said, this is what I understood you want to do:
Have a list, where the user can choose from.
In the list, only display the name of the car, not the ID.
Store the selected car's ID in carID, not the name.
I see two ways to do that.
Option 1: Use <select>
If the list of cars is fixed, I think you will be best served using a <select height="1"> element, resulting in a drop down box. Including the little event handler, it looks something like this:
<select #change=${(e) => { this.carID = e.target.value }}>
<option value="ID_1">Ferrari</option>
<option value="ID_2">Lamborghini</option>
<option value="ID_3">Jeep</option>
<option value="ID_4">Jeep</option>
</select>
This will display the text from the text content of the <option> elements, but set the value of the <select> from the <option>'s value attribute, and by the virtue of the onchange event handler will set the carID field on the element.
You can even have two cars with different IDs, but the same name. Note however, that your users would not know, if the display text is the same, which of the two "Jeep" entries to choose. So that might not be a good idea (but I don't know your full use case).
Option 2: Use <input> with <datalist>
Now, if the list of cars is not fixed, i.e. the users are allowed to enter arbitrary data and the selection list is not for limiting their choices, but to help them (prevent typos, speed-up entry) you can use an <input> with an associated <datalist>. But the popup will display both, the <option>'s value and text content (if they are both defined and different). If you insist on only showing the name of the car, not the ID, then the name has to go in the value attribute of the <option> (or the text content). While you could put the ID in the dataset, you really don't need to.
In any case you'll need to map the value string back to the ID through your own code. This will only work if "cars and names" is a one-to-one (aka bijective) mapping, so no two cars with the exact same name would be allowed. (Otherwise your code cannot know which one has been selected just by looking at the name.)
const CARS_BY_ID = {
ID_1: 'Ferrari',
ID_2: 'Lamborghini',
ID_3: 'Jeep',
}
class MyElem extends LitElement {
constructor() {
super();
this.carID = null;
}
render() {
return html`
<input list="myopts" #change=${this.carChanged}>
<datalist id="myopts">
${Object.values(CARS_BY_ID).map((name) => html`<option>${name}</option>`)}
</datalist>`;
}
carChanged(e) {
const name = e.target.value;
this.carID = null;
for (const [key, value] of Object.entries(CARS_BY_ID)) {
if (value === name) {
this.carID = key;
}
}
console.log(`this.carID = ${this.carID}`);
}
}
Note, that in this example the user can e.g. enter "Bugatti" and this.carID will be null.
Also note, that this.carID has not been registered as a lit-element property (it's not listed in static get properties), so there will be no update lifecycle triggered, and no re-rendering happens upon that change.

Select first element in dropdown in Angular 7

I am getting content of dropdown list as a Map<number, string>. When I get the map, it is received sorted according to keys in ascending order.
While showing it in html, I am setting pipe keyvalue and provided a function to sort the items in ascending order of values.
Now, I am trying to select first element of this dropdown, but unable to do so.
I have tried jQuery method to select the first element.
I have tried to set ngModel of the select box to first value of the map, but it sets the value to the first value received in the Map which is sorted by key.
My HTML:
<select class="form-control" id="empId" [(ngModel)]="empId" [disabled]="!isEditable">
<option *ngFor="let emp of empNames | keyvalue:descOrder" [value]="emp.key">{{ emp.value }}</option>
</select>
My ts file:
this.commonService.getEmployeeList())
.subscribe((response) => {
this.empNames = response;
this.empId = this.empNames.keys().next().value;
});
data I am sending from server is:
{id:1,name: "Tim"},
{id:6,name: "Martha"},
{id:5,name: "Alex"},
{id:8,name: "Stacy"}
data I am receiving on screen is like:
Alex
Martha
Stacy
Tim
with Tim pre-selected
what I need is Alex should be pre-selected.
Then set the empId before subscribing.
this.empId = 5;
this.commonService.getEmployeeList())
.subscribe((response) => {
this.empNames = response;
});
Of course you might want another logic based on some kind of order. You can never be sure how the data are going to be received.
In this case you need to send the order from your api and filter by order.
Working Demo
<option *ngFor="let emp of empNames | keyvalue:descOrder" [value]="emp.key" [selected]="emp.id === 1">{{ emp.value }}</option>
you can use selected attribute like above
I would highly recommand you to use Angular's reactive forms! And set the select's value to the one you want, when you recieve your data. Don't use ngModel as it is deprecated and should have been removed by Angular 7 (Or will be soon). Check this
The best way to pre select an option is to use ngModel as you tried. Your list is sorted by keys so what you want is not to select the first item, yes it's the first but in other order so or you change the order in code or you search for the item you want to select and stores it to set on model.
I would suggest some changes that should improve the code and fix your problem.
<select class="form-control" id="empId" [(ngModel)]="currentEmployer" [disabled]="!isEditable">
<option *ngFor="let emp of employers$ | async" [value]="emp">{{ emp.value }}</option>
</select>
And order your list in a pipe with the function you prefer.
public currentEmployer: Employer = null;
private sortByNameAscending(e1, e2) {
return e1.name > e2.name ? 1 : 0;
}
this.employers$ = this.commonService.getEmployeeList().pipe(
switchMap(employers => {
const sortedList = employers.sort(this.sortByNameAscending);
if (sortedList.length > 0) {
this.currentEmployer = sortedList[0];
}
return sortedList;
})
);

Dynamically filled HTML select, change selected visual . Blade Laravel template

I have this code. I'm working in Blade template by Laravel framework.
<select class="form-control" name="id_zupanije" id="id_zupanije" onchange="popuniGradove(this, document.getElementById('id_grada'))">
#foreach($zupanije as $zupanija)
#if($zupanija->id == $idzupanije)
<option value="{{$zupanija->id}}" selected="selected">{{$zupanija->naziv_zupanije}}</option>
#else
<option value="{{$zupanija->id}}" selected="">{{$zupanija->naziv_zupanije}}</option>
#endif
#endforeach
<option value="0" selected="">--Odaberite--</option>
idzupanije is id of the select option that needs to be selected...
javascript function "popuniGradove" is for creating select options for another select.
What I want to know is how to visual update selected option, so when window loads I see created select and showing me selected option, not this one
"--Odaberite--".
EDIT
here is screenshoot of how it looks..
I have 3 selects.. first is Zupanija (eng. "province"), Grad (eng. City), Kvart (eng. quart).. when I select zupanija, select grad is filled with options -> cities that have foregin key id_zupanija in table .. samo for kvart, after city is selected, javascript creates options with proper kvarts
... After I press submit (bnt Filtriraj) I refresh the page and filter results below... but I want my selects to save their choosen options before before submiting.. they keep showing --Odaberite-- (default option, last created) afer submiting..
If I understand you right you could consider using a Package like the old laravel 4 FormBuilder.
E. g. https://github.com/kristijanhusak/laravel-form-builder
That way you can bind every form to the respective model like so:
{!! Form::model($user, array('route' => array('user.update', $user->id))) !!}
Laravel automatically checks if input is existing in cache and will attach that data to the form.
You have to add 2 selectize, in this example we have first one for states (for example) and a second one for cities (for example). when we select a state the page send an ajax request to fetch cities in this state, then we set cities list on the cities' select.
the state select :
<select id="select-cities-state" class="selectized">
<option value="1">State 1</option>
...
</select>
the cities select :
<select id="select-cities-city" class="selectized" disabled="">
<option value=""></option>
</select>
var xhr;
var select_state, $select_state;
var select_city, $select_city;
$select_state = $('#select-cities-state').selectize({
onChange: function(value) {
if (!value.length) return;
select_city.disable();
select_city.clearOptions();
select_city.load(function(callback) {
xhr && xhr.abort();
xhr = $.ajax({
url: 'https://jsonp.afeld.me/?url=http://api.sba.gov/geodata/primary_city_links_for_state_of/' + value + '.json',
success: function(results) {
select_city.enable();
callback(results);
},
error: function() {
callback();
}
})
});
}
});
$select_city = $('#select-cities-city').selectize({
valueField: 'name',
labelField: 'name',
searchField: ['name']
});
select_city = $select_city[0].selectize;
select_state = $select_state[0].selectize;
select_city.disable();

Meteor how to access different documents in a form

I have a simple form that uses a couple different helpers :
<select class="form-control">
{{#each openTables}}
<option>Table {{tableNumber}}</option>
{{/each}}
</select>
<select id="working-servers" class="form-control">
{{#each workingServers}}
<option>{{name}}</option>
{{/each}}
</select>
<input id="num-of-guests-select" type="text" value="" name="num-of-guests-select">
openTables and workingServers are global helpers that access different collections
Template.registerHelper('openTables', () => {
let openTables = CurrentTables.find({occupied : false});
if(openTables) {
return openTables;
}
})
Template.registerHelper('workingServers', () => {
let servers = Servers.find({working : true});
if(servers) {
return servers;
}
});
My question is basically : I am trying to update a document in that CurrentTables collection with the information from the form.
Template.newTableModal.events({
'click #sendTable' : function(event, template) {
event.preventDefault();
event.stopPropagation();
}
});
In that event function, how do I access the data context of those select boxes? For example,
{{#each workingServers}}
<option>{{name}}</option>
{{/each}}
each of the objects in workingServers has an ID that I want to be able to access in that event function :
CurrentTables.update(tableId?, {$set: {"serverId" : ??, "currentGuests" : ??}});
How do I access those documents in relation to the document in the form when I make it? Rather, how do I get that serverId from the document selected in that workingServers loop.
Is there a better way to do this kind of thing because I need to be able to do similar forms in the future? I mean I know I could take the name value ,
$("#working-servers").val() and look up in the Servers collection to find the ID that matches but that seems really bad.
<select>
<option value="1">Volvo</option>
<option value="2">Saab</option>
<option value="3">Opel</option>
<option value="4">Audi</option>
</select>
value is the returned value when selected, the text part inside the tag is the displayed value.
So, use your id in value and that's what you'll get when selecting
{{#each workingServers}}
<option value={{serverId}}>{{name}}</option>
{{/each}}

How to access selected option in Angular.js controller

I have two selects, the contents of second must depends on selected value at first select. E.g. I want to load list of companies (to 2nd <select>) which providing particular service (selected in 1st <select>):
<div ng-controller="GetCompaniesByService as class">
<select ng-options="service.name for service in class.services" ng-model="selectedService"></select>
Chosen service_id: {{ selectedService.id }}
<select ng-options="company.name for company in class.companies" ng-model="selectedCompany"></select>
</div>
How to access in controller current selectedService.id value from model?
Е.g I need to dynamically load data via ajax, from url like this
$http.get('http://127.0.0.1:3000/service/'+selectedService.id)
to use it in second <select>
you have to watch for selectedService in your controller scope. First initialize the value of selectedService to "false", then when the user will change the option selectedService will get the value of the select.
Once the value of selectedService became different from "false" show the second select/option.
your html :
<div ng-controller="GetCompaniesByService as class">
<select ng-options="service as service.name in class.services" ng-model="selectedService"></select>
Chosen service_id: {{ selectedService.id }}
<select ng-options="company as company.name in class.companies" ng-model="selectedCompany" ng-show="selectedService"></select>
</div>
your controller :
app.controller('GetCompaniesByService',function($scope){
$scope.class = {}; //initialize the class object
$scope.class.services = [...]; //your first $http request
$scope.selectedService = false;
$scope.class.companies = function(){
if(selectedService){
return $http.get('http://127.0.0.1:3000/service/'+selectedService.id)
.success(function(data){return data;})
.error(function(error){return [];});
}
else{
return [];
}
};
});

Categories