Angular 2 Kendo UI Grid display foreign key text - html

I have a cart like system for a PO using Angular 4 and ASP.NET CORE WEB APIs. I have two main calls getting the PO details and then all of the "Cart" line items for a specific PO. I have related funding category ids and project ids in the table because finance people need to adjust these. I need to get the Kendo UI grid to display the text of the related foreign key. I will be implementing edit soon hence the ng-template but working on having the non-edit view with the text value displayed. The office id is a simple integer and the office data returns id and name in JSON
HTML
<kendo-grid
[data]="view | async"
[pageSize]="gridState.take"
[skip]="gridState.skip"
let-dataItem="dataItem">
<kendo-grid-column field="productName" title="Product Name">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<input [(ngModel)]="dataItem.productName" name="productName" class="k-textbox" />
</ng-template>
<kendo-grid-column>
<kendo-grid-column field="officeId" **<!--Fix here??-->** title="Office">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-dropdownlist name="officeName"
[data]="office"
[textField]="'name'"
[valueField]="'id'"
[valuePrimitive]="true"
[(ngModel)]="dataItem.officeId">
</kendo-dropdownlist>
</ng-template>
<kendo-grid-column>
...
</kendo-grid>
Typescript
public po: PO = {
id: 0,
poNumber: '',
...
}
public cart: Cart[] = [{
id: 0,
productName: '',
officeId: 0,
...
}];
office: any[];
...
constructor(
private route: ActivatedRoute,
private router: Router,
private cartService: CartService, //has cart items
private referenceService: ReferenceService //has office FK and text value
#Inject(CartEditService) editServiceFactory: any){
route.params.subscribe(p => {
this.po.id = +p['id'] || 0;
});
this.cartEditService = editServiceFactory();
this.view = this.cartEditService.map(data => process(data, this.gridState));
}
ngOnInit(){
//todo implement check for new po or existing
this.cartService.getPo(this.po.id).subscribe(po => this.po = po);
this.cartEditService.read(this.po.id);
this.referenceService.getOffices().subscribe(office => this.office = office)
...
}
//To Do add the action handlers for grid
Added solution thanks to topalkata
HTML
<kendo-grid-column title="Office">
<ng-template kendoGridCellTemplate let-dataItem>
{{getOfficeNameById(dataItem.officeId)}}
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-dropdownlist name="officeName"
[data]="office"
[textField]="'name'"
[valueField]="'id'"
[valuePrimitive]="true"
[(ngModel)]="dataItem.officeId">
</kendo-dropdownlist>
</ng-template>
<kendo-grid-column>
Typescript
public office: Reference[] = [{
id: 0,
name: ''
}];
...
public getOfficeNameById(id: number){
return this.office.find(office => office.id === id).name;
}
Thanks again!! I don't have enough rep to up vote answer.

You can use Cell template and bind the content to a method that will return the Office name by ID (the ID is available as part of the dataItem, accessible in the template), e.g.:
<kendo-grid-column field="CategoryID" title="Category">
<ng-template kendoGridCellTemplate let-dataItem>
{{getCategoryNameById(dataItem.CategoryID)}}
</ng-template>
</kendo-grid-column>
...
public categories = [{
CategoryID: 1,
CategoryName: 'Beverages'
}, {
CategoryID: 2,
CategoryName: 'Condiments'
}, {
CategoryID: 7,
CategoryName: "Produce",
}, {
CategoryID: 6,
CategoryName: "Meat/Poultry",
}, {
CategoryID: 8,
CategoryName: "Seafood",
}];
public getCategoryNameById(id: number) {
return this.categories.find(c => c.CategoryID === id).CategoryName;
}
EXAMPLE

Related

Mat-Option selected option based on patch value from api subscription

