I have this code:
<input type="checkbox" ng-checked="item.selected == 'yes'" ng-click="change()">{{item.selected}}<br/>
http://jsfiddle.net/NmQXp/2/
As you can see, there are three identical checkboxes, with their checked status binded to the value of some string. The catch is that I want to introduce an intermediate "partially selected" state to the checkboxes. In fact what I want to do is use the checkbox to change the value from "pending" to "ongoing" and then to "done" (I'm making a ToDo list).
If you click one of them several times you can see the correct behavior in the others, but the checked status is wrong in the one you are clicking.
¿Is this a bug in Angular Binding, or am I missing something?
Well i tried to fix your fiddle to take care of the checked issue. The behavior was inconsistent because checkbox gets checked when mouse is clicked over it due to the default HTML behavior. I added these lines to prevent the default behavior for checkbox.
if ($scope.item.selected == 'no') {
$scope.item.selected = 'halfway';
$event.preventDefault();
}
Here is the updated fiddle.
http://jsfiddle.net/cmyworld/J27jN/
Related
In my cypress runner, I can see that it is successfully finding the checkbox, as I can see it has the type="checkbox" attribute. I also noticed that by manually clicking the checkbox, its "value" attribute became "true", so I tried using invoke('attr', 'value', 'true') which also "succeeds" but the text box remains unchecked. Not sure what I am missing. There are no onClick events that I can see that might uncheck it immediately after checking either.
My code:
What it returns:
I guess it may depend on the way that the checkbox is implemented. I had the same issue and the way that I found to solve it was by using .click({force: true}) instead of check({force: true})
I have a gist demonstrating my problem here:
https://gist.run/?id=e2ccd5925e383f2fc36ad277c31bcf23
The checkbox version works fine, if you remove a check, it updates the model right, but if you change the selected radio button, it will keep the true value on the radio that got it's selection removed. If I remove the model.bind="true" value, it will write "on" instead of true. I do want true/false, and not on/off.
I want the object to get it's property updated to be a true or false depending on if it's chosen or not, dynamically. In my scenario I don't know how many radio buttons or checkboxes will need to be selected. I only know that in the cases of it not being a collection, I only want one, and in the case that it is a collection, I want an unknown number selected.
Like the other answer mentions - <input type="checkbox"> behavior is different from <input type="radio">.
I have forked your gist and made the following changes to make your scenario with the radio button work in aurelia:
1) Added a function to the first object in your params collection called selectedChanged(it doesn't have to be there, could be on the viewmodel class instead, perhaps a better place for it). It is responsible for handling selection change in the radio button group, specifically it will iterate over a collection of options (second parameter), and set selected property to true on the option who's id matches the first parameter of the function, and to false on all other options.
2) There is an event on the input called change, I delegate it to a function called selectedChanged mentioned above, passing the option.id value and options as parameters.
https://gist.run/?id=5f38414fde799dfc145fc1291d9f2fd3&sha=f12283b08bfb45e77b8280d46a3709b6ccb82768
I think what you want to achieve (turning individual value on/off) is not achievable with radio, as the way it works (select a single value out of a set) is different with check-box (turning individual value on/off) (MDN reference). So If you truly want to have radio like appearance, with check-box behavior, consider employing some CSS and extra DOM elements to make it look like so, and behave like so.
I am using a Angular md-autocomplete which starts showing users the auto completion options in a drop-down after they first type in the text box. Is there anyway to have this dropdown shown when the user first clicks in the text box as well?
Here is the md-autocomplete html:
<md-autocomplete flex
role="combobox"
md-selected-item="text"
md-no-cache="true"
md-search-text="searchText"
md-search-text-change="searchTextChange(searchText)"
md-items="item in getMatches(searchText)"
md-item-text="item.autocompleteVal"
md-min-length="0"
md-selected-item-change="$parent.selectedItemChange(item)" on-enter ng-cloak>
<span id="autocompleteText" md-highlight-text="searchText" md-highlight-flags="^i">{{item.autocompleteVal}} </span>
</md-autocomplete>
I was having same issue but setting the value of md-min-length to zero starts working
md-min-length="0"
Update:
If it still does not work then make sure that the searchTerm is initially set to null
Controller
vm.searchTerm = null
Html:
md-search-text="vm.searchTerm"
As #Hodglem pointed out, the current Material docs use the valueChanges observable of the FormControl object to detect changes and filter the options to display on the autocomplete panel. They prime the initial value as "null" so that all the available options will display.
However, often your list of options comes from a service (as in my use case) and your list is empty when startsWith(null) runs, especially if this code is in the ngOnInit() method like it is in the Material docs. This method runs immediately, while the service takes time to fill the list.
Autocomplete is smart enough to not open the panel if there are no options, so the initial focus on the element will not open the panel since there are no options to display. Even after the list fills from the service, the filter only gets triggered with a change in value on the control, so until the user starts typing, autocomplete's list of options remains empty, and therefore the panel remains closed.
I have two solutions:
Move the observable set up out of the ngOnInit() method, and into the subscribe() method that follows the call to the service that retrieves the options. After setting the list, run the observable set up. Now the panel will open on focus since the panel has options to display. (Make sure that the initialization of the FormControl remains in the ngOnInit() method, or the binding in the template will throw an error.)
Scratch the observable approach, and bind the filter method to the control's native events, such as (focus) and (input). In the filter method, check if the passed-in value is null or empty, and if it is, return the entire list. This way, the filter gets triggered every time the user clicks on the control or changes its value, and as long as the user clicks on the control at least once after the service filled the list of options, the panel will display. Depending on your use case, I found that by the time the user moves the mouse and focuses on the control, the service already delivered the goods, and the panel opens.
Seems like demo from site works as you expected.
https://material.angularjs.org/latest/demo/autocomplete
For Angular 2 and Material 2, setting startWith() on the valueChanges() observable of the control to null will show all of the values. Omitting this will result in the user first needing to type a value in to see results.
All values displayed on focus:
this.filterFuelTypeObservers.push(newFormGroup.controls.fuelType.valueChanges
.startWith(null)
.map(value => value ? this.fuelTypeFilter(value) : this.fuelTypes));
No values displayed until entry:
this.filterFuelTypeObservers.push(newFormGroup.controls.fuelType.valueChanges
.map(value => value ? this.fuelTypeFilter(value) : this.fuelTypes));
I haven't, but I imagine you could play around with startWith() to set the on focus list to be filtered by something also.
Just add the following line
md-min-length="0"
This will trigger a md-items calls even when just clicked
I'm working on system where I have an ng-repeat populating from an array of elements, with a radio buttons setting a property. When it loads in, none of the radio buttons are selected, but when I select any of the radio buttons, it binds to the model appropriately. It works in a single format without the outer ng-repeat, so I'm not sure why it refuses to select the radio button from the model.
<div ng-repeat="selectedTag in selectedGroup.tags track by $index" ng-controller="ThemesEdit_TagStylesCtrl">
<div class="type-select">
<label ng-repeat="styleGroup in styleGroups.list" ng-hide="styleGroup.name == 'Settings'">
<input type="radio" name="tagType" ng-model="selectedTag.styleGroupId" ng-value="styleGroup.styleGroupId"/> <span>{{styleGroup.name}}</span>
</label>
</div>
<div ng-include src="another_page"></div>
<div class="clear-float"></div>
<p tag-example="selectedTag" data-style-group="styleGroup"></p>
</div>
I can see that the $parent.selectedTag.styleGroupId comes through on each selectedTag, and it triggers the options in the template that is brought in with ng-include, so I know that is pretty close to working properly. The only remaining issue seems to be that it doesn't automatically select a radio button with a defined ng-model.
I'm fairly new to angular, so it could be something completely obvious, but I was hoping someone could light my way. Thank you for any and all help!
Edit: Updated with two suggestions below. Still no joy, but thought I'd edit the code to the most current iteration.
I would say the solution is ng-value="styleGroup.styleGroupId", documentation here.
I feel pretty dumb - it was something simple that I overlooked. With a single instance, publishing with the name set in <input type="radio" name="tagType" ng-model="selectedTag.styleGroupId" ng-value="styleGroup.styleGroupId"/> <span>{{styleGroup.name}}</span>" worked fine. Once I stuffed it in an ng-repeat, it was publishing under the same name="tagType" and overwriting the selection. Sure enough, when I scrolled to the bottom of my page, the last set of radio buttons were checked appropriately.
Checking the docs, the name is optional, and removing it allowed all the radio button sets to populate properly. I haven't seen any ill effects on anything else - is there anything I should be watching for?
Thanks for the help/thoughts, everyone!
I think you should use ng-model="selectedTag.styleGroupId". selectedTag shouldn't be overwritten by your inner ng-repeat.
UPDATE:
Have a look at this SO answer ng-value needs to be set true.
I reference the document here: ng-checked
Here, at the example provided at the bottom of the page, you can see that clicking on the first checkbox also checks the second box.
The behaviour that I find with this is that even when the first checkbox is checked, the second checkbox can be unchecked - that is, after checking the first checkbox (the Master checkbox), the second checkbox gets checked automatically. If you now try to uncheck the second checkbox, it works which I find strange. The ng-checked directive is bound to the master model which is still true.
Why then is it possible to uncheck the second box? Shouldn't the ng-checked ensure that the checked state is always in sync with the expression - that is, shouldn't the ng-checked NOT allow the second checkbox to be unchecked as long as the first checkbox is still checked?
How does one manage to keep the second box checked as long as the master checkbox is checked?
Reference Code:
Check me to check both: <input type="checkbox" ng-model="master">
<br/>
<!-- Expecting the following input to be checked as long as the master checkbox
is checked / User should not be able to uncheck this as long as master
is checked -->
<input id="checkSlave" type="checkbox" ng-checked="master">
The ng-checked directive likely (I didn't examine the Angular source code) sets up a $watch on the master property. Only when that property changes will the $watch fire. So when you change the slave checkbox, the $watch doesn't fire, so the change happens.
If you want to keep the second checkbox in sync with the first, add an ng-model to the slave,
<input type="checkbox" ng-model="slave" ng-checked="master">
then force a sync anytime either model changes:
$scope.$watch(
function() { return $scope.master + $scope.slave; }
,function() { $scope.slave = $scope.master; }
);
Fiddle
Note that this will also work:
$scope.$watch('master + slave', function() {
$scope.slave = $scope.master;
});