Angular JS: Add and remove terms from checkboxes in ng-repeat? - javascript

I have a miller column constructed in Angular and Bootstrap.
http://codepen.io/smlombardi/pen/WGwGbY
In the second column, clicking the word (link) opens the third column, but I need to have the checkbox add that word to an array of search terms.
If the checkbox is UN-checked, I need to remove that word from the array of search terms. As you can see in the pen, I have the adding part working, but un-checking the box adds the word again.
I realize what I need to do is somehow check the state of the checkbox and if it's true add the word and if it's false check the array for the word (string) and pop it out of the array.
I can't figure out how to check only the checkbox that was clicked.
<div class="col-xs-3 inner-column">
<div class="panel panel-default">
<div class="list-group">
<div class="list-group-item" ng-class="{active: $index === pmt.millercolumn.level1Selected }" ng-repeat="level1 in pmt.millercolumn.level1 track by $index">
<input type="checkbox" ng-model="activeSearchTerm" ng-change="pmt.change($index)" id="ng-change-example1" />
<a href="" ng-click="pmt.getSublevel2($index)" >
{{level1.name}}
<i class="pull-right fa fa-angle-right fa-lg"></i>
</a>
</div>
</div>
</div>
the ng-change on the checkbox calls:
_this.change = function (index) {
var searchTerm = _this.millercolumn.level1[index].name;
_this.searchTerms.push(searchTerm);
};

It looks like you're thinking in a jquery mindset where you need to handle events when something changes. An easier way would be to make each checkbox correspond to an item in the array, so the ng-model would be something like level1.isSelected. Then, to construct your search terms array, use scope.$watch and pass true as the 3rd argument to deep watch your array of items. When a checkbox is checked, your watch will be called and you can reconstruct the search terms array by plucking the terms of the list items that are selected.

Add this code in place of your _change function it works for sure
_this.change = function (index) {
console.log('Clicked on', _this.millercolumn.level1[index].name);
var searchTerm = _this.millercolumn.level1[index].name;
var searchIndex = _this.searchTerms.indexOf(searchTerm);
if (searchIndex == -1) { // If new push
_this.searchTerms.push(searchTerm);
}
else { // Else remove item
_this.searchTerms.splice(searchIndex, 1);
}
console.log(_this.searchTerms);
};
Working codepen demo : http://codepen.io/krishcdbry/pen/EgKgBv

You're running the same code no matter if the checkbox is checked or not. Try something like this:
_this.change = function (index, checked) {
var searchTerm = _this.millercolumn.level1[index].name;
if(checked){
_this.searchTerms.push(searchTerm);
}
if(!checked){
_this.searchTerms.splice(searchTerm);
}
};

FWIW, this is what I did, which works:
<input type="checkbox" ng-model="level1.isSelected" ng-change="pmt.change($index, level1)" id="mycb" />
_this.change = function (index, item) {
if (item.isSelected) {
_this.searchTerms.push(item.name);
} else {
var termToRemove = item.name;
for (var i = _this.searchTerms.length - 1; i >= 0; i--) {
if (_this.searchTerms[i] === termToRemove) {
_this.searchTerms.splice(i, 1);
}
}
}
};

Related

What reasons are there, why disabled fields in an HTML form still get transmitted?

