Change mat-select default value - angular-material

I can't change the default value or my mat-select item even if i set value property in the proper way.
ngOnInit() {
this.vehicles$ = this.vehicleService.getVehicles();
if (this.fuelId > 0) {
this.editClicked();
this.fuelService.getFuel(this.fuelId).subscribe(data => (
......
this.vehicleList.patchValue(data.vehicle),
console.log(this.vehicleList.value),
......
));
}
This is the log output:
fuel-add-edit.component.ts:55 {id: 20, plate: "random1", fuels: Array(0), journeys: Array(0), maintenances: Array(0)}
and this is my mat-select
<mat-form-field class="float-left w-75">
<mat-label>Scegli Automezzo</mat-label>
<mat-select [formControl]="vehicleList" [errorStateMatcher]="matcher">
<mat-option *ngFor="let vehicle of vehicles$ | async" [value]="vehicle">
{{ vehicle.plate }}
</mat-option>
</mat-select>
</mat-form-field>
I would like to have the proper vehicle already selected when editing an item instead of "Scegli automezzo"

You must pass in a [compareWith] function if the value is an object. See the answer in Angular Material Select - Default Value - Reactive Forms for a full explanation of the problem.

Related

mat-select Maximum call stack size exceeded in select with 800+ options

I am using mat-select to populate an array of objects in the dropdown. there is an option to select and deselect all the values by clicking on ALL. For that, I am emptying the form control assocaiated with it and for selecting all, populating the form control with all the ids. For Small Data , its working fine..For data more than 800, when i am emptying the formcontrol on unselect of al, It throws an error(Maximum call stack size exceeded
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:200)
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:130)
)
component.html
<mat-select [formControl]="courseFormControl" multiple aria-label="Course Name"
[(ngModel)]="selectedCourseIds">
<mat-option *ngFor="let course of courseList"
(onSelectionChange)="onCourseSelect($event,courseFormControl)">
{{course.courseName}}
</mat-option>
</mat-select>
component.ts
onCourseSelect(event, form) {
if(event.isUserInput) {
let listCourseId = [];
//data contains the total list
data.map((val,ind) => {
if(val.selected === true){
listCourseId.push(val.courseId)
}
});
this.selectedCourseIds = listCourseId;
this.courseFormControl.setValue(listCourseId);
}
Error is being thrown , on deselcting all
core.js:15724 ERROR RangeError: Maximum call stack size exceeded
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:200)
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:130)
at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next (Subscriber.js:76)
at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:53)
at EventEmitter.push../node_modules/rxjs/_esm5/internal/Subject.js.Subject.next (Subject.js:47)
at EventEmitter.push../node_modules/#angular/core/fesm5/core.js.EventEmitter.emit (core.js:13499)
at MatOption.push../node_modules/#angular/material/esm5/core.es5.js.MatOption._emitSelectionChangeEvent (core.es5.js:2803)
at MatOption.push../node_modules/#angular/material/esm5/core.es5.js.MatOption.deselect (core.es5.js:2595)
Issue is when we directly assign values to the mat-select component. So instead of pushing value directly into the formControl, use the mat-select apis to deselect the option.
Try the following:
component.html
<mat-select [formControl]="courseFormControl" multiple aria-label="Course Name" [(ngModel)]="selectedCourseIds" #matSelect>
<mat-option *ngFor="let course of courseList" (onSelectionChange)="onCourseSelect($event, courseFormControl)">
{{ course.courseName }}
</mat-option>
</mat-select>
component.ts
#ViewChild('matSelect') matSelect;
deSelectAll(){
this.matSelect.options.forEach((x) => {
x.deselect();
});
}

Mat-table reset mat-sort to initial state