In my case i have several controls with mat-autocomplete with mat-options. in which the same form is handled for new entry creation and edit an existing entry data. So i have tried the following methods to implement the mat-option selected by default or mat-option is active based on the data fetched from the server side api, but the approach is not working. Please refer my code below provide a solution to make this case working.
HTML
<mat-form-field floatLabel="never" class="dropdown-arrow-icon">
<span class="material-icons">arrow_drop_down</span>
<input type="text" matInput formControlName="UOM" [matAutocomplete]="uomSelect" (keyup)="onUomKeyUp($event.target.value)">
<mat-autocomplete #uomSelect="matAutocomplete" [displayWith]="selectedDropdownForUom">
<mat-option *ngFor="let uom of uomOption" [value]="uom.value">
{{ uom.value }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
TYPESCRIPT
uomData = [
{ id: 1, value: "Parts", key: "PARTS" },
{ id: 2, value: "G/KG", key: "KG" },
{ id: 3, value: "L/Gal", key: "L" },
{ id: 4, value: "oz/lb", key: "OZ" },
{ id: 5, value: "%", key: "PERC" },
]
machineDetails;
ngOnInit(): void {
this.patchValue();
}
public selectedDropdownForUom = (uomData?: string): string => {
return uomData;
};
public onUomKeyUp(value: string): void {
if (value === "") {
// eslint-disable-next-line no-self-assign
this.uomOption = this.uomData;
} else {
this.uomOption = this.uomData.filter((item) => new RegExp(value, "gi").test(item.value));
}
}
private selectDefaultAutocompleteData(selectedUOM) {
find(this.uomSelect.options.toArray(), (uomOption) => uomOption.value === selectedUOM? .value)?.select();
}
public patchValue(){
this.httpClient.get("https://conversion.intil.io/units/45").subscribe(data =>
{this.machineDetails = data;
const selectedUOM = this.uomData.find(uom => uom.id === this.machineDetails.UoMID
this.experimentForm.patchValue({ UOM: selectedUOM.value});
this.selectDefaultAutocompleteData(selectedUOM);
})
}
RECIEVING OUTPUT
actual output recieving
EXPECTED OUTPUT
expected output

Angular table filter clearing group

I have 2 filters on a user list. A user can select a group containing members and use the search filter to search by last name. When the user backspaces a user to look for another, this resets the groups to all users. I need this to only show the users in the selected group.
TS
updateFilter(event) {
const val = event.target.value.toLowerCase();
const temp = this.temp.filter(function (d) {
return d.lastName.toLowerCase().indexOf(val) !== -1 || !val;
});
this.rows = temp;
if (this.table) {
this.table.offset = 0;
}
}
onGroupSelected($event) {
const groupId = $event.target ? $event.target.value : $event;
if (groupId === 'none') {
this.rows = this.temp;
} else {
const groupUsers = this.groupUserMap.get(groupId);
if (groupUsers) {
this.rows = this.temp.filter((serviceUser) =>
groupUsers.includes(serviceUser.id));
} else {
this.rows = [];
}
}
// #ts-ignore
this.userSelections = this.userSelections ? this.userSelections : {};
this.userSelections.groupId = groupId;
localForage.setItem(this.username, this.userSelections);
}
HTML
<input
type='text'
class="form-control w-200px"
placeholder='Search by Last Name...'
(keyup)='updateFilter($event)'
/>
<select class="form-control w-200px" (change)="onGroupSelected($event)">
<option value="none">All service users</option>
<option *ngFor="let group of groups"
[value]="group.id"
[selected]="userSelections.groupId === group.id">
{{group.name}}
</option>
</select>
You can use ngModel with tow-way binding, to save and manipulate search filters:
<select
class="form-control w-200px"
[(ngModel)]="selectedGroup"
(change)="onGroupSelected()"
>
<option value="none">All service users</option>
<option *ngFor="let group of groups" [value]="group.id">
{{ group.name }}
</option>
</select>
<input
type="text"
class="form-control w-200px"
placeholder="Search by Last Name..."
[(ngModel)]="search"
(keyup)="updateFilter()"
/>
And in order not to lose your users table you can create a copy which will be filtered and displayed.
public initialUsers = [
{ id: 100, groupId: 1, name: 'foo' },
{ id: 101, groupId: 2, name: 'bar' },
{ id: 102, groupId: 1, name: 'john' },
{ id: 103, groupId: 2, name: 'doe' },
{ id: 104, groupId: 2, name: 'baaar' },
{ id: 105, groupId: 1, name: 'fooodoe' },
];
public filteredUsers = [];
ngOnInit(): void {
this.filteredUsers = this.initialUsers;
}
Here is a demo on stackblitz, I used a list to go fast but It's just display. You just have to replace <ul> <li></li> </ul> by your <table> ... </table>
If you would want to take an observable way of doing this, then I would suggest to make a form of your controls, ooooor just use 2 form controls instead. I chose form here as it wraps it up nicely with both form controls (search and dropdown):
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
search: [''],
group: [0] // "all" option as initial id
})
}
Then we would listen to when the form value changes and assign the filtered data to a variable, here named filteredUsers$.
this.filteredUsers$ = this.form.valueChanges.pipe(
startWith(this.form.value), // to trigger initially
// 'this.users' refers to your original users array
map((value: any) => {
// 'all' option is chosen, just filter based on search
if (value.group === 0) return this.users.filter(x => x.lastName.toLowerCase().includes(value.search))
// filter by group and search
return this.users.filter(x => {
return (x.groupId === value.group) && (x.lastName.toLowerCase().includes(value.search.toLowerCase()))
})
})
)
That is it, then we just iterate filteredUsers$ in the template:
<tr *ngFor="let user of filteredUsers$ | async">
Of course we need the form in the view and it would look like this:
<form [formGroup]="form">
<input
type="text"
placeholder="Search by Last Name..."
formControlName="search"
/>
<select formControlName="group">
<option *ngFor="let group of groups" [ngValue]="group.id">
{{ group.name }}
</option>
</select>
</form>
Here is a DEMO with the above code

