Bind click and change checkbox angular2 - javascript

I have a strange problem with checkboxes using angular2 (beta 17).
I'm trying to set same checkboxes as elements has the array and bind the change or click event.
For example I have an array:
myObjects=[
{value:true},
{value:true},
{value:false},
{value:true}
];
And I'm using the ngFor to create all the checkboxes:
<div *ngFor="let myObject of myObjects; let i = index" >
<input
type="checkbox"
[checked]="myObject.value"
(change)="updateCheckedOptions(i, $event)"
/>
</div>
And finally I have a function in my component:
#Input() myObjects;
updateCheckedOptions(index,event){
this.myObjects[index].value=event.target.checked;
}
But the problem is that the checkbox don't change, I mean that when I click the checkbox, if the initial state is checked, always remain checked and vice versa.
I have tried using (click), (change), [(ngModel)] but it does not work any.
Thanks!

You can check ngModelChange:
//This is your component
#Input() myObjects;
myObjects=[
{value:true},
{value:true},
{value:false},
{value:true}
];
updateCheckedOptions(index,event){
this.myObjects[index].value=event.target.checked;
console.log(index + " ---- " + event);
}
<div *ngFor="let myObject of myObjects; let i = index" >
<input
type="checkbox"
[checked]="myObject.value"
(ngModelChange)="updateCheckedOptions(i, $event)"
/>
</div>

Related

Remove the selected option from select box

I am making angular application with angular form.
Here i have given a form with input fields first name and last name which will always showing..
After that i am having children which will be displayed upon clicking the add button and the children will get removed on click remove button.
As of now everything works fine.
Here i am making patching of data to the inputs on click option from select box.. The neccessary inputs gets patched..
HTML:
<div>
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<div *ngFor="let question of questions" class="form-row">
<ng-container *ngIf="question.children">
<div [formArrayName]="question.key">
<div *ngFor="let item of form.get(question.key).controls; let i=index" [formGroupName]="i">
<div *ngFor="let item of question.children">
<app-question [question]="item" [form]="form.get(question.key).at(i)"></app-question>
</div>
</div>
<select multiple (change)="changeEvent($event)">
<option *ngFor="let opt of persons" [value]="opt.key">{{opt.value}}</option>
</select>
</div>
</ng-container>
<ng-container *ngIf="!question.children">
<app-question [question]="question" [form]="form"></app-question>
</ng-container>
</div>
<div class="form-row">
<!-- <button type="submit" [disabled]="!form.valid">Save</button> -->
</div>
</form> <br>
<!-- Need to have add and remove button.. <br><br> -->
<button (click)="addControls('myArray')"> Add </button>
<button (click)="removeControls('myArray')"> Remove </button><br/><br/>
<pre>
{{form?.value|json}}
</pre>
</div>
TS:
changeEvent(e) {
if (e.target.value == 1) {
let personOneChild = [
{ property_name : "Property one" },
{ property_name : "Property two" },
]
for (let i = 0; i < personOneChild.length; i++) {
this.addControls('myArray')
}
this.form.patchValue({
'myArray': personOneChild
});
}
if (e.target.value == 2) {
let personTwoChild = [
{ property_name : "Property three" },
{ property_name : "Property four" },
{ property_name : "Property five" },
]
for (let i = 0; i < personTwoChild.length; i++) {
this.addControls('myArray')
}
this.form.patchValue({
'myArray': personTwoChild
});
}
}
addControls(control: string) {
let question: any = this.questions.find(q => q.key == control);
let children = question ? question.children : null;
if (children)
(this.form.get(control) as FormArray).push(this.qcs.toFormGroup(children))
}
removeControls(control: string) {
let array = this.form.get(control) as FormArray;
array.removeAt(array.length - 1);
}
Clear working stackblitz: https://stackblitz.com/edit/angular-x4a5b6-fnclvf
You can work around in the above link that if you select the person one option then the value named property one and property two gets binded to the inputs and in select box the property one is highlighted as selected..
The thing i am in need is actually from here,
I am having a remove button, you can see in demo.. If i click the remove button, one at last will be got removed and again click the last gets removed..
Here i am having two property one and two, if i remove both the inputs with remove button, the the highlighted value person one in select box needs to get not highlighted.
This is actually my requirement.. If i remove either one property then it should be still in highlighted state.. Whereas completely removing the both properties it should not be highlighted..
Hope you got my point of explanation.. If any needed i am ready to provide.
Note: I use ng-select for it as i am unable implement that library, i am making it with html 5 select box.. In ng-select library it will be like adding and removing the option.. Any solution with ng-select library also appreciable..
Kindly help me to achieve the result please..
Real time i am having in application like this:
Selected three templates and each has one property with one,two,three respectively:
If choose a dropdown then the property values for the respective will get added as children.
Here you can see i have deleted the property name three for which the parent is template three and the template three still shows in select box even though i removed its children
Firstly, get a reference to the select, like so:
HTML:
<select multiple (change)="changeEvent($event)" #mySelect>
<option *ngFor="let opt of persons" [value]="opt.key">{{opt.value}}</option>
</select>
TS:
import { ViewChild } from '#angular/core';
// ...
#ViewChild('mySelect') select;
Then, in your remove function, check if all elements have been removed, and if they have, set the value of the select to null
if (array.length === 0) {
this.select.nativeElement.value = null;
}
Here is a fork of the StackBlitz

