Why bootstrap uib dropdown won't work with the select element - html

When I run my angularJS (ver 1.4.9) app without the uib-.. directives, the select works but displays the items already open and the button does nothing.
It won't integrate with the dropdown menu component. All the examples I've seen use the unordered list and list item structure and the bootstrap UI dropdown appears based around the ui and li elements leading to the obvious question - why those elements and not select, option elements.
I would like to keep this: ng-options="e for e in vm.plannedEmployees track by e"
as I know it works and took a week of hacking to find it (though I don't understand it's complexity)
<div class="btn-group" uib-dropdown>
<button class="btn btn-primary" uib-dropdown-toggle>
<span class="caret"></span>
</button>
<select ng-options="e for e in vm.plannedEmployees track by e"
multiple="multiple" ng-model="selectedEmployee"
ng-change="vm.employeeSelectClick(selectedEmployee)">
</select>
</div>

All the examples I've seen use the unordered list and list item structure and the bootstrap UI dropdown appears based around the ui and li elements leading to the obvious question - why those elements and not select, option elements.
The uib-bootstrap project is aimed to port the bootstrap plugins into Angular directives, the bootstrap style defines the Dropdown by using a <ul> and <li> elements and uses CSS that select/target these elements, the uib directives just replace the jQuery code you would use in a non Angular page.
Also, in a UI sense, a select is not the same as a dropdown, in Angular sense, a select is an input that would normally expose an ngModel, uib-dropdown doesn't expose an ngModel (and subsequently, won't allow you to use directives that require it, such an ngChange or ngModelOptions).
That being said, you may want to look at ui-select as an alternative.

Related

The best way to make the view, a read-only of routeroutlet 's sections in angular ts

I am trying to make the mid-section to be read only and just enabling the button "OPEN".
I have the below original code. "router-outlet" renders the combination of several feature components. And I do not want to disable each and every elements or feature components
<div="row mid-section">
<router-outlet></router-outlet>
</div>
<div class="row">
<button class="btn btn-default"> OPEN </button>
</div>
I tried by adding as below:
<div="row mid-section" readonly="readonly">
But it still allows to edit and click on button inside mid-section div.
I would really appreciate your help. Thank you!
The HTML readonly property doesn't work like that. Its only for form fields and must be on that actual DOM element.
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly
Without seeing more of your code, I can't really give a better answer than these 2 options.
Option 1, a shared service that has that read only property. You could have a service, that has a behavior subject that you can update from the parent component. The inner components would all need to have that service injected, and do something appropriate when the value changes.
Option 2, you would need a container component that has a new boolean input, and it would need to pass that value down to all the children components (which would also need an input).

Angular 9: create custom directive that uses other standard directives