So I have this fix HTML structure that I cannot influence. The structure is provided by a tool, where you add selection/input/etc fields via drag and drop.
E.g. a regular textfield gets this HTML code:
<div id="d_001" class="field text ">
<label for="f_001" id="l_001">Shortdescription</label>
<input id="f_001" name="id1" value="" />
</div>
And a textarea would get this code:
<div id="d_002" class="field area ">
<label for="f_002" id="l_002">Description</label>
<textarea id="f_002" name="id2" cols="45" rows="5" ></textarea>
</div>
I also have a selection list, that based on the selected item shows/hides further information fields. Let's say for example the selection list contains different clothes. When you select "pants", two input fields "length" and "width" are made visible. When you select "bra", two input fields "cup size" and "underbust" are made visible.
So here's the thing:
When selecting "pants", it would currently submit the following information to the system:
Short Descritption: Pants
Description: I would like to by black skinny jeans
Length: 32
Width: 32
Cup Size:
Underbust:
From googling a little I found out that disabling the non-selected fields should do the trick. Yet it doesn't. It clearly disables the fields, as they're grey and not editable, but they still get transmitted when sending the form.
Based on the HTML snippets above I tried disabling via
document.getElementsByTagName('input').disabled = true;
--> which disables all "input" input fields, but still submits the label with no content. Even though I am not sure if it doesn't submit the content or if it still submits the empty content.
(yes, this would leave out selection lists, textareas, etc. but this is the secondary issue, solvable by doing the same for all tag types)
Hence I thought maybe I need to disable all elements within the div item, meaning I'd need to also "disable" the labels via
documents.getElementsByTagName('label').disabled = true;
I am not too surprised it doesn't work. But as I can't disable div items as a whole I really don't know what I am missing. Is there anything else that forces the fields to be submitted?
I am very grateful for any hints that push me in the right direction.
Edit:
As per request I want to add the exact loop that disables the mentioned input fields:
// list with links to make the items clickable
<ul id="myUL">
<li><a id="li1" title="pants" href="javascript:void(0)" onclick="returncontent(this);showpants();">Pants</a></li>
<li><a id="li2" title="bra" href="javascript:void(0)" onclick="returncontent(this);showbra();">Bra></li>
<li><a id="li3" title="tshirt" href="javascript:void(0)" onclick="returncontent(this);showtshirt();">T-Shirt</a></li>
<li><a id="li4" title="socks" href="javascript:void(0)" onclick="returncontent(this);showsocks();">Socks</a></li>
</ul>
// a var array containing the available class attributes
var elemente = ["pants","bra","tshirt","socks"];
// defining the functions triggered by the list
function showpants(){
howtocode(0);
}
function showbra(){
howtocode(1);
}
function showtshirt(){
howtocode(2);
wosOSD();
}
function showsocks(){
howtocode(3);
// have the function do different things depending on the selected list item
function howtocode(value){
var arrayLength = elemente.length;
for (var i = 0; i < arrayLength; i++) {
var classEle = document.getElementsByClassName(elemente[i]);
if (elemente[i] === elemente[value]){
changeStyle(elemente[i],classEle,"block");
setRequired(elemente[i],classEle);
}
else{
changeStyle(elemente[i],classEle,"block");
disableFields(elemente[i],classEle);
emptyFields(elemente[i],classEle);
}
}
}
function disableFields(ele, classEle){
for (var n = 0; n < classEle.length; n++) {
var inputfields3 = classEle[n].getElementsByTagName('input');
var textfield3 = classEle[n].getElementsByTagName('textarea');
for (var o = 0; o < inputfields3.length; o++){
inputfields3[o].disabled = true;
}
for (var p = 0; p < textfield3.length; p++){
textfield3[p].disabled = true;
}
}
}
and the other functions do the following:
- changeStyle() [sets the div item to "block" or "none"
- setRequired() [is self explainatory I hope]
- emptyFields() [resets all input fields if the selected item gets changed]
I am aware that it currently doesn't work. I am working on providing a fiddle.
Edit 2:
So I found the following code somewhere in the system JS/jQuery files. Could this have something to do with it? So far I haven't understood yet what it does.
var data = elements.inject({ }, function(result, element) {
if (!element.disabled && element.name) {
key = element.name; value = $(element).getValue();
if (value != null && (element.type != 'submit' || (!submitted &&
submit !== false && (!submit || key == submit) && (submitted = true)))) {
if (key in result) {
// a key is already present; construct an array of values
if (!Object.isArray(result[key])) result[key] = [result[key]];
result[key].push(value);
}
else result[key] = value;
}
}
return result;
});
Take a look at this question, How to disable an input type=text?
Instead of setting disabled = true you need to set disabled = "disabled".

ng-checked in a radio button in AngularJS

In my angular application, I am looping through a collection and displaying the records with input type="radio".
<tr ng-repeat="account in vm.pagedAccounts.items"
ng-class="{ 'highlight': (account.rowIsSelected) }"
<td>
<input
ng-model="account.rowIsSelected"
value="{{account}}"
name="selectedAccount"
ng-checked="account.rowIsSelected"
ng-change="vm.selectAccount(account)"
type="radio">
</td>
In my controller, I first set rowIsSelected property to false for all the accounts.
response.data.results.forEach(function(account) {
account.rowIsSelected = false;
});
So, I just make sure whenever account.rowIsSelected is set to something, make that checked.
This is working fine.
But, in the selectAccount function, if a different account is clicked, I want to remove the previous all highlights and highlight the current one.
vm.selectAccount = function (account) {
if (account.rowIsSelected) {
//First set all false
vm.pagedAccounts.items.forEach(function(account) {
account.rowIsSelected = false;
});
var selectedAccount = vm.pagedAccounts.items
.filter(function(x){
return x.id=== account.id;
});
//Then set only that accounts property to true
selectedAccount[0].rowIsSelected = true;
}
But if I click the same row twice, it is no longer checked. I want to keep it checked and highlighted.
How to do it?
Does whatever I am doing seem right?
Please help.
Try this.
vm.selectAccount = function (checkedItem) {
var selectedAccount;
vm.pagedAccounts.items.forEach(function(account) {
if(checkedItem.id == account.id){
selectedAccount = account;
account.rowIsSelected = true;
}else{
account.rowIsSelected = false;
}
});
//Then set only that accounts property to true
selectedAccount[0].rowIsSelected = true;
}
I think you should organize your radio buttons in a bit different way, like it recommends angularjs
Something like:
<tr ng-repeat="account in vm.pagedAccounts.items"
ng-class="{ 'highlight': vm.pagedAccounts.selected === account }">
<td>
<input
ng-model="vm.pagedAccounts.selected"
ng-value="account"
type="radio">
</td>
...
And radio buttons should be automatically selected, when ng-model values is equal to ng-value, so you don't need any specific logic or ng-checked, etc.

Remove deselected checkbox value from Array using Angular

This is simple one but i still somehow couldn't get it to work.
I have default value checked, checkbox. So when edit, of course the default value is chosen, but later I want to remove the default value and choose another value. Here it is
array1=[] //empty
After I check a checkbox, it will inserted to this array
array1=["sun"]
If I select 3 values (array1["sun","stars","moon"])
but I deselect the first selection (the default selection), it will be still in the array. (array1["sun","stars","moon"]) but I the expected result is this:
array1["stars","moon"]
No more first selection. So how to remove deselected value from array using Angular/Javascript?
I have tried use splice, remove and set
Same thing developed and used in project :
Template side :
<label *ngFor="let hobby of hobbies" #checkbox class="mdl-checkbox mdl-js-checkbox">
<input type="checkbox" name="hobbies" [value]="hobby"
(change)="populateMyHobbies(hobby,$event.target.checked)" class="mdl-checkbox__input">
<span class="mdl-checkbox__label">{{hobby}}</span>
</label>
Component Side :
selectedHobbies = [];
populateMyHobbies(value , status:boolean)
{
if(this.selectedHobbies.indexOf(value) === -1 && status)
{
this.selectedHobbies.push(value);
}
else if(!status)
{
let index = this.selectedHobbies.indexOf(value);
this.selectedHobbies.splice(index, 1);
}
}
Here selectedHobbies will give you what you want.
Have to change just name as per your app.
i used it once in my project. change the code according to your need. logic is same.
html part
<input type="checkbox" value="{{category._id}}" (change)="pushpopcategory(category._id)" id="{{category._id}}">
component code
pushpopcategory(value) {
if ((<HTMLInputElement>document.getElementById(value)).checked) {
this.categoryAdd.push(value);
} else {
let indexx = this.categoryAdd.indexOf(value);
this.categoryAdd.splice(indexx, 1);
}
}

Splice removing wrong object from ng-repeat in AngularJS

I'm listing an array of names in my view like this:
<div class="checkbox col-md-3" ng-repeat="staff in stafflist | orderBy: 'name'">
<div class="checkboxinner">
<button class="btn btn-staff form-control"
ng-show="!staff.chosen"
ng-click="pushStaff(staff)">
{{staff.name}}
</button> // visible when unselected, invisible when selected
<button class="btn btn-primary form-control"
ng-show="staff.chosen"
ng-click="unpushStaff(staff, $index)">
{{staff.name}}
</button> // visible when selected, invisible when unselected
</div>
</div>
The first button triggers this function, adding the object into the array and being replaced with another button (different color, same content) that is supposed to act as a toggle. This function works perfectly.
$scope.paxlist = [];
$scope.pushStaff = function (staff) {
staff.chosen = true;
$scope.paxlist.push(
{
name: staff.name
}
);
console.log($scope.paxlist);
};
Basically, when I click I add the object, when I click again, I remove it. Here's the remove function:
$scope.unpushStaff = function (staff, $index) {
staff.chosen = false;
var index=$scope.paxlist.indexOf(staff)
$scope.paxlist.splice(index,1);
console.log($scope.paxlist);
}
My problem is that the unpushStaff() will indeed remove an item, but not the item I clicked to remove, but another one.
What am I missing?
Maybe the ng-show is messing with the $index?
Your staff entry in stafflist and the entry in paxlist are not identical. Based on your template below:
<button class="btn btn-staff form-control"
ng-show="!staff.chosen"
ng-click="pushStaff(staff)">
{{staff.name}}
</button> // visible when unselected, invisible when selected
It is clear that each staff entry in stafflist is some sort of object that has at least one attribute name and another chosen.
When you push onto paxlist, you are creating a new object that looks like:
$scope.paxlist.push(
{
name: staff.name
}
);
This is fine. But when you then come to remove it, you are looking for it by:
var index=$scope.paxlist.indexOf(staff)
where staff is the object in stafflist! Of course, that object does not exist in paxlist - a separate object you derived above in paxlist.push() is - and so indexOf() is returning -1, leading splice() to remove the last item on paxlist.

Dynamically add filter options in angularjs

Hello I have a small project in which I want to have perform search from multiple dynamically added text fields.
This is how I add the search fields:
<div class="form-group" ng-repeat="choice in choices">
<button ng-show="showAddChoice(choice)" ng-click="addNewChoice()">Add another choice</button>
<input type="text" ng-model="choice.name" name="" placeholder="Search criteria">
</div>
And later I have a table with ng-repeat and here is that part:
<tr ng-repeat="todo in todos | filter: {filter from all fields}">
.......
</tr>
What I want to do is to have the contents filtered with all dynamically added search fields.
You'll have to create your own filter to handle that. I've gone ahead and gotten you started.
$scope.myFilter = function(input){
for(var key in input){
for(var x = 0; x < $scope.choices.length; x++){
if(input[key] == $scope.choices[x].name){
return true;
}
}
}
return false;
}
Here is the jsFiddle of the output: http://jsfiddle.net/wsPrv/
Rather than using the filter, do the filtering in the controller yourself. Here is the updated fiddle with the solution. In the first textbox, replace choice1 with "some" and you will see the todo with text "Some stuff" being displayed.
See the relevant part below. For details, see the fiddle.
$scope.$watch('choices', function(newValue) {
$scope.DisplayedTodos = [];
// Filter items here and push to DisplayedTodos. Use DisplayedTodos to display todos
}, true);

Categories