Hide other elements in list

I have the below code:
<li *ngFor="let item of Array let i = index">
<span>
<label (dblclick)="editTag($event,i)">
{{item.tag}}
</label>
<input type="text" #tagInput />
</span>
</li>
The code is in a for loop. When I click on a label, all labels should be hidden and the input should be visible. Currently, when I click on each label, the other remain open. How do I hide the other span when clicking on any item?
I have below code in .ts
#ViewChild('tagInput') tagNameTextInput: ElementRef;
editTag(event: any,index: any) {
//console.info(event);
this.tagNameTextInput.nativeElement.hidden = true;
this.tagNameTextInput.nativeElement.previousElementSibling.hidden = false;
let initialValue = event.target.childNodes[0].nodeValue.trim();
event.target.hidden = true;
event.target.nextElementSibling.hidden = false;
event.target.nextElementSibling.value = initialValue;
console.log(index);
// this.checkListNameHidden = true;
// this.checkListNameTextInput.nativeElement.value = initialValue;
// this.checkListNameTextInput.nativeElement.focus();
event.stopPropagation();
}
How to solve this?
You have multiple children, So you need to use #ViewChildren instead of #ViewChild.
Also in your ngFor loop you do not have unique template reference #tagInput. Use QueryList with ElementRef.
Try : #ViewChildren('tagInput') tagNameTextInput: QueryList<ElementRef>;
instead of
#ViewChild('tagInput') tagNameTextInput: ElementRef;.
Import QueryList from #angular/core.
Like this import { Component, QueryList } from '#angular/core';
the best aproach is add a new property to "item", (e.g. called "editing") so
<li *ngFor="let item of Array let i = index">
<span>
<label *ngIf="!item.editing" (dblclick)="item.editing=true;">
{{item.tag}}
</label>
<input *ngIf="item.editing" [(ngModel)]="item.tag" type="text" (blur)="item.editing=false" />
</span>
</li>
See several things:
1.-in a click of label, the variable becomes true, so the inpĆ¹t is showed
2.-in blur of item, the variable becomes false, so the label is showed
3.-Use [(ngModel)] to relation between the input and the value

ngFor inputs copy values of eachother

I'm using Angular5 and would like to create a form where user can insert new records with a button. Each record has many controls in it and should be changed independently from other records. I have done this many times but now I'm getting weird results.
<form #newRequestForm="ngForm">
<a class="list-group-item" *ngFor="let detail of currentRequest.details; let index$ = index">
.
.
.
<ng-select [items]="products" [searchFn]="searchProduct" (change)="productChanged($event, detail)">
<ng-template ng-label-tmp let-item="item">
{{item.code}} - {{item.name1}}
</ng-template>
</ng-select>
<input class="form-control" name="productname1" type="text" [ngModel]="detail.product.name1" />
<input class="form-control" name="productname2" type="text" [ngModel]="detail.product.name2" />
<input class="form-control" name="productname3" type="text" [ngModel]="detail.product.name3" />
<input class="form-control" type="text" name="description" [(ngModel)]="detail.description" />
The problem is that the method called by ng-select onchange productChanged sets the product names of the current selected product. (Products has 3 seperate name fields in erp.) And when this happens all 3 name fields of all records in the form changes to the name fields of the currently selected product. No matter which ng-select I use, all has changed. All the other fields working seperately eg: description.
So I suppose the bug is in the method but it looks like this:
productChanged($event, detail) {
detail.product = $event;
console.log('-----------------------------------------');
this.currentRequest.details.forEach((d, i) => {
console.log(i, d.product !== null ? d.product.name1 : '');
});
}
Yes, I have tried to debug with the good old console.log and it says that the content of each of the detail.products are perfect, according to the last selection of that record's ng-select.
The input controls are still rewritten by whatever select I make. Why?
Because you didn't specify a track by function, so Angular doesn't really know how to keep track of your inputs.
<a class="list-group-item" *ngFor="let detail of currentRequest.details; let index$ = index; trackBy: customTB">
customTB(index, item) { return index + '-' + item.product.name1; }

ng-change event firing only once inside ng-repeat

