Angular ngbDropdown: keyboard navigation not working - javascript

I have modal made of a search bar and a list of elements, created with a ngbDropdown splitted into 2 components, parent and child. It works correctly, but I can't figure out a way to enable navigation with keyboard (use UP & DOWN keys to move between list's elements).
Following the official documentation, I tried to use ngbDropdownItem, but it's not working.
These are my 2 component's templates:
PARENT:
<div ngbDropdown class="dropdown-no-arrow" (openChange)="openChange($event)" container="body">
<!-- trigger -->
<ng-container>
<button class="btn" ngbDropdownToggle *ngIf="authorizationService.loggedInUser">
<i class="fal fa-farm me-2"></i>
<span>{{ authorizationService.loggedInUser.name }}</span>
<i class="fal fa-chevron-down ms-2"></i>
</button>
</ng-container>
<!-- menu -->
<div ngbDropdownMenu aria-labelledby="currentMenuItem" id="currentDropdown_{{ id }}">
<button ngbDropdownItem (click)="openNewModal()">
<i class="fal fa-plus me-1"></i> <span i18n>New</span>
</button>
<dm-child-list [isListDisplayed]="isDropdownOpened"></dm-child-list>
</div>
</div>
CHILD:
<div class="dropdown-header d-flex margin-x align-items-center">
<h6 class=" mb-0 flex-grow-1" i18n>MY LIST</h6>
</div>
<div class="px-4 py-2">
<div class="form-group mb-0">
<input class="form-control form-control-sm" libAutofocus [(ngModel)]="elSearch" (ngModelChange)="elSearch$.next($event)" i18n-placeholder placeholder="Search elements">
</div>
</div>
<button *ngFor="let el of list; let i = index;" ngbDropdownItem [ngClass]="{'top-border': i === 0}">
<i class="me-2 fas fa-check-circle text-success"
*ngIf="selectedEl?.id === el.id" title="Current element"></i>
<i class="me-2 fal fa-circle" *ngIf="selectedEl?.id !== el.id"></i>
<span>{{ el.name }}</span>
</button>
Can anyone help with making keyboard selection work?
Thanks!

By adding
#myDrop="ngbDropdown" (keyup)="myDrop.onKeyDown($event)"
on div where ngbDropdown directive is used, Arrow key navigation works.
In your case,
<div ngbDropdown
#myDrop="ngbDropdown"
(keyup)="myDrop.onKeyDown($event)"
class="dropdown-no-arrow
(openChange)="openChange($event)"
container="body">

Related

How to show active Bootstrap Accordion in a Laravel foreach loop?

I'm trying to show active bootstrap accordion in a laravel foreach loop, but I'm facing an issue.
What I'd like to do is what you see on the top image. But when I refresh the browser, the accordion closes as you can see on the bottom image.
Here's my code :
<div class="course-dashboard-side-content">
#foreach ($formation->sections as $section)
#php
$increment++;
#endphp
<div class="accordion course-item-list-accordion" id="accordionCourseMenu">
<div class="card">
<div class="card-header" id="collapseMenuOne">
<h2 class="mb-0">
<button class="btn btn-link" type="button" data-toggle="collapse"
data-target="#collapseSection-{{ $section->id }}" aria-expanded="false"
aria-controls="collapseOne">
<span class="widget-title font-size-15 font-weight-semi-bold">Section
{{ $increment }}: {{ $section->name }}</span>
<div class="course-duration">
<span>{{ $section->courses->count() }} Vidéos </span>
<span>Durée Totale</span>
</div>
</button>
</h2>
</div>
<div id="collapseSection-{{ $section->id }}" class="collapse" aria-labelledby="collapseMenuOne" data-parent="#accordionCourseMenu">
<div class="card-body">
<div class="course-content-list">
<ul class="course-list">
#foreach ($section->courses as $course)
#php
$increment2++;
#endphp
<li class="course-item-link {{ $loop->first ? 'active' : '' }}">
<div class="course-item-content-wrap">
<div class="custom-checkbox">
<input type="checkbox" id="chb29">
<label for="chb29"></label>
</div>
<div class="course-item-content">
<h4
class="widget-title font-size-15 font-weight-medium">
{{ $increment2 }}.
<a href="{{ route('student.course.chapitre', [
$formation->slug,
'course' => $course->slug,
'id' => $section->id,
]) }}">
{{ $course->name }}
</a>
</h4>
<div class="courser-item-meta-wrap">
<p class="course-item-meta"><i
class="la la-play-circle"></i>Durée</p>
#if ($course->document)
<div class="msg-action-dot">
<div class="dropdown">
<a class="theme-btn theme-btn-light"
href="#" data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="fa fa-folder-open mr-1"></i>
Document<i
class="fa fa-angle-down ml-1"></i>
</a>
</div>
</div>
#else
#endif
</div>
</div>
</div>
</li>
#endforeach
</ul>
</div>
</div>
</div>
</div>
</div>
#endforeach
</div>
Also, is there a way to make the li active?
I tried different ways, but I failed.
<ul class="faq-list">
#forelse ($faqs as $faq)
<li>
<div data-bs-toggle="collapse" class="collapsed question" href="#faq{{ $faq->id }}">{{ $faq->question }}<i class="bi bi-chevron-down icon-show"></i><i class="bi bi-chevron-up icon-close"></i></div>
<div id="faq{{ $faq->id }}" class="collapse" data-bs-parent=".faq-list">
<p>
{{ $faq->answer }}
</p>
</div>
</li>
#empty
<div>No Questions Found</div>
#endforelse
</ul>

