How to change toggle icon of mat-expansion-panel? - javascript

The default icon for toggling a mat-expansion-panel is >. Setting hideToggle true just hides the toggle icon. Is there any way to change it? I found nothing in the official documentation. I want to use + or - icon if the state is closed or opened respectively.

As stated on the Angular Material Expansion Panel documentation, we can customise the stylings of the mat-extension-panel-header, which will in turn allow you to customise the icons.
First and foremost, you hide the original icon by setting the hideToggle attribute to true. In addition, we assign the event bindings (opened) and (closed) to the panelOpenState property. You may check out the other properties of mat-expansion-panel over here.
<mat-expansion-panel (opened)="panelOpenState = true" (closed)="panelOpenState = false"hideToggle="true">
Next, on your mat-panel-description, you include the custom icons required. The icons shown will be dependent on the state of the panel. For this example, we are using the icons from Material Icons.
<mat-panel-description>
<mat-icon *ngIf="!panelOpenState">add</mat-icon>
<mat-icon *ngIf="panelOpenState">remove</mat-icon>
</mat-panel-description>
I have edited the original sample from the Angular documentation, such that it is using the custom + and minus icons to denote the expanded/collapsed state of the mat-extension-panel. You may access the demo over here.

There is a class .mat-expanded which is applied to a <mat-expansion-panel>
So you can just use CSS to change the icon, no JS required.
To change arrow down to up:
.mat-expanded .mat-expansion-panel-header-title .material-icons{
transform: rotate(180deg);
}
<mat-panel-title>
<span class="material-icons">expand_more</span>
Panel with icon `transform`
</mat-panel-title>
To swap icons:
.mat-expansion-panel-header-title .open,
.mat-expanded .mat-expansion-panel-header-title .close{
display: inline-block;
}
.mat-expanded .mat-expansion-panel-header-title .open,
.mat-expansion-panel-header-title .close{
display: none;
}
<mat-panel-title>
<span class="material-icons open">open_in_full</span>
<span class="material-icons close">close_fullscreen</span>
Panel with open/close
</mat-panel-title>
Here is a link to a demo https://stackblitz.com/edit/angular-e2fnlm
CSS solution works with multiple Material Expansion Panels

Give the name to the mat-expansion-panel say panel in the example. Use expanded property of the mat-expansion-panel to show or hide the + or - icon.
<mat-expansion-panel #panel hideToggle>
<mat-expansion-panel-header>
<mat-panel-title>
Buttons
</mat-panel-title>
<mat-icon >{{panel.expanded? 'remove' : 'add'}}</mat-icon>
</mat-expansion-panel-header>
</mat-expansion-panel>

For those interested, here is an example that works with multiple Expansion Panels:
View:
<mat-accordion>
<mat-expansion-panel *ngFor="let panel of panels; let i = index"
(opened)="panelOpenState[i] = true" (closed)="panelOpenState[i] = false"
hideToggle>
<mat-expansion-panel-header>
<mat-panel-title>
{{panel.title}}
</mat-panel-title>
<span *ngIf="!panelOpenState[i]">Show</span>
<span *ngIf="panelOpenState[i]">Hide</span>
</mat-expansion-panel-header>
Panel Content
</mat-expansion-panel>
</mat-accordion>
Component:
export class PanelComponent implements OnInit {
panels: [];
panelOpenState: boolean[] = [];
ngOnInit() {
// loop to add panels
for (let i = 0; i < 5; i++) {
this.panels.push({ title: i });
this.panelOpenState.push(false);
}
}
}

You can use mat-icon in the mat-expansion-panel-header.
Please check this working example on stackblitz.
For a + icon you can use <mat-icon>add</mat-icon>
<mat-accordion>
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-panel-title>
Personal data
</mat-panel-title>
<mat-icon>add</mat-icon>
</mat-expansion-panel-header>
<mat-form-field>
<input matInput placeholder="First name">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Age">
</mat-form-field>
</mat-expansion-panel>

add hideToggle to mat-expansion-panel :
<mat-expansion-panel (opened)="panelOpenState = true"
(closed)="panelOpenState = false" hideToggle>
and add icon:
<mat-expansion-panel-header>
<mat-icon>add</mat-icon>

Related

Vue multiselect slots caret dropdown option show and hide

<v-multiselect
ref="tagMultiselect"
v-model="sort"
:options="sorts"
class="filter"
>
<span
slot="caret"
slot-scope="{ toggle }"
class="arrow"
#mousedown.prevent.stop="toggle"
>
<font-awesome-icon
class="icon"
icon="chevron-down"
/>
</span>
</v-multiselect>
I just want to change caret to chevron up when dropdown options show.
Thank you in advance.
They use the class multiselect--active, all you need to do is use that and add a transform on it (again, the same as them)
.multiselect--active .icon {
transform: rotate(180deg);
}

Angular: Check if element has a class and add a class to another element

