Toggling expand/collapse button for a knockout table/sub- table - html

I have a table using Knockout and Typescript. Each record on the table is expandable with an expand button. Clicking this expand button toggles the collapse button. The problem I'm having is that when there are more than 1 records in the table, clicking this expand button toggles the collapse button for all of the records in the table. Is there anyway to just toggle the one I've clicked? Here's the code:
HTML
<a href="#" data-bind="attr: {'id': 'show_' + $index()}">
<img src="../../Images/expand.gif" class="expand_button" alt="Show Checks" title="Show Checks" border="0" data-bind="click: PrintCheck.GetBankDrafts, visible: !$parent.expand(), click: PrintCheck.ToggleExpand">
<img src="../../Images/collapse.gif" class="collapse_button" alt="Show Checks" title="Show Checks" border="0" data-bind="visible: $parent.expand(), click: PrintCheck.ToggleExpand">
</a>
The Typescript
class SearchPrintedChecksModel {
public expand = ko.observable(false);
public checkRuns = ko.observableArray<CheckRunModel>(null);
}
$(document).ready(() => {
ko.applyBindings(printModel);
});
var printModel = new SearchPrintedChecksModel();
export function ToggleExpand(data: CheckRunModel): void {
printModel.expand(!printModel.expand());
GetBankDrafts(data);
}

You'll want each table to keep track of its own expanded state. Your toggle variable appears to be global with respect to the tables so it's getting globally applied to all of them.
First you'll have to have a separate observable for each table to keep track of their respective expanded states. Move the expand variable from SearchPrintedChecksModel to the model for your table (CheckRunModel?). Then in your ToggleExpand function change printModel.expand(...) to data.expand(...). Finally in your binding change "visible: $parent.expand()" to just "visible: expand".

Related

Angular. Getting wrong data from ngb-panel

I use ngb-accordion in my app. I am trying to get data from every panel but when the first panel is opened click from the second panel returns me wrong data.
Result
I think the problem is the event which raises when input file changes.
Stackblitz Link
I will be glad if someone give me a hint for solving this problem.
There are few things to note in your code.
Your *ngFor is at ngb-accordion which is creating a new accordion for every loop, instead of creating multiple panel within one accordion.
Fix: <ngb-panel *ngFor="let data of datalist; let i = index">
You are using the same label for all three panels, because of which your first panel is opening every time, regardless of which panel you are clicking.
Fix: <label [for]="'image-input-' + i"> and <input ... [id]="'image-input-' + i"
The modal that opens after image selection has no knowledge of which panel it's getting triggered from. So, you have to use your (change)="onFileChange($event, data)" event/function to keep track of selected panel/corresponding data.
Then you can pass that selection from your modal to your processFile(...)
Fix:
export class AppComponent {
...
selectedData: Data;
...
...
onFileChange(event: any, data): void {
...
this.selectedData = data;
}
}
html:
...
<input ... (change)="onFileChange($event, data)>
...
...
<button
...
(click)="processFile(imageInput, selectedData)"
> Done
</button>
Stackblitz Demo

How can I check a Material Checkbox in Angular with a single click?