Angular - Dynamic tooltip content

I have the following problem: I want to give content to a tooltip dynamically by executing a function with a param. The key here is that the param has the information I need to change the tooltip content since this tooltip is in a ng-repeat directive.
My html is kinda this:
<div class="module-box highlight clearfix" ng-repeat="request in model.requests.items track by request.id" ng-class="{ 'inactive-section': model.isLoading.value }">
...
<div class="module-column text-center">
<h5>REQUEST STATUS</h5>
<p>
<span data-toggle="tooltip" uib-tooltip="{{request.requestStatus.name}} {{someFunction(request)}}">
<i class="fa fa-clock-o fa-lg {{request.requestStatusGroupCssClass}}" aria-hidden="true"></i>
</span>
</p>
</div>
...
</div>
This html view is has it's own directive called homePendingRequests.js. Then I insert the view in a Home.html page (that has it's controller).
Thanks!!
I just solved it. It works pretty sweet
html directive:
<div class="module-column text-center">
<h5>ESTADO</h5>
<p>
<div ng-mouseenter="tooltipHelper(request)">
<span data-toggle="tooltip" uib-tooltip="{{msg}}">
<i class="fa fa-clock-o fa-lg {{request.requestStatusGroupCssClass}}" aria-hidden="true"></i>
</span>
</div>
</p>
</div>
js directive:
$scope.tooltipHelper = function (request) {
$scope.msg = request.requestStatus.name;
if (request.validatorsForPendingStatus) {
$scope.msg += ' (' + request.validatorsForPendingStatus.validatorsList + ')';
}
};
use $copmile(`<div class="module-box highlight clearfix" ng-repeat="request in model.requests.items track by request.id" ng-class="{ 'inactive-section': model.isLoading.value }">
...
<div class="module-column text-center">
<h5>REQUEST STATUS</h5>
<p>
<span data-toggle="tooltip" uib-tooltip="{{request.requestStatus.name}} {{someFunction(request)}}">
<i class="fa fa-clock-o fa-lg {{request.requestStatusGroupCssClass}}" aria-hidden="true"></i>
</span>
</p>
</div>
...
</div>`)($scope)

comment the below div structure the error is dissappearing

