Angular2 - reset the value of a select after user changes it - javascript

I have a <select> which the user can change. Initially it has a value and when the user changes it I must prompt her "are you sure"? and in case the answer is NO then change back the <select>'s selected value to the previous one. The <select> is bound to a collection of objects, not values.
The best I could come up with so far is this:
in the html
<select [ngModel]="selectedObj" (ngModelChange)="onSelectedObjChanged($event)">
<option *ngFor="let obj of availableObjs" [ngValue]="obj">{{whatever}}<option>
</select>
in the code
onSelectedObjChanged(obj) {
if (prompt answer is no) {
let currentlySelectedObj = this.selectedObj;
this.selectedObj = null;
this.changeDetectorRef.detectChanges();
this.selectedObj = currentlySelectedObj;
this.changeDetectorRef.detectChanges();
}
}
This works, but is ugly. Why do I do it:
there seems to be no way to cancel the selection changed event in the DOM
when onSelectedObjChanged is called and the answer is "no", I need to somehow let angular know it has to refresh the binding target, i.e. the <select>...
...however I can't simply set this.selectedObj = this.selectedObj as the value doesn't change and there no change detected by angular; that's why I set it to null and back...
...however even that is not enough, I need to invoke changeDetectorRef.detectChanges() to notify angular of it
I'm sure there is a better and easier way to do this, it would be great if you could share some ideas.
Thanks!

Here's how I did it:
HTML:
<select [(ngModel)]="option"
#selectModel="ngModel"
(ngModelChange)="optionChanged($event)">
<option [ngValue]="null">PlaceHolder</option>
<option *ngFor="let o of options" [ngValue]="o">...</option>
</select>
Component:
#ViewChild('selectModel') private selectModel: NgModel;
optionChanged($event) {
//logic
this.selectModel.reset(null);
}

I use $event.target.value instead of $event. I send this.selectedObj = $event.target.value; and if failed change this.selectedObj = previousObject I don't need any change detection.

try this.selectedObj = obj.target.value; and than perform your condition.

Related

Vue.js - select element not updating when using :value, v-model is fine