Here in my form, I am using two radio buttons and would like to check the selected values. But this does not work as expected. I would like to determine the individual radio selection value and display Big/Small in the Result column
<div ng-controller="CityCtrl">
<table>
<tr>
<th>Name</th>
<th>Big</th>
<th>Small</th>
<th>Result</th>
</tr>
<tr ng-repeat="city in cities">
<td>{{ city.name }}</td>
<td>
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="big" value="big_value" ng-change="check(city.name, big)">
</td>
<td>
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="small" value="small_value" ng-change="check(city.name, small)">
</td>
<td>***</td>
</tr>
</table>
</div>
Controller:
var myApp = angular.module('cityApp', []);
function CityCtrl($scope) {
$scope.big = '';
$scope.small = '';
$scope.cities = [{
id: 1,
name: 'Tokyo'
}, {
id: 2,
name: 'Guangzhou'
}, {
id: 3,
name: 'Seoul'
}];
$scope.check = function(city, value) {
console.log("City->" + city + " ::: Value->" + value);
};
}
From other answers I did infer a few things:
Understand Prototypical Inheritance in JS ?!
ng-repeat creates child scope that affects these radios inside ng-repeat
use $parent on ng-modal in the radio
I did played around, but couldn't make this work, I guess I am making some obvious mistake here.
Here is the fiddle:
http://jsfiddle.net/mw2us37h/
NOTE:
I am not using ng-value but passing a hardcoded value.
Changing to ng-click doesnt work either
Flagged this as a duplicate, but it seems like the solution is to use ng-click instead of ng-change when working with radio buttons in angularjs.
Here is an edit of your fiddle that works: http://jsfiddle.net/mw2us37h/1/
I'm not sure I understand what your trying. I think you like to display the radio button value in the result column and/or have a function on click where the value is passed.
Display the radio button value in result column
I would change the ngModel value so it binds to something city specific. So change:
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="big" value="big_value">
to:
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="city.size" value="big_value">
and
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="small" value="small_value">
to
<input type="radio" name="{{city.name}}-{{$index}}" ng-model="city.size" value="small_value">
So the model points to the same variable city.size, this is where the selected value will be saved. Then change *** in the result column to city.size
See changes fiddle: http://jsfiddle.net/u2kxuyr7/
Get value on ngClick
Use ngChange as you allready do I would do it a bit different for simplicity. Change:
ng-change="check(city.name, whatEverValue)"
to:
ng-change="check(city)"
and the check function would look like:
$scope.check = function(city) {
console.log("City->" + city.name + " ::: Value->" + city.size);
};
See fiddle: http://jsfiddle.net/Lmszxj8s/
Then ngChange is fired on each change.

angularJs - Is it possible for 2 different models of different structures to sync or share states?

I have a list of checkboxes and values I"m loading from a list which comes back from the database.
Controller
listA = ['item1','item2'...'itemn']; //Master list of items
$scope.selectedItems = ["item1",... "item5"]; //selected items
$scope.attributesModel = [ //new model based on selected items
{"index":5,"attribute":"item1"},
{"index":10, "attribute":"item2"},
{"index":13, "attribute":"item3"},
{"index":21, "attribute":"item4"},
{"index":24, "attribute":"item5"}
];
View part 1
<td>
<div class="checkbox checkbox-notext">
<input checklist-model="selectedItems" checklist-value="key" type="checkbox" id="{{key}}" ng-disabled="exceededLimit && !checked" />
</div>
</td>
<td>
<label for="{{key}}">{{key}}{{$index}}</label>
</td>
view part 2
<div ng-repeat="(index, row) in attributesModel" >
<div class="margin10">
<div>Index<input ng-model="row.index" value="row.index" type="number" class="indexInputs"></input>{{row.attribute}}</div>
</div>
</div>
Now I would like to sync $scope.selectedItems and $scope.attributesModel. When a checkbox is deselected, both selectedItems and attributesModel models remove that item, and vice versa. So every time someone checks a new checkbox they are presented a attributesModel with an empty text field to type the index value.
catch The index key is null initially for every newly selected item that is added to attributesModel. The user must enter a new index # once the new item is created.
I've tried using watch but the problem I run into is when a new item is selected, I don't have access to the item itself. I only have access to the list without any idea whether the new item is X or if the item removed is Y in order to push/delete the right item.
So this might be a watch solution that I'm missing.
Let me know if I can clarify anything.
I am not sure what the problem is, but you could use ngChange on the checkboxes:
<input type="checkbox" ... ng-change="..." />
I asdume you have a checklist directive or something, so should do something there, but (since you don't share it with us) I can't tell what exactly :)
UPDATE:
Since the checklist directive is an external dependency, you could handle the ng-chage in your code:
<input type="checkbox" ... ng-change="changed(key)" />
/* In the controller: */
...
$scope.changed = function (key) {
if ($scope.selectedItems.indexOf(key) === -1) {
// The checkbox for `key` was unchecked...
} else {
// The checkbox for `key` was checked...
}
};

Categories

Resources