After upgrading to Angular 5 I am facing this error:
ExpressionChangedAfterItHasBeenCheckedError: Expression has
changed after it was checked. Previous value: 'ngIf: undefined'.
Current value: 'ngIf: null'.
If I comment the below div structure the error is disappearing
<div *ngIf="sportsServ?.relatedsportssData" class="stars stars-default pinnedSection">
Any idea how to fix this?
<!-- language-all: lang-or-tag-here -->
<div class="playerpobUpBox playerWindow kPopupConfirmationBox" id="greaterAlertHeader">
<div class="row playerPopUpGridCollection playerPopUpContent lineHeightInputs">
<div class="playerContent"></div>
</div>
<div class="clearFloat"></div>
<div class="row playerPopUpFooter textAligncenterImp">
<button class="commonBtn" type="button" id="greaterAlertOKHeader">OK</button>
</div>
<div class="clearFloat"></div>
<div class="clearFloat"></div>
</div>
<progress-circle></progress-circle>
<div class="leftSlider toggleShow">
<div class="fixedLeftSlider">
<div class="leftSliderBox">
<div class="stars-group" id="accordion">
<div class="stars stars-default recentsportss">
<div class="stars-heading active">
<h4 class="stars-title">
<a data-toggle="collapse" href="#recentsportss" aria-expanded="true">
<i class="fa fa-angle-right starsCaret"></i>
{{recentsportss}}
</a>
<span>{{lastTenViewsportss}}</span>
</h4>
</div>
<div id="recentsportss" class="stars-collapse collapse in" aria-expanded="true">
<div class="stars-body">
<ul *ngFor="let item of recentsportssList; let k=index;">
<li class="recentList" (click)="opensports($event,item.sportsNo,item.packageName,item.pinnedFlag,item.originatingNetwork);$event.stopPropagation();" [class.active]="currentsportsId == item.sportsNo" *ngIf="k<20">
<span> {{item.packageName}}
<i id="pinToggle" class="recentsportssRightIcon" (click)="pinUnpin($event,item.sportsNo); $event.stopPropagation();"></i>
</span>
</li>
</ul>
</div>
</div>
</div>
<div class="stars stars-default pinnedSection">
<div class="stars-heading">
<h4 class="stars-title">
<a data-toggle="collapse" href="#pinnedSection" aria-expanded="true">
<i class="fa fa-angle-right starsCaret"></i>
{{pinnedsportss}} & {{titles}}
</a>
</h4>
</div>
<div id="pinnedSection" class="stars-collapse collapse in" aria-expanded="true">
<div class="stars-body pad0Imp">
<label class="pinnedsportsSubTitle">sportss</label>
<ul *ngFor="let item of pinnedsportssList; let i=index;">
<li class="recentList" (click)="opensports($event,item.sportsNo,item.packageName,item.pinnedFlag,item.originatingNetwork);$event.stopPropagation();" [class.active]="currentsportsId == item.sportsNo" *ngIf="i<20">
<span>{{item.packageName}}
<i id="pinToggle1" class="recentsportssRightIcon" (click)="pinUnpin1($event,item.sportsNo); $event.stopPropagation();"></i>
</span>
</li>
</ul>
<!--<ul>
<li>sports Number One<i class="recentsportssRightIcon"></i></li>
<li>sports Two<i class="recentsportssRightIcon"></i></li>
<li>sports Three<i class="recentsportssRightIcon"></i></li>
</ul>-->
<label class="pinnedsportsSubTitle borderTopTitle">Titles</label>
<ul *ngFor="let item of pinnedTitlesList;let k=index;">
<li class="recentList" (click)="navigateTitlePage($event,item.titleNo);$event.stopPropagation();" *ngIf="k<20">
<span>{{item.titleName}}
<i id="pinTitle1" class="recentsportssRightIcon" (click)="pinUnpintitle($event,item.titleNo);$event.stopPropagation();"></i>
</span>
</li>
<!--<li>Title Name Number One<i class="recentsportssRightIcon"></i></li>
<li>Title Name Number two<i class="recentsportssRightIcon"></i></li>
<li>Title Number 3<i class="recentsportssRightIcon"></i></li>-->
</ul>
</div>
</div>
</div>
<div *ngIf="sportsServ?.relatedsportssData" class="stars stars-default pinnedSection">
<div class="stars-heading">
<h4 class="stars-title">
<a data-toggle="collapse" href="#relatedsportss" aria-expanded="true">
<i class="fa fa-angle-right starsCaret"></i>
{{sportsServ?.relatedsportssHeading}}
</a>
</h4>
</div>
<div id="relatedsportss" class="stars-collapse collapse in" aria-expanded="true">
<div class="stars-body">
<ul>
<li *ngFor="let item of sportsServ?.relatedsportssData" (click)="selectedsports($event,item.sportsId,item.sportsName);$event.stopPropagation();" [class.active]="currentsportsId == item.sportsId">{{item.sportsName}}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="showHideLeftSliderBtn"></div>
</div>
</div>
<router-outlet></router-outlet>
<!--<div><sportsSearch style="display:none;" (sportsSearchEmitter)="sportsSearchEmitter($event)"></sportsSearch></div>-->
Probably you're getting this value asynchronously, you may try to:
Use changeDetection: ChangeDetectionStrategy.OnPush on your #Component (before selector), in your .ts file, don't forget to import changeDetection from "#angular/core";
Try to not get the value asynchronously, using setTimeout() function to get the value;
Inject cdr: ChangeDetectorRef on your module and import it from your "#angular/core", after that, on your implemented ngAfterViewChecked just put this.cdr.detectchanges.
To provide a better answer, you need to post your .ts code, and remove some lines to make the code cleaner. Hope it helps!