How can I check with Angular if a DOM element has a class and then add a class to another element?
My Template:
<div class="d-table">
<ui-switch class="d-table-cell">
<span class="switch">
<small></small>
</span>
</ui-switch>
<span class="d-table-cell">
sign in
</span>
</div>
If the span with the class switch has also the class checked, then the color of the text between the span element with the class d-table-cell should be black, otherwise the color should be gray.
No problem with jQuery, but I want to do it the right Angular way :)
.gray {
color:gray;
}
.black {
color: black;
}
<div class="d-table">
<span class="switch" #switch>
<small></small>
</span>
<span class="d-table-cell" [ngClass]="switch.classList.contains('checked') ? 'gray' : 'black'">
sign in
</span>
</div>
We can create template reference variables and can directly access them in the template to get attributes, classes and etc. #switch is a template reference variable
stackblitz
The Angular way would be to avoid, if possible, direct, low-level DOM manipulation. Tie the checked class to some model, and tie the d-table-cell's class to the same model. Like that:
<div class="d-table">
<ui-switch class="d-table-cell" [class.checked]="someVariable">
<span class="switch">
<small></small>
</span>
</ui-switch>
<span class="d-table-cell" [class.active]="someVariable">
sign in
</span>
</div>
and in TS
someVariable: boolean;
Maybe tie someVariable to an ngModel of some input, or whatever logic should dictate whether the switch is checked or not.
step: create a template ref for the main element like this:
HTML:
in the corresponding component:
#ViewChild('switchSpan') switchSpanElement:ElementRef;
After this, you can check the classlist of the said element like this:
if(switchSpanElement:ElementRef.nativeElement.classList.contains('checked')) {
...
}
step: create a boolean variable in the component, that you can access in the template as well (==don't make it private).
isSwitchChecked = false;
and you can make it check whether the class was added when change detecion runs (from this answer, syntax here)
class <yourComponent> implements DoCheck
...
ngDoCheck() {
this.isSwitchChecked = witchSpanElement:ElementRef.nativeElement.classList.contains('checked');
}
you can then bind that boolean variable in the template to add a class to the said span:
<span class="d-table-cell" [ngClass]={'some-class':isSwitchChecked}>
sign in
</span>
and you can define a class in css:
.some-class{
background-color: gray;
}

Styling Material Vue AutoComplete suggestion drop down

Background
I am using the Material Vue AutoComplete component to provide TypeAhead functionality to my users in a vue application.
When the Chrome browser is minimized in width to check for responsiveness I noticed the suggestion container gets smaller in width but, the text inside of the suggestion container does not break in the box. Instead, the sentence that is being displayed runs off the box to the right of the screen.
Problem
I can not figure out how to add styles to correct the before mentioned issue.
Example
<div class="md-layout md-gutter">
<div class="md-layout-item md-small-size-100">
<md-autocomplete
v-model="currentCustomer"
:md-options="customers"
#md-changed="getCustomers"
#md-opened="getCustomers"
#md-selected="getSelected"
:md-fuzzy-search="false"
>
<label>Search Customers...</label>
<template slot="md-autocomplete-item" slot-scope="{ item, term }">
<md-highlight-text :md-term="term">{{ item.email }}</md-highlight-text>
</template>
<template slot="md-autocomplete-empty" slot-scope="{ term }">
No customers matching "{{ term }}" were found. <a #click="addSearchedCustomer(term)">Create </a>this customer.
</template>
</md-autocomplete>
</div>
Specifically this line runs off the screen when there are no search results,
<template slot="md-autocomplete-empty" slot-scope="{ term }"> No customers matching "{{ term }}" were found. <a #click="addSearchedCustomer(term)">Create </a>this customer.</template>
Image Example
Link AutoComplete
UPDATE
What I have tried
When I inspect the AutoComplete with Chrome Dev Tools, I expand the div and this is what it looks like,
Suggestion Container Div -
Question
Looking at the documentation I can not seem to find a way to handle this. How can I apply styles to this suggestion box so it will break the text as the screen gets smaller?
The templated slot does not appear to respond to word-wrap styling (but other styles like color do work).
One way, a bit hacky, is to use a <label style="white-space: pre-wrap;"> to get a muli-line label, and use a directive to set the height.
template
<md-autocomplete v-model="value" :md-options="colors">
<label>Color</label>
<template slot="md-autocomplete-item" slot-scope="{ item, term }">
<span class="color" :style="`background-color: ${item.color}`"></span>
<label v-wrapit
style="white-space: pre-wrap;"
>{{item.name}}</label>
</template>
<template slot="md-autocomplete-empty" slot-scope="{ term }">
<label v-wrapit
style="white-space: pre-wrap;"
>No colors matching "{{ term }}" were found</label>
</template>
directive
<script>
export default {
name: 'AutocompleteTemplate',
directives: {
wrapit: {
inserted: function (el, binding, vnode) {
el.style.height = `${el.scrollHeight}px`
el.style.color = 'red'
console.log('el', el.scrollHeight, el.offsetHeight, el)
}
}
},
data: () => ({
value: null,
colors: [
{ name: 'Aqua blue blue blue blue blue', color: '#00ffff' },
{ name: 'Aquamarine blue', color: '#7fffd4' },
]
}),
style
This style sets overall list width. It is non-scoped because the menu appears outside <div id="app">
<style>
.md-menu-content {
width: 200px !important;
}
</style>
Here is a CodeSandbox to play with.

Clear data object on route change

I have a data object that contains color item (string), and It's associated with radio input, where I have v-model on t.
I have Add To Cart button, which I display conditionally in template using v-if - so if color wasn't selected, the button won't be visible, otherwise it would be.
The problem is that color always keep one value, and If I switch to another product it would keep the value from previous product, and button would be visible instantly.
Is there a way to clear color string after route changes - there is $route.afterEach but I'm not sure how to use it here.
Thanks.
Code:
<div class="Radio">
<input
type="radio"
id="radio-{{ va.attributes.term_id }}"
name="variation"
value="{{ va.attributes.color_name }}"
v-model="color"
>
<label for="radio-{{ va.attributes.term_id }}"></label>
</div>
Stuff about buttons
<div v-if="color">
<button #click="addToBag" class="Btn Btn--primary Btn--expanded">Add to Bag</button>
</div>
<div v-else>
<button class="Btn Btn--primary Btn--expanded" disabled>Add to Bag</button>
</div>
There is data related to JS
data() {
return {
product: [],
shared: State.data,
color: ''
}
}
1.user a global event bus in the entry js
window.eventBus = new Vue();
2.add a event handler to this color picker component , ie in ready()
ready(){
var vm = this
eventBus.$on('clearColorPicker', function(){vm.color = ''})
}
3.trigger the clearColorPicker event in $route.afterEach by
eventBus.$emit('clearColorPicker')
This can help you to trigger some change to certain component during route change.
But I think In your case there is a better way like the changing of product trigger the color picker reset

Angular 2 - Add CSS class from Component event

I'm using Angular 2 with TypeScript and MDL and have the following simple component. Basically it's an input with the button, and then a collection of spans underneath it. So you enter some "tag", then click the button, and the collection is updated.
One issue is when I add the item to collection like this:
this.chips.push(this.chip);
this.chip = '';
Then the input's value becomes blank (as wanted), but the "is-dirty" CSS MDL class on the parent div is not removed. So what I want to do is to simply remove the class from DIV element in Component's template.
How do I query DOM in Angular2 and manipulate it (add/remove CSS classes)?
I was trying to play with Renderer, but no luck so far.
Note: I can't bind this "is-dirty" class because MDL javascript updates it manually, when text is being entered in the input.
The code:
import {Component, View, Directive, Input, ElementRef, Renderer} from 'angular2/core'
import {NgIf, NgFor} from 'angular2/common'
#Component({
selector: 'chipCollection'
})
#View({
directives: [NgIf, NgFor],
template: `
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="chip" [(ngModel)]="chip" (keyup.enter)="addChip()">
<label class="mdl-textfield__label" for="chip">{{ label }}</label>
<label class="mdl-button mdl-js-button mdl-button--icon aa-mdl-input-button">
<i class="material-icons" (click)="addChip()">add</i>
</label>
</div>
<div *ngIf="chips.length > 0">
<span *ngFor="#chip of chips" class="aa-chip">{{ chip }}</span>
</div>
`
})
export class ChipCollection {
#Input() chips: Array<string> = ["chip1", "chip2"];
#Input() label: string;
chip: string;
private renderer: Renderer;
private element: ElementRef;
constructor(renderer: Renderer, element: ElementRef){
this.renderer = renderer;
this.element = element;
}
addChip() {
if(this.chip) {
this.chips.push(this.chip);
this.chip = '';
debugger; //testing here...
// what I need to do here is to find the DIV element and remove its "is-dirty" CSS class, any ideas?
this.renderer.setElementClass(this.element.nativeElement, 'is-dirty', false);
}
}
}
EDIT:
Here are two "dirty" classes. I guess ng-dirty is added by ngModel. However I need to also remove parent's "is-dirty". I think it's MDL class.
See screenshot:
what I need to do here is to find the DIV element and remove its "is-dirty" CSS class
Don't do that on element. That class is set by ngModel and you should use ngModel's API to set its .dirty state to false.
More
http://blog.ng-book.com/the-ultimate-guide-to-forms-in-angular-2/
Yeah, the is-dirty is set by mdl, not by ngModel (as suggested by the other answer). I don't know why this didn't come up when I tried to solve the opposite issue a few months ago: Register model changes to MDL input elements, but binding to the class list, https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngClass, might be a better way:
for your example:
<div class="mdl-textfield mdl-js-textfield mdl-text-field--floating-label"
[class.is-dirty]="false">
or for mdl-tabs:
<div class="mdl-tabs__panel"
[class.is-active]="someIsActiveBoolean">
or for mdl-checkboxes:
<label class="mdl-radio mdl-js-radio mdl-js-ripple-effect"
[class.is-checked]="val == 1">

Categories