I have a mat-table in which I have several sort-able column. I can set the initial sort of the table with the matSortActive and matSortDirection properties on the mat-table. Using this, the arrow indicating the sorting direction in the headers is displayed correctly.
Now when I am trying to reset the sorting to the initial state by using a button the sorting is correctly reset. However, the arrow in the header is not updated. So the arrow is still displayed in the header of the previous sorted column.
How can I get the arrow to be displayed in the initial state again?
My table and reset button in HTML:
<button mat-button mat-raised-button (click)="removeFilters()" class="reset-button">Verwijder filters</button>
<mat-table #table [dataSource]="dataSource" matSort (matSortChange)="sortData($event)" matSortActive="comp_name_sort" matSortDirection="asc">
<ng-container matColumnDef="assetName">
<mat-header-cell *matHeaderCellDef mat-sort-header="comp_name_sort">Systeem</mat-header-cell>
<mat-cell *matCellDef="let asset"> {{asset.comp_name}} </mat-cell>
</ng-container>
<ng-container matColumnDef="softwareName">
<mat-header-cell *matHeaderCellDef mat-sort-header="soft_name_sort">Software</mat-header-cell>
<mat-cell *matCellDef="let asset"> {{asset.soft_name}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
My ts file:
export class AssetsComponent implements OnInit {
#ViewChild(MatSort) sort: MatSort;
assets: Asset[];
displayedColumns = ['assetName', 'softwareName',];
dataSource = new MatTableDataSource<Asset>(this.assets);
constructor( private assetsService: AssetsService) { }
ngOnInit() {
this.getAssets();
}
getAssets(): void {
this.assetsService.getAssets().subscribe(
assets => {
this.assets = assets;
this.dataSource = new MatTableDataSource<Asset>(this.assets);
}
);
}
sortData(event): void {
this.assetsQueryService.setSorts(event);
this.getAssets();
}
removeFilters() {
this.sort.active = 'comp_name_sort';
this.sort.direction = 'asc';
this.sort.sortChange.emit({active: 'comp_name_sort', direction: 'asc'});
this.assetsQueryService.removeFilters();
this.getAssets();
}
}
The sorting column and direction are passed to the assetsService because sorting is done in the backend (because of server-side pagination, not shown here). This is all working well, also with the reset button. The only problem is the displayed arrow.
So in summary, how do I reset the sorting arrow that is displayed in the table to it's initial state programmatically?
Any help would be appreciated.
For anyone else looking for an answer for this particular problem. As ericbea pointed out in the comments, Angular is aware of this problem and an issue is still open on github about this: https://github.com/angular/components/issues/10242.
The only workaround that I found that worked for me is also listed there. It's something like this:
this.sort.sort({ id: null, start: 'desc', disableClear: false });
this.sort.sort({ id: 'comp_name_sort', start: 'asc', disableClear: false });
(this.sort.sortables.get('comp_name_sort') as MatSortHeader)._setAnimationTransitionState({ toState: 'active' });
The first line is used to clear the sort in case the column you want the sort to is currently the active column.
I don't know if you still need help with this, but I encountered the same issue and here's my solution. After you reset the active and direction for your sort, add the following line:
this.sort._stateChanges.next();
This should remove the sort arrow from your UI. Hopefully this helps!
Maybe you should try ChangeDetectorRef, it could just be that the view is not being refreshed properly after the change.
This is usually useful when the view needs a refresh after a change that doesn't require user interaction with the view.
Just inject the change detector in your component like this:
constructor( private changeDetector: ChangeDetectorRef ) { }
and then use it just after reset the sort state programatically to force the view to detect changes like this:
this.changeDetector.detectChanges();
Hope it helps ;)
I know this question is old but in case anyone else comes looking...
To set the arrow in the sort header, this worked for me:
import { MatSort } from '#angular/material/sort'
Then, in the class:
#ViewChild(MatSort) myMatSort:MatSort
Finally, you can do this:
myMatSort.sort({
id: 'column-name', // The column name specified for matColumnDef attribute
start: 'asc', // Can be 'asc', 'desc' or null,
disableClear: false
})
See the sort method and MatSortable interface here.
Using Angular 6+,
if you want sort-reset DESCENDING:
defaultSortStart: 'desc', defaultSortDirection: 'asc'
if you want sort-reset ASCENDING:
defaultSortStart: 'asc', defaultSortDirection: 'desc'
// Reset sort: FIELD_NAME/DESC.
let defaultSortStart: ('asc' | 'desc') = 'desc';
let defaultSortDirection: ('asc' | 'desc') = 'asc';
let defaultSortField = this.matListColumnName.FIELD_NAME;
this.sort.direction = defaultSortDirection;
this.sort.sort({
id: defaultSortField,
start: defaultSortStart,
disableClear: true
});
// Reset sort: FIELD_NAME/ASC.
let defaultSortStart: ('asc' | 'desc') = 'asc';
let defaultSortDirection: ('asc' | 'desc') = 'desc';
let defaultSortField = this.matListColumnName.FIELD_NAME;
this.sort.direction = defaultSortDirection;
this.sort.sort({
id: defaultSortField,
start: defaultSortStart,
disableClear: true
});

How to get input box (filterbox) values from ui-grid column header