I am working to a little framework based on Angular/Material 9. I want to create custom directives that apply standard Material directives to their host element. For some UI elements, I prefer to use directives instead of creating custom wrapper components (using ng-content). For example, for buttons, I want use a "custom-button" directive like this:
<button type="button" custom-button>Hello world!</button>
The directive should apply the standard Material directives (and some other attribute, too) for buttons to the host element. The rendered HTML button should be:
<button type="button" custom-button mat-button mat-raised-button color="accent">Hello world!</button>
I know how to set properties/attributes, but obviously the button does not act like a "real" mat-button (it hasn't custom inner elements added by Material, nor the for the ripple effect).
I am pretty new with Angular, so I searched a lot for an answer. But I only found very complicated or outdated solutions (AngularJS). Any help?

How to add button role to a custom element in angular to make it accessible by screen reader?

I have a custom component in angular that handles images. I have alt text as an input to the element and the screen reader picks it up to utter it out. But whenever I tab to the image, it says 'Trash' group. I want the screen reader to read it out as 'Trash' button. How can I achieve this? The following is my current implementation:
Icn component:
<img [ngClass]="class" [file]="file" [alt]="alt">
Usage:
<icn class="del-icon" [file]="'trash'" [alt]="Trash"></icn>
I tried role="button" but that didn't work.
I don't know Angular so you may need to do some digging on how to structure this but your approach is making things difficult.
Instead make the button a <button>. This way you get all the native accessibility built in (e.g. accessible via tab by default, accepts focus, has hover and focus states etc.)and you will also get the correct announcements in screen readers.
Then place the icon inside the <button> and just style it appropriately.
<button> <!--add whatever directives angular requires here-->
<img [ngClass]="class" [file]="file" [alt]="alt">
</button>
Also you may consider using inline SVGs for your icons as they offer styling options and can change colour according to user preferences. It is also one less resource to download so will help with performance.
I figured out the solution to this problem by experimenting more with the roles.
The image tag doesn't take the role="button" as an attribute. Instead, the role needs to be assigned to the parent element of the image tag i.e., in my case the component icn like follows:
<icn class="del-icon" role="button" [file]="'trash'" [alt]="Trash"></icn>
Now, the screen reader software reads out as the 'Trash button' and gives further more instructions on how to interact with the button. And also if the above doesn't work, just by encapsulating the custom component in a span tag and assigning the role="button" to the span tag works like a charm.
<span class="del-btn-container" role="button">
<icn class="del-icon" [file]="'trash'" [alt]="Trash"></icn>
</span>
Note: Button role examples

Angular sub-navigation based on route

I'm building an Angular application with two levels of navigation in the header.
The top-level navigation are visible on all routes in the application, however the second-level navigation is context-specific and only exists on certain routes.
For example, when viewing a specific course in a university webapp, the second-level navigation might have links to description, prerequisites, the different subjects, etc.
One way of doing it is creating a subnav component that has a switch statement and renders the correct subnav component based on the route:
<div [ngSwitch]="currentRoute">
<course-sub-nav *ngSwitchCase="course"></course-sub-nav>
<students-sub-nav *ngSwitchCase="students">...</students-sub-nav>
<support-sub-nav *ngSwitchCase="support">...</support-sub-nave>
</div>
I don't like that solution because now the sub-nav component knows too much about all the different context-specific sub-navs.
Another approach is to use a service to manage it. i.e. having a SubNavService.setNavigationLinks(links: NavLink[]) that takes an array of links. The sub-nav component can then listen to changes to the SubNavService and dynamically render a list of sublinks.
This solution is a bit cleaner however it does mean that I need to call that service on ever top-level component to ensure that the sub-nav is being updated. I'd also need to clear the links on pages that don't require sub-navs.
A third solution might be doing something like:
<!-- In header.component.html -->
<div class="sub-nav">
<ng-content="subnav"></ng-content>
</div>
<!-- IN course-page.component.html -->
<div>
<subnav>
<!-- my nav links -->
</subnav>
<div class="course-content">
<router-outlet></router-outlet>
</div>
</div>
I like this approach the most given that if a subnav component exists, the subnav will be rendered, and if not, nothing will show. Additionally, the relevant component has full control over rendering the links within the subnav. however given that course-page isn't actually a child of the header, I'm not sure how I can make it work.
I was wondering if there's any way to get the last approach to work. If not, would the second approach be the most pragmatic solution to this problem or is there a cleaner solution that exists?
The cleanest solution would be the second you mentioned.
Having a service that would handle the menu - menu component would subscribe and listen for changes, meanwhile other components would handle what items they need to have in the menu.

How do I make a dynamic selectable list that's NOT a dropdown

Here is my current code:
<select [(ngModel)]="items">
<option *ngFor="let item of items" [ngValue]="item " {{item .symbol}} : {{item.companyName}}</option>
</select>
The problem is this is a dropdown that is not fully expanded by default. My goal is to have this list act similar to how Google has their search; A fully expanded list that updates based on the input.
I have the binding correct, but I can't seem to get the HTML correct to where it's always expanded and there is no empty entry for the first element.
Are the Select/Option tags the incorrect tags to use?
I can't find any other tags to use. Anything in Angular?
I don't know if natively you can have this feature inside a browser. But you can write one component to do this job. Basically, it'll have two parts, one input, and one panel with some help via Bootstrap.
<input type="text" class="form-control input-xs">
<ul class="dropdown-menu"></ul>
When you type in anything in the input, you can make the list of items displayed. After you select one item, the list can disappear. In terms of style, it'll be very similar to http://getbootstrap.com/docs/3.3/javascript/#dropdowns
Then you can wrap up everything inside an angular component. I know it's a long answer.