Meteor + Iron-Router: Template no more working

I'm working on a project, it was working normally. But then I had to add Iron-Router to add some features and one of my templates do not work anymore.
I have this home.html file, that import two templates, "nova" and "piadas". But only the "nova" is displayed. How do I fix it?
home.html:
<template name="home">
<br>
{{> nova}}
<div class="fixed-action-btn horizontal">
<a class="btn-floating btn-large green accent-4"> <i class="large material-icons">person</i> </a>
<ul>
<li><a class="btn-floating green accent-4"><i class="material-icons">add</i></a></li>
<li><a class="btn-floating green accent-4"><i class="material-icons">account_circle</i></a></li>
</ul>
</div>
<br>
{{> piadas}}
</template>
piadas.html:
<template name="piadas">
<div class="row"> {{#each piadas}}
<div class="col s12 m6">
<div class="card grey lighten-4">
<div class="card-content black-text"> <span class="card-title">{{titulo}}</span>
<p class="truncate">{{piada}}</p>
</div>
<div class="card-reveal"> <span class="card-title grey-text text-darken-4">{{titulo}}<i class="material-icons right">close</i></span>
<p>Essa piada foi enviada por <strong>{{autor}}</strong> no dia <strong>{{formataData}}</strong>.</p>
<button id="btn-like" class="btn" style="float: left;"><i class="material-icons ">thumb_up</i></button>
<button id="btn-denunciar" class="btn btn-danger" style="float: right;"><i class="material-icons ">flag</i></button>
</div>
<div class="card-action"> <i class="material-icons right">more_vert</i> </div>
</div>
</div> {{/each}} </div>
</template>
I've discovered that: how the "piadas" template was returning a helper function with some MongoDB querys, I had to pass the querys for the router.

Ddrag and drop and bootstrap UI Accordion

I have such code:
<div ui-tree>
<ol ui-tree-nodes="" ng-model="policies">
<li ng-repeat="item in policies" ui-tree-node>
<div ui-tree-handle>
<accordion>
<accordion-group>
<accordion-heading>
{{item.Title}}
<i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.open, 'glyphicon-chevron-right': !status.open}"></i></accordion-heading>
{{item.Content}}
<ol ui-tree-nodes="" ng-model="item.Options" data-nodrop>
<li ng-repeat="subItem in item.Options" ui-tree-node>
<div ui-tree-handle>
<accordion-group>
<accordion-heading>{{subItem.Title}}<i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.open, 'glyphicon-chevron-right': !status.open}"></i></accordion-heading>
{{subItem.Content}}
</accordion-group>
</div>
</li>
</ol>
</accordion-group>
</accordion>
</div>
</li>
</ol>
</div>
and when i click on header to roll over content of accordion bootstrap i get that drag and drop event is handled
how could i:
add to accordion-header some icon, with the help of which i could drag and drop all my nodes, and when i click on title my data get rolled as bootstrap do? Is it reall to hadle too events? and how?
https://github.com/JimLiu/angular-ui-tree
http://angular-ui.github.io/bootstrap/
upd:
<script id="template/accordion/accordion-group.html" type="text/ng-template">
<div class="panel panel-default">
<div class="panel-heading">
<h4 href class="btn btn-success btn-xs accordion-toggle pull-left" data-nodrag ng-click="toggleOpen();"><i class="glyphicon" ng-class="{'glyphicon-chevron-right': isOpen, 'glyphicon-chevron-down': !isOpen}"></i></h4>
<h4 class="panel-title">
<a href accordion-transclude="heading"><span>{{heading}}</span></a>
</h4>
</div>
<div class="panel-collapse" collapse="!isOpen">
<div class="panel-body" ng-transclude></div>
</div>
</div>
</script>
<div ui-tree="options" data-drag-delay="20">
<ol ui-tree-nodes="" ng-model="articles">
<li ng-repeat="item in articles" ui-tree-node>
<div ui-tree-handle>
<accordion close-others="false">
<accordion-group>
<accordion-heading>
{{item.Title}}
</accordion-heading>
<div ng-bind-html="item.Content"></div>
<ol ui-tree-nodes="" ng-model="item.Options">
<li ng-repeat="subItem in item.Options " ui-tree-node>
<div ui-tree-handle>
<accordion close-others="false">
<accordion-group>
<accordion-heading>{{subItem.Title}}
</accordion-heading>
<div ng-bind-html="subItem.Content"></div>
</accordion-group>
</accordion>
</div>
</li>
</ol>
</accordion-group>
</accordion>
</div>
</li>
</ol>
</div>
drag and drop work only if i click on header text, or on body text (when on panel-title nothing is fired)
opening is on ng-click="toggleOpen();" and all is ok, just that drag and drop must be on whole element (cursor is for whole element, but drag-and-drop is only for element text)
how to be with scopes maybe?
Wrap <accordion-heading> content in a <div>
HTML accordion code
<accordion close-others="false">
<accordion-group is-open="isopen">
<accordion-heading ng-click="isopen=!isopen">
<div>{{item.Title}}</div>
</accordion-heading>
<div ng-bind-html="item.Content"></div>
<ol ui-tree-nodes="" ng-model="item.Options">
<li ng-repeat="subItem in item.Options " ui-tree-node>
<div ui-tree-handle>
<accordion close-others="false">
<accordion-group>
<accordion-heading>{{subItem.Title}}
</accordion-heading>
<div ng-bind-html="subItem.Content"></div>
</accordion-group>
</accordion>
</div>
</li>
</ol>
</accordion-group>
</accordion>
Hope this will help you.
Try to use $event.stopPropagation() on toggle accordion. In my case I added ui-tree-handle only on icon like this
<div class="panel-controls left" ng-hide="isEditable" ui-tree-handle>
<span class="controls-item no-border">
<i class="glyphicon glyphicon-th-list text-sm text-gray"></i>
</span>
</div>
Full code
<div ui-tree="options" data-max-depth="3">
<!-- Ranks list START -->
<div ui-tree-nodes data-type="rank" ng-model="taskbook.ranks">
<div ng-repeat="rank in taskbook.ranks"
class="panel panel-clean panel-solid panel-sortable"
ui-tree-node
ng-controller="RankListCtrl"
data-type="rank">
<!-- Rank Edit START -->
<div class="panel-heading clearfix"
ng-class="{'': isCollapsed, 'collapsed': !isCollapsed}">
<div class="panel-controls left" ng-hide="isEditable" ui-tree-handle>
<span class="controls-item no-border">
<i class="glyphicon glyphicon-th-list text-sm text-gray"></i>
</span>
</div>
<h2 class="panel-title pull-left" ng-hide="isEditable">
<span ng-bind="rank.name | truncate:false:15"></span>
</h2>
<div class="inline-edit" data-nodrag ng-show="isEditable">
<form class="form-inline" role="form">
<div class="form-group">
<input type="text"
class="form-control"
placeholder="Rank name"
ng-model="rank.name"
focus-me="isEditable">
</div>
</form>
</div>
<div class="panel-controls right">
<button type="button"
class="controls-item btn btn-sm btn-danger btn-flat text-sm"
ng-hide="!isEditable"
ng-click="cancelEditable()">
<i class="glyphicon glyphicon-remove"></i>
</button>
<button type="button"
class="controls-item btn btn-sm btn-success btn-flat text-sm"
ng-hide="!isEditable"
ng-disabled="rank.name === ''|| isSaving"
ng-click="hideEditable()">
<i class="glyphicon glyphicon-ok"></i>
</button>
</div>
</div></div></div>
The only thing is that I did't use accordion. I implemented collapsed directive.
Hope this can help!
Good luck

Categories

Resources