Ractive.js dynamically choose a Select option when a checkbox value changes - javascript

I have a checkbox which toggles state - giving a true or false value.
If the value is false, I'd like to hide the select box. If the value is true, I'd like to show the select box. This is all working 'out of the box' with just some template stuff - I simple do:
{{#if fooState }}
<select class="form-control" id="addFoo" value='{{selectedFoo}}'>
<option selected disabled>Select Foo</option>
{{#foo}}
<option value="{{_id}}|{{title}}">{{title}}</option>
{{/foo}}
</select>
{{/if}}
However, if I select a Foo option... this option remains selected when I set the fooState to false. I'd like to reset this - so the first option (select Foo) is selected - emptying the {{selectedFoo}} value.
I've tried doing stuff with observe, and events - but cant quite seem to grok this. When I punch my code into the console - I'm able to change the select option... but cant seem to trigger it from when the state changes.

Ok - so I finally figured it out... to do this, I don't lean on the data binding... I create a new on-change event and add that to the select.
<select class="form-control" id="addFoo" on-change='selectedFoo' >
<option></option>
</select>
ractive.on('selectedFoo' , function (event) {
var resourceArray = event.node.options[event.node.options.selectedIndex].value // Get our selected Foo option
resourceArray = resourceArray.split('|')
var FooData = {
a1: resourceArray[0],
a2: resourceArray[1],
a3: 'foo'
}
data.foo.push(resourceData);
});
So grab the data from the event - and then manually push it onto the array. This is completely self contained - it only affects this single select. Now does everything that I need to it to.
* Scrub this... it appears to break two way data binding. Please see above *
So I was watching a Ractive video - and had an epiphany! I was sort of stabbing around in the right area - but doing it all wrong:
ractive.on('fooStateToggle', function(event) {
console.log(event)
if (data.fooState) {
// Reset our select box! This took me ****ing ages to figure out... but its so, so, simple!
var addFooElem = ractive.nodes.addFoo;
addFooElem.selectedIndex = 0;
} else {
.....
}
});
So I needed to add some code in the event that's fired when the checkbox is clicked (fooStateToggle). Then if fooState is true... we grab the select box from the Ractive node list (not the dom - this is a virtual dom, so doing a direct selection wasn't going to work) and select the the first option by index. One gotcha... when the elem is hidden, its no longer available in the ractive.node list (makes perfect sense as its not being rendered to the page) so you have to catch it as soon as its rendered. You cant act upon it when its hidden - as it doesn't exist. I must say, I'm loving Ractive... I'm finding it so much quicker to pick up than React / Angular or any of the others. I love it! (thats probably going to get edited out by someone - but the guys who made this are awesome. Its made my dev so much easier!!)

Related

Javascript's "selectedIndex" appears to work, but actually doesn't give the same behavior as a real click

I have an HTML form with a Javascript custom dropdown function taken from W3Schools. It replaces the ugly default dropdown with a really neat one, and works in most cases. However, I ran into a problem.
The custom dropdown code uses the function "selectedIndex" in order to define which label should be selected when the user clicks. It seems to work, but I am also using the Sisyphus "save form data" plugin, and when I refresh the page, the user changes are lost.
I know it is not a problem with Sisyphus or my implementation, because if I unhide the default original dropdown, I click on it, and upon refresh the options are saved just fine.
This inquiry shows that the "selectedIndex" function doesn't give exactly the same result as if the user had physically clicked on the label. It appears to change the value but somehow doesn't really register it, spooky....
After reading similar issues on stackoverflow, I added the two following lines under the "selectIndex" function: trying to programmatically set it's "selected" state to "true", and also to trigger a click:
s.selectedIndex = i;
s.options[i].selected = true;
s.options[i].click();
Still no luck. Here is a wider view of the code:
// When an item is clicked, update the original select box, and the selected item
for (i = 0; i < sl; i++) {
if (s.options[i].innerHTML == this.innerHTML) {
//update the original select box
s.selectedIndex = i;
s.options[i].selected = true;
s.options[i].click();
//update the new select box
h.innerHTML = this.innerHTML;
}
}
Here is the HTML:
<div class="dropdown has-label">
<label for="jours_entiers_de_travail">Number of days</label>
<div class="select-wrapper">
<select name="jours_entiers_de_travail" id="jours_entiers_de_travail">
<option value="1">1 day</option>
<option value="2">2 days</option>
<option value="3">3 days</option>
</select>
</div>
</div>
And a full version of the dropdown can be seen on this Codepen:
https://codepen.io/benviatte/pen/OJNYwRy
This codepen appears to work, but again, the issue comes when I try to use the value assigned by selectedIndex, for example with Sisyphus. The value doesn't seem to have been properly assigned.
Thank you dearly for your help
Sisyphus documentation hints that it uses change events to monitor updates of form elements. Source code appears to confirm this in JSDoc markup for the bindSaveDataOnChange function.
Hence try triggering a change event on the select box instead of clicking the option element programmatically. Untested but possibly like
//update the original select box
s.selectedIndex = i;
s.options[i].selected = true;
// s.options[i].click(); // replace with:
$(s).trigger("change"); // trigger change event on select box
Also see Trigger change event <select> using jquery for a variety of ways of triggering change events in both jQuery and plain JavaScript, and trigger() | jQuery API Documentation.

Changes to selected element in scope won't be persisted in array of available elements

I'm using angularJS 1.5.6 and try to update a value by using a combo box within an object that had already being selected through another combo box. But after switching the source object these changes are lost.
For a better understanding try this JSFiddle.
Within the above example the first combo box just selects an item out of a list of availables:
<select data-ng-model="currentElement"
data-ng-options="element.name for element in elements track by element.id"
data-ng-change="updateDeeperOption()">
</select>
The second combo box works on a second list of available options:
<select data-ng-model="deeperOption"
data-ng-options="option.name for option in availableOptions track by option.value"
data-ng-change="writeDeeperOptionToCurrentElement()">
</select>
The two method, which are being called whenever a change happens are simply updating the data in both directions:
$scope.updateDeeperOption = function() {
$scope.deeperOption = $scope.availableOptions.filter(function (option) {
return option.value === $scope.currentElement.going.deeper.to.myOption; })[0];
};
$scope.writeDeeperOptionToCurrentElement = function() {
$scope.currentElement.going.deeper.to.myOption = $scope.deeperOption.value;
};
The problem comes from writeDeeperOptionToCurrentElement(). It updates the current element (you can see it within the fiddle), but if you switch to another element and back again, the changes are lost.
Any idea, what I made wrong? I think, I'm still missing some fundamental understanding about when objects are copied or just referenced within AngularJS. So any explanation or links would be helpful.
Update:
I isolated the actual problem here and found that if you remove the track by from your first select then the currentElement object gets updated by reference. See: https://jsfiddle.net/tbzggyg1/4/
So instead of this:
<select data-ng-model="currentElement"
data-ng-options="element.name for element in elements track by element.id"
data-ng-change="updateDeeperOption()">
Try this:
<select data-ng-model="currentElement"
data-ng-options="element.name for element in elements"
data-ng-change="updateDeeperOption()">
And here is why: https://docs.angularjs.org/api/ng/directive/ngOptions
... is to use a track by clause, because then ngOptions will track the identity of the item not by reference, but by the result of the track by expression. For example, if your collection items have an id property, you would track by item.id.
Old answer:
It looks like your '$scope.currentElement' object is actually just getting a copy reference from the array, so when you make modifications it doesnt really update the original array (which is what your dropdowns bind to). I tried this and it worked:
$scope.writeDeeperOptionToCurrentElement = function() {
// $scope.currentElement.going.deeper.to.myOption = $scope.deeperOption.value; // old code
var elementRef = $scope.elements.filter(isMatchingElement)[0];
elementRef.going.deeper.to.myOption = "" + $scope.deeperOption.value;
};
function isMatchingElement(e) {
return e.id === $scope.currentElement.id;
}
Also, add this to your template so you can see the entire picture of whats happening:
<tt>{{currentElement}}</tt>
<hr>
<tt>{{elements}}</tt>
Link to fiddle forked: https://jsfiddle.net/t2dvm0a2/
In ng-options each option item is identified by the value you provide in track by.
Had you given your deep value in track by, things would have worked.
<select data-ng-model="currentElement"
data-ng-options="element.name for element in elements track by element.going.deeper.to.myOption"
data-ng-change="updateDeeperOption()">
</select>
Please check the updated fiddle
https://jsfiddle.net/tbzggyg1/3/

How do I remove a <select> option once it's been chosen using jQuery?

I'm trying to make it so that once the dropdown option is selected, it'll be removed from the menu, and then once another one is selected the previous removed option will get returned to the menu. Is there a way to do this using jQuery?
I'm new to jQuery and JavaScript, so I'm not too sure what I'm doing, and all my poking around has only further broken code. Thanks!
For reference, this is what my HTML looks like:
<div class="FlightList">
<select id="departureFlightsControl">
<option value="default">No flight chosen</option>
<option value="20121113 17:37:00">November 13, 2012 (5:37pm) - $137.38</option>
<option value="20121119 05:11:00">November 19, 2012 (5:11am) - $121.05</option>
<option value="20121124 19:41:00">November 24, 2012 (7:41pm) - $182.44</option>
<option value="20121208 08:22:00">December 8, 2012 (8:22am) - $140.75</option>
and so on, with more options. Once an option other than default is selected, it is populated down to a "flight information" div, that has a small "X" div "button" to clear it. I want the X button to return it to the list.
You can use this as a base:
<div class="FlightList">
<select id="departureFlightsControl" onchange="addToInfo(this)">
<option value="default" >No flight chosen</option>
<option value="20121113 17:37:00">November 13, 2012 (5:37pm) - $137.38</option>
<option value="20121119 05:11:00">November 19, 2012 (5:11am) - $121.05</option>
<option value="20121124 19:41:00">November 24, 2012 (7:41pm) - $182.44</option>
<option value="20121208 08:22:00">December 8, 2012 (8:22am) - $140.75</option>
</select>
</div>
<div id="flightInformation">
</div>
<script type="text/javascript">
function addToInfo(element){
var opt = $(element).find(":selected").remove();
var span = $('<span style="margin-left:20px;" onclick="addToList(this)"> X</span>');
$(span).data('option', opt);
var div = $('<div>'+$(opt).html()+'</div>')
$(div).append(span);
$('#flightInformation').append(div);
}
function addToList(element){
$('#departureFlightsControl').append($(element).data('option'));
$(element).closest('div').remove();
}
</script>
Here's a solution that caches all the options when page loads. When an option is selected, the options are replaced from the cache with all the options whose values aren't the one just selected
var departSelect=$('#departureFlightsControl');
/* cache clones of all options*/
var departOptions=departSelect.find('option').clone();
departSelect.change(function(){
var currVal=$(this).val();
/* other display logic using currVal*/
/* get new options that don't include current one selected from cache*/
var newOptions= departOptions.clone().filter(function(){
return $(this).val() ! = currVal;
});
$(this).html(newOptions)
})
I came up with script that worked kind of ok, at least in Firefox. The problem with it in Firefox was that the change event doesn't fire until you leave the control (and maybe in other browsers besides), and the click event occurs when you pull out the dropdown, not just when making a selection. This made it very hard to synchronize and to not remove the "no flight chosen" item from the list until after a real flight was chosen.
So, I did a bit of research on the issue and then tried to work up my own jQuery plugin (jsFiddle) for detecting select dropdown and rollup. It gets better, but still doesn't work perfectly in Firefox.
My conclusion is that while you can get such an animal mostly working, you can't get around a few things:
Not all browsers implement things the same way, so they "break" differently.
In Firefox, if you click out of an iframe containing a dropped-down select control, the dropdown rolls up and there is nothing you can do to respond to it. So now the code reports the wrong event for a while until you either leave the control or click elsewhere in the page.
Firefox additionally was throwing script errors in the jQuery library, but not Chrome.
Using keyboard shortcuts such as Alt↓ to pop open the dropdown do not throw mouse events, at least in Firefox. So you'd have to add even more trapping of keyboard events (and are you sure you'll get this right on the Mac? Android? iPhone?)
Note: I am aware that my code in that fiddle is not optimal and has bugs. It is an abandoned prototype, not a finished product. :)
So, may I suggest instead that you use a custom select box, which can do most anything you want it to. Here's one example select box replacement jQuery plugin.
Here's a reasonable solution that keeps it simple and does the job. You can obviously increase the specificity of the function should you desire.
$(document).ready(function () {
var temp = 0;
if ($(':selected')) {
$('select').change(function () {
if (temp !== 0) {
temp.show();
}
temp = $(':selected').hide();
return temp;
});
} else {
('select').show();
}
});

Altering a page from a Greasemonkey script whenever a specific <option> is selected

While writing a GreaseMonkey script using jQuery and "tamtam", a question arose in my mind.
There is a page (which I can't edit) having a <select> element with four options. Option 2 is preselected.
<select id="idname" name="namename">
<option>Option1</option>
<option selected="selected">Option2</option>
<option>Option3</option>
<option>Option4</option>
</select>
Now I'd like to call the script when Option1 is selected. Option1 leads to another site I want to insert my script.
How would I write something like:
if (Option1 is selected) {
perform the script
}
Would something like the following work?
if(document.getElementById("idname").getElementsByTagName("option")[0].onselect == true){
perform the script
}
If not, can you post a reference helping me?
Edit
I was able to create a function and an event handler.
function myCompute(Event) {
with (this) {
var myTest = value;
if (value == "option1") {
$("#tabelle").show();
}
else {
$("#tabelle").hide();
}
}
}
Event handlers:
$("#option-type").change (myCompute);
$("#tabelle").hide();
It works as follows:
By choosing option 2,3 or 4 the table is hidden. By choosing option 1 the table is shown.
By visiting the site option 2 is selected most of the time and nothing is shown.
Now I got the case that option 1 is selected by visiting the site and no table appears, too. My idea was that the table should be shown when option1 is preselected.. I think that an EventHandler is missing.
Like you, Brock Adams, said.
$("#option-type option:first").select (myCompute);
$("#option-type").change (myCompute);
$("#tabelle").hide();
If I bind the function with $("#tabelle").hide();, the table is hidden from the very beginning. By changing the options to option1 the table is shown. How can I show the table when option 1 is selected and how can I hide the table when option 2,3,4 are selected?
Trying option:first results in an "unknown pseudo-element" error.
Update:
Ok, if I understand the revised question, the code now works as intended except when option 1 starts as selected. (PS, the id's of the given code should be edited to match up.)
If that's true, then just change this line:
$("#tabelle").hide();
.
To this:
if ($("#option-type")[0].selectedIndex == 0 )
$("#tabelle").show();
else
$("#tabelle").hide();
In Greasemonkey, you can't set event handlers that way due to sandbox protection. See Common Pitfalls in Greasemonkey.
Also, with jQuery, there are easier ways to select that element.
Something like: $("#idname option:first").select (YourEventHandler) should work.
Where:
function YourEventHandler (Event)
{
//YOUR CODE HERE
//Note: Event is a variable passed automatically to all event handlers, you often just ignore it.
}
Handy jQuery reference.
You can use the .selectedIndex property to get which one's selected, like this:
if(document.getElementById("idname").selectedIndex === 0) { //option 1
//perform the script
}
It's a 0-based index, so 0 is Option 1 in your example markup, you can test/play with it here.
I'm unclear from the question, but if you want this on the change event, it'd look like this:
document.getElementById("idname").onchange = function() {
if(this.selectedIndex === 0) {
//perform the script
}
};
You cant test it here.

Selecting options from a drop down

I'm building a recipe-finder for a new food blog. The design I have basically involves the user selecting ingredients, one at a time, from a drop down <select>, the option disappearing from the list (so they can't select it again) and appearing on another HTML list with a link to remove it from the list. Once they're done, they click a button and that takes them through to a results page.
Here's the select markup as generated by the PHP:
<select>
<option value="">Please select</option>
<option value="beef-mince">Beef mince</option>
<option value="carrots">Carrots</option>
...
</select>
It's not drastically complex but it does raise a few questions on how I'm going to do some of these things. I'm using jquery.
I need to store the selected items in memory so I know what to send to the search page when they've done selecting items. What's the best way of doing that in your opinion as each item has two values (its "real" value and its database-value)?
How do I make "Please select" the selected option after they've selected something (preferable without triggering the onchange event)?
Once I've stored it in memory and added it to the displayed list of things they're searching for, how do I delete that item from the available items? Can I just "hide" or disable it (safely)?
If in #3 I have to delete it from the DOM, when I add it again, can I sort the list (based on either value) and keep the please-select option at the top?
1.) You can append hidden form elements to the page whose value is the value of the selected option.
2.)
jQuery("#select-list")[0].options[0].selected = true // assuming it's the first item
3.) I would remove the element from the DOM using jQuery("#select-list option:selected").remove()
4.) You can use before(). jQuery(your_default_option).before("#select-list option:first");
You can store the 'two values' in a hidden form field as an object in JSON notation. This will make it easy to modify in jQuery as the user interacts with the page.
You will need to use a combination of the onchange, keyup and keydown event to capture possible changes to the form so that you can re-select the 'Please Select' option.
You will need to remove the option from the dom and re-add it later. You can easily do this through jquery through something like this:
$("select option:selected").remove();
You can write a sorting function for the options starting with index 1, and keep the 'Please Select' as the first option.
1)
Basic idea, you need to check to make sure the first is not picked
var selections = [];
var mySel = document.getElementById("mySelectId");
var ind = mySel.selectedIndex;
selections.push( mySel.options[ind].value ); //add to a list for you to remember
mySel.options[ind] = null; //remove
2)
mySel.selectedIndex = 0;
3)
See #1
4) Yes you can add it anywhere you want by using insertBefore
Example here: http://www.pascarello.com/lessons/forms/moveSelectOptions.html
Will leave this answer here but I think I failed to read your whole post, so it might not help much.
You need to give your select a id like this:
<select id="MySelect">
<option value="">Please select</option>
<option value="beef-mince">Beef mince</option>
<option value="carrots">Carrots</option>
...
</select>
And to get it is just something like this:
<?php
$value = $_REQUEST["MySelect"];
echo $value;
?>
Code is not tested and $_REQUEST can be replaced by $_GET or $_POST regarding what you have specified as action on your form. $_REQUEST will eat it all though.

Categories