Console error <ng-select>: No value accessor for form controll with name:

I have added ng-select in my project for multiselect-searchable-grouping dropdown. I have followed the steps from https://ng-select.github.io/ng-select#/grouping, but unfortunately getting:
ERROR Error: No value accessor for form control with name: 'optProject'
I am using angular 6
Please see my code as below:
I have added NgSelectModule to module.ts
html
<ng-select formControlName="optProject"
[items]="projects"
bindLabel="title"
bindValue="id"
groupBy="subprojects"
[multiple]="true"
[(ngModel)]="selectedProjects">
<ng-template ng-optgroup-tmp let-item="item">
{{item.title}}
</ng-template>
<ng-template ng-option-tmp let-item="item">
{{item.title}}
</ng-template>
</ng-select>
component.ts
import { FormGroup, FormControl} from '#angular/forms';
.
.
.
public selectedProjects;
public projects;
ngOnInit() {
this.selectedProjects = [];
this.projects = [
{
id: 'p1',
title: 'Project A',
subprojects: [
{ title: 'Subproject 1 of A', id: 's1p1' },
{ title: 'Subproject 2 of A', id: 's2p1' },
]
},
{
id: 'p2',
title: 'Project B',
subprojects: [
{ title: 'Subproject 1 of B', id: 's1p2' },
{ title: 'Subproject 2 of B', id: 's2p2' },
]
}
]
}
projectForm = new FormGroup({
optProject: new FormControl(null),
});
While searching on Google, I found that this kind of error occurs while adding formCorntrolName to labels/div instead of input controls. But is the case here, with ng-select ? Whats wrong with my code...! Thanks in advance...
Just to add, I have added ngDefaultControl to ng-select, because I found: Third party controls require a ControlValueAccessor to function with angular forms (What is ngDefaultControl in Angular?), but still no luck!!! The error goes but no content display within <ng-select>

Translations reflect after click on combo, where combo options are filled dynamically in ngOnInit in Angular 6

I have an autocomplete drop-down to which I bind list of ViewOption (my class)
class ViewOption {
name: string;
ordinal: number;
label: string
}
I create list of viewoption in ngOnInit by calling getViewOptionList,
getViewOptionList(): ViewOptions {
const viewoptions = new ViewOptions();
for (const enumMember in ViewOptionEnum) {
if (ViewOptionEnum.hasOwnProperty(enumMember)) {
const viewoption = new ViewOption();
const ordinalValue = parseInt(enumMember, 10);
if (ordinalValue >= 0) {
viewoption.oridinal = ordinalValue;
viewoption.label = ViewOptionEnum[enumMember].toLowerCase();
this.translate.get(viewoption.label).subscribe(msg => viewoption.name = msg);
viewoptions.push(viewoption);
}
}
}
return viewoptions;
}
<lis-auto-complete-dropdown placeholder="select view" automationid="worklistMaster_ddl_selectView"
[options]="ViewOptions" [(selectedItem)]="selectedViewOption"
(itemSelected)="selectViewOption($event)">
<ng-template let-item="item">
<span title="{{ item }}">
<span>{{ item.name }}</span>
</span>
</ng-template>
</lis-auto-complete-dropdown>
Translation reflect only when user clicks on autocomple dropdown on UI. How to make it fill before user taking any action?

Searching in multiple columns angular2

I am using a textarea for searching a table, all in an angular2 application with bootstrap.
My code was searching in one column, now I want the search term to be looked up across two columns, and the user will see all the matched table rows highlighted.
HTML:
<input class="form-control" id="search" [(ngModel)]="searchString" type="text" placeholder="Search by Book ID.." style="float:right;"
value="">
<tr *ngFor="let books of bookDetails |jobsearch:{ bookId: searchString} : false | paginate: { itemsPerPage: 50, currentPage: p };">
The other column that I want to be searched is 'bookName'.
Custom pipe example:
#Pipe({
name: 'booksearch'
})
export class BookSearchPipe implements PipeTransform {
transform(items: Array<any>, filter: {[key: string]: any }): Array<any> {
return items.filter(item => {
let noMatch = Object.keys(filter)
.find(key => item[key] !== filter[key]);
return !noMatch;
});
}
}
Usage:
<tr *ngFor="let books of bookDetails |booksearch:{ bookId: searchString, bookname: searchString }”>