I have been trying to figure out how to set the 'selected' attribute on an option within a select box and have fallen short today. Any help would be appreciated.
What I'm trying to accomplish:
When a user chooses an option and hits submit, the data saves to a new mongo document and the new entry is shown on the screen (this part works). Upon browser refresh, the state is lost (due to no selected attribute being set) and the select box goes back to the first option. I need to figure out how to set the selected state depending on the value the user has saved on that particular document. On this page there are many of the same select boxes allowing the user to choose different options for other entries.
References (tried to implement with no luck):
Check for equality in Spacebars?
https://github.com/meteor/meteor/wiki/Using-Blaze#conditional-attributes-with-no-value-eg-checked-selected
How do I indicate 'checked' or 'selected' state for input controls in Meteor (with spacebars templates)?
select box template:
<template name="tEdit">
<div class="form-group">
<div class="controls">
{{> fFEdit}}
</div>
</div>
</template>
select box option template:
<template name="fFEdit">
<select class="form-control" name="fFSelect">
{{#each fFSelect}}
<option value="{{fId}}">{{fF}}</option>
{{/each}}
</select>
</template>
setting data context
Template.fFEdit.helpers({
fFSelect: function() {
var fFArray = [
{ fF: "foo", fId: 0 },
{ fF: "bar", fId: 1 },
{ fF: "buzz", fId: 2 }
];
return fFArray;
}
});
If you want state to be persisted across browser refreshes, you should be able to either fetch it from the server or from something like the Meteor Session.
The second thing you'd need is a helper to get the selected state. Conveniently, Meteor makes the data context available under this (for an each loop, this context is an individual item).
Template.userEdit.helpers({
userSelected: function() {
if(this.id === Session.get("selectedUserId")) {
return "selected";
}
}
});
This assumes you've set the Session variable previously with something like Session.set("selectedUserId", user._id).
Then in your template:
<template name="userEdit">
<select class="form-control" name="userSelect">
{{#each users}}
<option {{userSelected}}>{{username}}</option>
{{/each}}
</select>
</template>
(Note: The selected state can not actually be seen by inspecting the DOM with an element inspector.)
I'm not sure if this is the best answer to my problem, though this is what I came up with to add the selected attribute to the saved option. I welcome anyone else with a better/Meteor solution for this.
When the user clicks the edit link, I find the inputs that need to be updated and assign the values onClick. Ideally I'd rather have this already done on pageLoad, though feel that that solution may impact the initial pageLoad time.
Template.tItem.events({
'click .edit-t-item': function(e) {
e.preventDefault();
var $editTWrapper = $(e.currentTarget).next('.edit-t-wrapper');
$editTWrapper.find('[name=tNEdit]').val(this.tName);
$editTWrapper.find('[name=fFSelect]').val(this.fFId);
}
});
Related
I want to edit some Data in a HTML select Element:
<select id="Photoshop" name="Photoshop">
<option selected value= 0> </option>
<option value=2>Installieren</option>
<option value=0>Nicht Installieren</option>
</select>
I want to get this selected, based on the Value which is in the Database. (If I select Value 2, it should stay Value 2 after reload, now every time I reload it resets to 0)
I've tried different things with JavaScript (Newbie in js here), but it didn't work.
<script>
$(document).ready(function () {
$("#Photoshop").val("Model.Photoshop")
})
</script>
changes the value to Null
the code block from vbhtml wont want to work too. I can get the Data, but cant change it in the selectbox (doesn't "know" the selectbox)
There should be a another way, to get the Database Data, at the best without js. (Model.Photoshop should do its Job)
I haven't found something similar, which can fix this kind of problem. My researches were hopeless...
The solution is kinda simple.
<script src="~/Scripts/jquery-3.1.1.js"></script>
<script>
$(document).ready(function () {
$("#Photoshop").val("#Model.Photoshop")
})
</script>
as you can see, you just need to Type #Model.Photoshop, instead of Model.Photoshop.
If this Value is not in the select list, you will se a blank Space.
AR
I am working on a dynamic form that populates a dropdown conditionally based on another input. I have written a directive because the data that comes back will also carry validation rules that need to be applied. However when i apply the validation rules to the dropdown and recompile the select options are borked. The final HTML looks like this:
The questions on the form can depend on another to be answered in a very specific way before they appear. This dropdown for example depends on the country selected but can be required or optional depending on what country is selected. The data coming back from my server gives me a validation object that contains validation information for the input field such as:
var question = scope.question;
var input = element.find('select');
if (question.validation.required) {
if (!input.attr('required')) {
input.attr('required', question.validation.required);
}
}
$compile(input)(scope);
The standard <option value="?" selected="selected"></option> is put in but when the question the dropdown depends on triggers the watch and a request happens for the dropdown the server returns and the backing select values are changed but the HTML output results as seen above all the items that are set to selected are unselectable and the form validation fails.
function answerMatch(countryCode) {
sectionService.getDivisionsByCountryCode(countryCode).then(function (response) {
scope.question.selectValues = response.data;
element.show();
});
}
and an HTML snippet for good measure
<select id="question{{question.questionId}}" name="answer" ng-model="question.value" ng-options="value.text for value in question.selectValues" class="form-control">
</select>
In my application, I have a list of <select> controls. Change of selection in each of these controls can be made only after user confirmation. So I deal with it as follows:
<select onchange="changeSelect(this)" onmousedown="clickSelect(this)">
<option value="1" selected="true">A</option> // here is 'default' value 1
<option value="2" >B</option>
</select>
<select onchange="changeSelect(this)" onmousedown="clickSelect(this)">
<option value="1" >A</option>
<option value="2" selected="true">B</option> // here is 'default' value 2
</select>
// more select controls with arbitrary 'default' values, they can be added and removed dynamically
When the user clicks the select to choose another option, the clickSelect(this) method saves the currently chosen (old) selection:
function clickSelect(select) {
globalScope.previouslySelected = $(select).val();
}
And now, if the user really tries to change the selection, I ask the user for confirmation, and if she does not confirm, I use the saved value to restore the previous state:
function changeSelect(select) {
var response = confirm("Current changes to the threat log will be lost. Continue anyway?");
if (response == false) {
// change it back
$(select).val(globalScope.previouslySelected);
return;
}
}
}
Now, on IE, Chrome and Opera it works just fine. However, when I use Firefox, it does not work. THe reason is that FF calls the onmousedown handler twice during the selection -- first time when the user clicks to roll down the select control, and second time when she selects a new selection. Therefore the last remembered value of globalScope.previouslySelected will become the new selected value and not really the old one, as in other browsers.
I know using global variables is considered bad practice. This is not the issue here. Using any other storage will not make it work.
My only idea right now is to write a Firefox specific piece of code in the clickSelect(select) handler and ignore the second notification. However, for some reason the following code I found on stack overflow does not work:
if($.browser.mozilla) {
console.log("Olalala");
}
E.g., in Chrome it throws following exception:
Uncaught TypeError: Cannot read property 'mozilla' of undefined
So how can I make it work so that ti would only run in FF? Or if any of you guys have a better solution to the problem of confirmation, I would gladly hear it. Just remember, there is a dynamic set of select controls, not just a single one. It has to work for all of them. Plus they can be added or removed dynamically, so in a single page request their number can vary. User loads page with a single select control, presses a '+' button and adds another select control, etc., but the solution has to work uniformly with all the controls.
Is there a specific reason you're saving the currently selected option using the onmousedown listener? You can just initialize your global variable to hold the default option, and update it whenever a new selection is made.
Something like this:
HTML
<select id="mySelect" onchange="changeSelect(this)">
<option value="1" >A</option>
<option value="2" >B</option>
</select>
JS
var globalScope = {previouslySelected: $("#mySelect").val()};
function changeSelect(select) {
var response = confirm("Current changes to the threat log will be lost. Continue anyway?");
if (response == false) {
// change it back
$(select).val(globalScope.previouslySelected);
return;
}
globalScope.previouslySelected = $(select).val();
}
EDIT
I noticed that on Firefox, the second mousedown event's target is the option, so you can use the following code to ignore the event in this case:
$("select").mousedown(function(e) {
if ($(e.target).is('option'))
return;
globalScope.previouslySelected = $(this).val();
});
With this code you can remove the onmousedown registration from your DOM.
currently i'm having trouble with meteor sessions and how my code triggers it to render a template. Currently I have a session that sets its ._id to whatever is clicked.
Template.sidebar.events({
/* on click of current sidecat class change to sidecat selected */
'click .sidecat': function (event) {
Session.set("selected_project", this._id);
}
});
And I have it add a css class if selected_player equals the div.
Template.sidebar.sidebarselected = function () {
return Session.equals("selected_project", this._id) ? "sidebarselected" : '';
}
Now I render a template when there is sidebarselected and another class present. On click of the item it renders the template.
Template.sidebar.projectselected = function() {
var find = Session.get("selected_project");
var find2 = ($("#"+find).attr('class'));
/* if exists return true render the template */
if (find2 == 'project sidebarselected')
{
return true
}
/* else don't render it return null */
else {
return null
}
};
Everything up until now works great.
Now I have a Button that creates a new item in the list and makes it the selected item. This is when the trouble occurs. When I create a new Item the list item is rendered and given the class sidebarselected but it does not render the template which should be called. It does render the template when I click an item. But not when using the button to create a new list item. It becomes selected but does not render the template. Here is the code for that.
Template.sidebar.events({
/* add bar settings menu functions for clicks */
'click #newProject': function(event){
var create = NewProject();
Session.set("selected_project", create);
},
This is the NewProject Function
/* add a new project to the side bar */
function NewProject() {
id = Aprojects.insert({
name: "New Project",
type: "project"
});
doc = Aprojects.findOne({_id:id});
return doc._id;
}
Ok there is everything. Item is created when I click on button, class is added but template is not rendered. This is all of the javascript, if html is need let me know and I will provide it.
Let me add some details and html template stuff. But anyways what does work is
you can select a project in the list
when you select a project (by clicking on it), it's given a classname to indicate that it's selected
when you click "new project" you want to add an item to the list and immediately select it
That all works correctly. The problem is when clicking the new project button. It still selects the newly made project properly and gives it a class of selected. What doesn't happen is if it finds that the selectedbar class also has a class of project with it. It renders a separate template (projectpicked). Projectpicked shows up when items are clicked. Not when #newproject is clicked. Even through it ends up selected it does not show projectpicked template. Let me see if the code can do more of the talking.
<body>
{{> sidebar}}
</body>
<template name="sidebar">
{{#if aproject}}
{{#each aproject}}
<div class="sidecat {{sidebarselected}} project" id="{{divids}}">
{{name}}
</div>
{{/each}}
{{/if}}
{{#if projectselected}}
{{> projectpicked}}
{{/if}}
</template>
<template name="projectpicked">
project was picked from sidebar
</template>
The reasoning behind the application logic and DOM structure combo is that I have other things besides projects like categories. I figured using session gets the id and then we figure out what class it is like project or category and display a different template based off of what class it is. Don't know if its the best way but it's what I came up with. Open to suggestions. If somethings not clear not me know and I'll try to explain it. Thanks for the help.
I would rewrite the projectselected helper to not depend on a HTML classname to execute its logic, but to depend on a Session value. You're using Session correctly in most of your code, but it seems weird to then couple your application logic to the DOM structure.
The way your code works at the moment (without seeing your HTML template - you should post that too!), it looks like you want it to do the following:
you can select a project in the list
when you select a project (by clicking on it), it's given a classname to indicate that it's selected
when you click "new project" you want to add an item to the list and immediately select it
Here's how I'd solve that scenario:
Your template
<body>
{{> sidebar}}
</body>
<template name="sidebar">
<ul>
{{#each items}}
<li class="{{selected}} project" id="id_{{_id}}">{{_id}}</li>
{{/each}}
{{> newproject}}
</ul>
{{> projectpicked}}
</template>
<template name="newproject">
<li id="newproject">Create a new project</li>
</template>
<template name="projectpicked">
{{#if projectpicked}}
A project was picked!
{{/if}}
</template>
Your javascript
// When clicking an item in the list, remember which one we just clicked
Template.sidebar.events({
"click li": function() {
Session.set("selected_project", this._id);
}
});
// Add the selected class to the item that was remembered
// by the session when we clicked on it
Template.sidebar.selected = function() {
return Session.equals("selected_project", this._id) ? "selected" : "";
}
Template.sidebar.items = function() {
// or whatever code you have to return the list of items
return Projects.find();
}
// when clicking the new project button, create the new project
// and remember the id, then store that as the selected value in the session.
Template.newproject.events({
"click #newproject": function() {
var newprojectId = newProject(); // do your thing
Session.set("selected_project", newprojectId);
}
});
Template.projectpicked.projectpicked = function() {
return !Session.equals("selected_project", undefined);
}
The key here is to only set the selected_project Session value when you click on something. That happens twice; when you click an item to select it, and when you click the new project.
To get your template to draw the selected item, all you need to do in Meteor is describe when an item is selected: an item is selected when some session value matches its id. That's all.
I haven't tested this code and it's obviously incomplete, but hopefully this points you in the right direction for refactoring your code a bit.
Well, after getting rather frustrated and rewriting most of the code over again I found the problem. In my projectpicked template instead of using Session.get and then using it to find the class. In stead I just have a
if ($('.sidecat.project').hasClass('sidebarselected')) {
return true
}
While the original was:
if (find2 == 'project sidebarselected')
{
return true
}
So I used some jquery instead of directly comparing variable find2 to a string.
The select values are confusing me. When the user edits a row in my app I clone a tag with jquery (called empty-X) and put it on a modal window so that the user can edit the values. At the same time I get a json object (data) from server and fill in the current fields on the modal window as it stands in the database :
empty_X.find('#id_deals-1-currency').val(data[0].fields['currency']);
Now when the modal shows, the user can see how the correct currency is selected in the dropdown.
Yet when I check the HTML for this element with Firebug, I get a different picture, nothing seems selected.
<select id="id_deals-1-currency" name="deals-1-currency">
<option selected="selected" value="">---------</option>
<option value="1">USD - $</option>
<option value="2">EUR - €</option>
<option value="3">GBP - £</option>
</select>
And yet when I send the form to the server, there are no validation errors and the currency is the same value as it was previously set through val(). Life is good.
While this works by itself, there is a problem. What if the user wants to get back to the edit mode and verify the currency once more before saving it?
In this case I can't load the values from the database any more. His previous local changes matter now. I have to clone the current record with currency inside back in the modal window, so the user can see what he had changed previously and verify it. The problem is now the user doesn't see the currency he had changed in the previous step. In fact he would see an empty dropdown instead.
What are my options here? Is there a way to set the selected flag to the actual selection rather than using val()?
When cloning a <select>, the option with the 'selected' attribute becomes the current option in the cloned object - instead of the actual current object (as per value attribute).
To counter this, you can find the currently selected option from the value returned by val() and then apply the selected attribute to it prior to cloning it. This way you wont need to set the value after cloning.
Demo: http://jsfiddle.net/DqADq/
Code: (.x1 is the <select>)
// simple cloning
$('.x1:first').clone().appendTo('.out');
// setting selected attr before cloning
var v = $('.x1:first').val();
$('.x1:first option').removeAttr('selected'); // remove 'selected' from all options
$('.x1:first option').each(function() {
if($(this).attr('value') == v) {
$(this).attr('selected', true); // apply 'selected' to current option
}
});
$('.x1:first').clone().appendTo('.out');