Execution:
I have several Material Checkboxes in a Material Dialog Component. They represent a column filter for a table:
<h1 mat-dialog-title>Filter</h1>
<div mat-dialog-content>
<ng-container *ngFor="let column of allColumns; index as i; first as firstEntry ">
<mat-checkbox *ngIf="!firstEntry" class="checkbox" [checked]="checkedList[i]" [aria-label]="column" (click)=doToggle(i)>{{column}}</mat-checkbox>
</ng-container>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onNoClick()">Abbrechen</button>
<button mat-button [mat-dialog-close]="allColumns" cdkFocusInitial>Reset</button>
<button id="ok-button" mat-button [mat-dialog-close]="displayedColumns" cdkFocusInitial>Ok</button>
</div>
When I click on a Checkbox I want to check it. All booleans of the Checkboxes are saved in a boolean array named checkedList. When I click on a Checkbox the method doToggle(i) gets executed.
doToggle(i: number){
if(this.checkedList[i]){
this.displayedColumns[i] = this.allColumns[i];
}
else{
this.displayedColumns[i] = null;
}
this.checkedList[i] = !this.checkedList[i];
}
The method also fills or empties the values at the appropriate position of the column list. At the end of the method it negates the boolean at the appropriate position in the checkedList.
Problem:
I always have to click twice on the Checkbox to check or uncheck it. The values are added or removed properly. But still I have to click two times on the checkbox. Why is that?
Try sending the value of checkbox in the click event.
Add template reference variable(#ck) to checkbox and get its value ck.checked
[checked]="checkedList[i]" [aria-label]="column" #ck (click)="doToggle(i,!ck.checked)"
and in doToggle function
doToggle(i,value){
this.checkedList[i] = value;
if(this.checkedList[i]){
this.displayedColumns[i] = this.allColumns[i];
}
else{
this.displayedColumns[i] = null;
}
}

Angular generate refresh button click

I have a button that refreshes data.
I would like to generate 10 clicks on this button.
<data-box class="market" [data]="data">
<h2>Data</h2>
<img class="refresh" src="assets/img/refresh.png" (click)="getMarketData()"/>
</data-box>
I want a function that clicks the button (not call the function that is triggered by the button)
<img class="refresh" src="assets/img/refresh.png" #Refresh (click)="getMarketData()" />
Add #Refresh on your element, and then reference it using ViewChild
#ViewChild('Refresh') myDiv: ElementRef<HTMLElement>;
triggerFalseClick() {
let el: HTMLElement = this.myDiv.nativeElement;
el.click();
}
triggerFalseClick will click your button, you can call it as many times as you want.

How to control multiple (click) events inside a table?

Hello I hope the title is descriptive enough to understand my problem,
I have this HTML code. In <td> i have attached a (click) function. Inside this <td>, I have another div which also has a (click) function attached. When I click the button inside <td> both functions are triggered.
But, I would like only the (click) in div to be triggered when I click it and not the <td> (click).
Is it possible to happen?
<td mat-cell align="center" *ngFor="let option of options; let opt_indx = index"
(click)="optionsClicked(opt_indx, i);" id="defaultBadge-wrapper">
<div *ngIf="!rowCommands[i].defaultSelected[opt_indx]" class="defaultBadge">
<a class="badge badge-secondary" matTooltip="Click here to set this Option as Default"
(click)="markDefault(i,option.id)">Default</a>
</div>
</td>
Typescript code
markDefault(index, option: string) {
alert('this function triggered');
}
optionsClicked(option, rule) {
alert('I dont want this function to be triggered when i click markDefault');
}
In ts method pass the $event as param and include
event.stopPropagation();
or in the click event pass another function:
(click)="markDefault(i,option.id); event.stopPropagation();">Default</a>
use the stoppropagation to prevent the event from propagating
(click)="markDefault(i,option.id); event.stopPropagation();">Default</a>
just add $event.stopPropagation() to inner click.
<div *ngIf="!rowCommands[i].defaultSelected[opt_indx]" class="defaultBadge">
<a class="badge badge-secondary" matTooltip="Click here to set this Option as Default"
(click)="$event.stopPropagation();markDefault(i,option.id)">Default</a>
</div>

Parent/child click relationships in AngularJS directives

I have a custom directive placed on a Kendo UI treeview widget.
It seems to be working fine side-by-side, except that I'm trying to simply display the custom icons next to the tree node which is clicked on (see sample image below).
So my directive is data-toggle-me, placed next to the Kendo k-template directive as follows :
<div class="reports-tree" kendo-tree-view="nav.treeview"
k-options="nav.treeOptions"
k-data-source="nav.reportsTreeDataSource"
k-on-change="nav.onTreeSelect(dataItem)" >
<span class="tree-node" k-template data-toggle-tree-icons>{{dataItem.text}}</span>
</div>
and the directive code here inserts some custom icons next to the tree node when a user clicks on that tree node :
.directive('toggleMe', function ($compile) {
// Kendo treeview, use the k-template directive to embed a span.
// Icons appear on Click event.
return {
restrict: 'AE',
transclude: true,
template: '<span ng-show="nav.displayIcons" id="myIcons" class="reptIcons" style="display:none;width:50px;align:right;">' +
' <a title="add new folder" ng-click="nav.addAfter(nav.selectedItem)"><i class="fa fa-folder-open"></i></a> ' +
'<a title="add report here" ng-click="nav.addBelow(nav.selectedItem)"><i class="fa fa-plus"></i></a> ' +
'<a title="remove" ng-click="nav.remove(nav.selectedItem)"><i class="fa fa-remove"></i></a> ' +
'<a title="rename" onclick="showRename(this);"><i class="fa fa-pencil"></i></a>' +
'</span>',
link: function (scope, elem, attrs) {
var icons = elem.find("#myIcons");
elem.on('click', function (e) {
$('.reptIcons').css('display', 'none');
icons.css("display", "inline");
icons.css("margin-left", "5px");
});
}
}
})
My biggest problem at this point is getting the icons to appear on the treenode which is clicked on. Then once the user clicks on a different node, the icons will only render again on the newly-clicked node.
This fiddle represents a partially-working example but the icons are appearing on every single treenode - click tree item to show icons
**** UPDATED TREE IMAGE - All child nodes now show icons (not what I want) ****
I'm not sure to understand your issue, you should try to reduce the code to the minimum and have a snippet/jsfiddle that works.
If all you want is not trigger click events when $scope.disableParentClick is set to true, simply add
elem.on('click', function (e) {
// Do not execute click event if disabled
if (!$scope.disableParentClick) { return; }
...
});
Now that seems all not very angular friendly to me. You should externalize your HTML in either the template or templateUrl of your directive, potentially adding to it a ng-if="displayTemplate" which would only display the node when a click would set $scope.displayTemplate = true;
Also, instead of listening for click events this way, you should use the ng-click directive. Everything is doable with directives. I can give more information when you better understand your problem: I suspect you are not approaching it the right way.
UPDATE: if all you want is display the icons list of the clicked element, you could do it way easier. You actually don't need the toggle-me directive, but even if you keep it you can solve all your troubles the angular-way, which is by using ng-click, ng-repeat, etc. Please have a look at the following jsFiffle to see one way of doing that. There are many other ways, but really try using ng-click to avoid troubles:
http://jsfiddle.net/kau9jnoe/
Events in the DOM are always bubbling up. That is a click on a link would trigger an onclick handler on every element up the hierarchy, e.g. also the body element. After all the click happened within body.
The same is true for your directive. Any click within your element triggers its event handler. To circumvent this either attach the event handler somewhere else or ignore clicks from the links.
The event object has a target property that tells you what element initiated the event. So you could do something like this:
elem.on('click', function (e) {
if (e.target.nodeName.toLowerCase() == 'a') return; //ignore click on links