I have two <select> elements. When I select a value in the first, I want to default the value for the second. It works with v-model, but not with :value.
One is a list of vehicles.
<select :value="vehicle" #change="setVehicle($event.target.value)">
<option value="CAR">Car</option>
<option value="PLANE">Plane</option>
</select>
The other is a list of parts. The WINGS part is only visible if PLANE is selected.
<select :value="part" #change="setPart($event.target.value)">
<option value="ENGINE">Engine</option>
<option value="WINDOWS">Wheels</option>
<option v-if="vehicle === 'PLANE'" value="WINGS">Wings</option>
</select>
The setter for vehicle is trivial, but the default part for a plane is the conditionally rendered WINGS.
setVehicle: function(newVehicle) {
this.vehicle = newVehicle;
if (this.vehicle === "PLANE") {
this.part = "WINGS";
} else {
this.part = "ENGINE";
}
}
The reactive part data is set to the correct value, e.g. you can display it with {{ part }}, but the <select> element is not properly updated. If I change the part's select to use v-model it works fine:
<select v-model="part">
Using v-model is not an acceptable workaround because in my real world app the part is readonly and can only be mutated with a setter (using a store-like architecture). Another workaround is using a setTimeout(.., 1) in the setVehicle setter. I really do not like that solution. What I really would like to know is why v-model behaves differently than :value/#change when the documentation suggest v-model is simply syntactic sugar.
Demo: https://codesandbox.io/s/eager-liskov-2yor4?file=/src/App.vue
Since the third option is not in the DOM yet when you are updating this.part, it is not selected at that time. So you have two option:
1) Use nextTick() to wait for component update, then set the new value:
setVehicle: function(newVehicle) {
this.vehicle = newVehicle;
this.$nextTick(()=>{
if (this.vehicle === "PLANE") {
this.part = "WINGS";
} else {
this.part = "ENGINE";
}
})
}
or,
2) Keep the third option rendered, but hide it when the vehicle is car. In such way it will get selected, at the moment of changing the vehicle, and showed later when the DOM will be updated. You will do it with v-show instead of v-if.
<select :value="part" #change="setPart($event.target.value)">
<option value="ENGINE">Engine</option>
<option value="WINDOWS">Wheels</option>
<option v-show="vehicle === 'PLANE'" value="WINGS">Wings</option>
</select>
Here's my solution.
Basically you shouldn't be afraid of v-model. Copy the values into the component itself and use them as you please.
But I agree. :value is behaving weirdly, even if using .sync prop. (I suspect it's because of setting property vehicle to PLANE and v-if renders right after setting part WINGS. But not quite sure.)
My solution
Found an alternative workaround that may resolve similar issues:
Bind the <select> element using a computed property that simply returns the reactive part property.
<select v-model="partComputed" #change="setPart($event.target.value)">
computed: {
partComputed: function () {
return this.part;
}
}
The question of why v-model behaves different than :value/#change combo is still open if anyone has insight into that. I tried Googling it, but the closest match was my own bug report I filed several years ago that I completely forgot about! I may see if this behavior is the same in Vue 3.

AngularJS 1.x: perform code while in data-ng-repeat...?

I have the following code snippet:
<select ng-model="option.optionValue">
<option data-ng-repeat="value in attribute.catalogue.values"
ng-init="option.optionName = value.name"
value="{{value.value}}">{{value.name}}</option>
</select>
The idea was to assign an option.optionName a value of value.name when the selection from the option list is made. This (not a surprise) doesn't work as needed, as it just assigns one value when it runs the code and it doesn't change when any selection is made.
Is there a way to overcome this situation? Maybe add this option.optionName = value.name code in data-ng-repeat?
Thanks!
use ng-options to iterate and initialize the options, and update the alternate variable you want to update in selection (though I don't what is the purpose of that when you already have tat value in the ng-model variable)
<select
ng-model="option.optionValue"
ng-change="option.optionName = option.optionValue.name"
ng-options="value.name for value in attribute.catalogue.values">
</select>

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

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!!)

select onchange same value

Is there a way to trigger an event if I open a dropdown select, and select the same value WITHOUT clicking away to switch focus?
I tried
onblur, but it only works if I make an extra click outside the select.
onchange, but it only works if I select a different value.
I need to trigger an event simply if I open the dropdown and select anything, regardless of whether it is the same or different.
<select name='show1' id='show1' onblur='dosomething(this);'>
<option value='1'>One</option>
<option value='2'>Two</option>
</select>
I think you'd have to use the onClick-event and store the previously selected item in a variable.
Here's a quick example which I think could use some refactoring :-)
var dropboxOpen = false,
oldItem = null;
function onClick(e) {
if (dropboxOpen) {
if (oldItem && oldItem === e.target.value) {
console.log('same selected');
}
oldItem = e.target.value;
}
dropboxOpen = !dropboxOpen;
}
http://jsfiddle.net/vtHPV/
It doesn't matter what library or framework you use, but this is quite simple. You are essentially looking for two things to be true:
Test that the control was clicked.
Test that a value (any value) has been selected, --or-- is still selected.
Number 2 can be achieved with something like...
var elem = document.getElementById("ddlViewBy");
var value = elem.options[elem.selectedIndex].value;
It's then a simple matter to test that it is non-empty.
If you use jQuery
You can try focus() event.

Update field with jquery don't update observable

I want to say hello to the stackoverflow community.
I've just started using knockout a few days ago.
Right know I'm using it to make a dynamic menu builder for a CMS I'm working on.
Here is the code: http://jsfiddle.net/dnlgmzddr/HcRqn/
The problem is that when I choose an element from the select box, the input field update as I expect, but the observable doesn't reflect the change. Because of that, the add button is not enabled.
What am I missing? How can I fix it?
Thank you.
When you populate the url field, you would need to trigger the change event to get the observable to be upated. So, you could do:
$("#url").val('/pages/' + id).change();
Another option though that is more in the Knockout spirit is to use a binding on your select. In this case, you would likely want to populate an observable with that value, then use a manual subscription to default the formatted value into the input field.
this.itemUrl = ko.observable();
this.selectedUrl = ko.observable();
this.selectedUrl.subscribe(function(newValue) {
if (newValue) {
this.itemUrl("/pages/" + newValue);
}
}, this);
Then, bind your select to selectedUrl:
<select id="pagedList" data-bind="value: selectedUrl">
<option value=""><option>
<option value="test">Test</option>
</select>
Here is a sample: http://jsfiddle.net/rniemeyer/HcRqn/21/
You could also eliminate the extra observable and manual subscription if the "value" of your options was the url.
I can't see anywhere in your code where you are actually enabling the button when a field is selected. So I might be missing something, but just enable the button on change. Like the following:
function LoadMenu() {
$("#pagedList").change(function () {
var id = $(this).val();
$("#url").val('/pages/' + id);
// remove the disabled attribute here
$('button.space').removeAttr('disabled');
});
}

Categories