I'm trying to get the input box values present in the ui-grid column header.
I'm trying to use my own filtering function which requires to get values from multiple input boxes in the column header.
$scope.gridOptions.columnDefs = [
{
field: 'name',
displayName: 'Name',
headerCellTemplate: '<input type='text' disallow-spaces placeholder='Enter to Search' ng-model='testCol' id='Enter to Search'>},
];
You can give your custom filter to the columnDef like this:
$scope.gridOptions.columnDefs = [
{
field: 'name',
displayName: 'Name',
headerCellTemplate: '<input type='text' disallow-spaces placeholder='Enter to Search' ng-model='testCol' id='Enter to Search'>,
filter: {
type: uiGridConstants.filter.INPUT,
condition: myCustomFilterFunction
},
];
and your filtering function will look something like this:
function myCustomFilterFunction(term, value, row, column){
//do something here
}
uiGrid will call the myCustomFilterFunction everytime the filter input changes. The parameter term refers is what you were looking for. It's the value present in the input box. Parameter value refers to the value of the column for the current row. Parameters row and col give you access to the current rowEntity and column.
myCustomFilterFunction should return a boolen. If the returned value for a row is true then the row is displayed otherwise not.

Free Text Entry in Angular Material mdAutoComplete

I want my angular material autocomplete to be a list of suggestions but not requirements. However I'm not sure how to implement as their is no clear example from the Angular Material docs.
In the example below my model is $ctrl.item.category
Clearly the example below is wrong, as my model is linked to md-selected-item, but this only works if I select an item. I want the user to be able to free enter the text if the item is not in the list. Basically how autocomplete already works in most browsers.
I see plenty of questions on how to disable this, but they are not trying to disable so much as clean up the left over text when an item is not selected. In these cases when an item is not selected then the model value is null, but text is left in the input.
I want the text left int he input to be the model value if the person does not select (or a match is not made).
md-autocomplete(
md-floating-label="Category Name"
flex="50"
md-input-name="category"
md-selected-item="$ctrl.item.category"
md-search-text="catSearch"
md-items="category in $ctrl.categories"
md-item-text="category"
md-min-length="0"
md-select-on-match=""
md-match-case-insensitive=""
required=""
)
md-item-template
span(md-highlight-text="catSearch" md-highlight-flags="^i") {{category}}
My options ($ctrl.categories) is an array of strings ['Food','Liqour'] and I wan the user to be able to use one of those or free enter Tables as their choice.
In this case you should link md-search-text to your model.
If you want to implement fuzzy search you have to write the filter method yourself. Look at this example:
template:
<md-autocomplete
md-items="item in $ctrl.itemsFilter()"
md-item-text="item.label"
md-search-text="$ctrl.query"
md-selected-item="$ctrl.selected"
>
<md-item-template>
<span md-highlight-text="$ctrl.query">{{item.label}}</span>
</md-item-template>
<md-not-found>
No item matching "{{$ctrl.query}}" were found.
</md-not-found>
<div ng-messages="$ctrl.myValidator($ctrl.query)">
<div ng-message="short">Min 2 characters</div>
<div ng-message="required">Required value</div>
</div>
</md-autocomplete>
controller:
var items = [ ... ];
ctrl.itemsFilter = function itemsFilter() {
return ctrl.query ? filterMyItems(ctrl.query) : items;
};
ctrl.myValidator = function (value) {
return {
short: value && value.length < 2,
required : value && value.length < 1,
};
};
then you just need to add filterMyItems method to filter your items
To improve the answer of #masitko, I have implemented the filter in a way, that it adds the query to the filtered list. So it becomes selectable and a valid option. So it's possible to make the autocomplete a suggestion box.
I'm using ES6 in my projects. But it should be easily adaptable to ES5 code.
myFilter() {
if (!this.query) return this.items;
const
query = this.query.toLowerCase(),
// filter items where the query is a substing
filtered = this.items.filter(item => {
if (!item) return false;
return item.toLowerCase().includes(query);
});
// add search query to filtered list, to make it selectable
// (only if no exact match).
if (filtered.length !== 1 || filtered[0].toLowerCase() !== query) {
filtered.push(this.query);
}
return filtered;
}

Set selected of Paper-Tabs

I am trying to select a paper-tab based on it's data-id attribute. I have the element but I cannot changed to selected property of the inner_tabview.
I have a Polymer:
<paper-tabs id="inner_tabview" noink="true">
<template repeat="{{item in tabNames}}">
<paper-tab data-id="{{item['id']}}"><h3>{{item['name']}}</h3></paper-tab>
</template>
</paper-tabs>
And some Dart code behind it:
selectTab(itemId) {
PaperTab item = shadowRoot.querySelector("paper-tab[data-id='" + itemId + "']");
print('Selecting: ' + itemId + ', text:' + item.text);
PaperTabs tabView = shadowRoot.querySelector('#inner_tabview');
tabView.selected = item; // This doesn't work
}
Changing the selected using an integer (index) works, but I don't know what the index should be.
Only thing I can currently think of is finding all paper-tab elements and get the index of the correct element in that List. But that sounds a bit silly to do so.
Any other way?
I don't know why querySelector doesn't work but selected expects an index by default not an element.
if you specify the valueattr attribute you can use other attributes than the index.
<paper-tabs id="inner_tabview" noink="true" valueattr="data-id">
<template repeat="{{item in tabNames}}">
<paper-tab data-id="{{item['id']}}"><h3>{{item['name']}}</h3></paper-tab>
</template>
</paper-tabs>
then
tabView.selected = itemId;
should work as well

Resources