How to open and close the detailtemplate programmatically? - kendo-ui

I have 3 columns, and therefore 3 cells in which I have a button.
At the click of the button I would like to open / close the detail template
Without having the + icon on the left
HTML:
<kendo-grid-column field="info" title="info">
<ng-template kendoGridCellTemplate let-dataItem>
<button mat-button (click)="clickInfoCell()">
</button>
</ng-template>
</kendo-grid-column>
<ng-template kendoGridDetailTemplate let-dataItem let-rowIndex="rowIndex">
//DETAIL TEMPLATE BODY
</ng-template>
TS:
public onCellClick(event: CellClickEvent){
this.myEvent= event;
}
//toggleTemplate
public clickInfoCell(){
//Close previous template
//Open detail template
}
thanks

There's actually a built-in mechanism with dedicated directives for this purpose.
Template:
<kendo-grid
[kendoGridBinding]="gridView"
[kendoGridExpandDetailsBy]="expandDetailsBy"
[(expandedDetailKeys)]="expandedDetailKeys"
></kendo-grid>
Component:
public expandedDetailKeys: any[] = [1];
public expandDetailsBy = (dataItem: any): any => {
return dataItem.ProductID;
};
You can read about the mechanism here.
You can see a working demo in StackBlitz here.

Related

How to implement dropdown list in a grid for kendoUI for angular. Can't find any documentation

Something like this. But this example is for kendoUI for jquery. I need documentation for kendoUI for angular.
I do it in my application. Here is a simple version of it:
HTML Template
<kendo-grid [data]="someData" [height]="750">
<kendo-grid-column field="LaborType" title="Task" width="120">
<ng-template kendoGridCellTemplate let-dataItem>
{{ GetLaborTypeDesc(dataItem.LaborType)?.LaborTypeDesc }}
</ng-template>
<ng-template kendoGridEditTemplate>
<kendo-dropdownlist [defaultItem]="{LaborTypeID: null, LaborTypeDesc: 'Select a task...'}" [data]="LaborTypes"
textField="LaborTypeDesc" valueField="LaborTypeID" [valuePrimitive]="true">
</kendo-dropdownlist>
</ng-template>
</kendo-grid-column>
</kendo-grid>
Typescript
LaborTypes: Array<{ LaborTypeDesc: string, LaborTypeID: number }> = [];
public GetLaborTypeDesc(id: number): any {
return this.LaborTypes.find(x => x.LaborTypeID === id);
}
I have Add, Edit, and Delete commands in my grid that involves a form not seen here. I populate the LaborTypes object array in my ngOnInit function as well, so the user has options to choose in the dropdown.

Is there any way to define maximum and minimum date for a date column of a kendo grid?

I have a kendo grid with in-cell editing and a pair of date columns. I want to specify a maximum and a minimum date for the datepickers inside the cell the user is editing, but it doesn't seem to exist any property for that.
I tried to do it with templates:
<kendo-grid-column field="StartDate" title="Start Date">
<ng-template kendoGridCellTemplate let-dataItem let-rowIndex="rowIndex">
<kendo-datepicker
format="{0:dd/MM/yyyy}"
[(ngModel)]="dataItem"
></kendo-datepicker>
</ng-template>
</kendo-grid-column>
But I'm getting a bunch of errors. How can I make this work?
EDIT: I created a stackblitz example based on one of the in-cell editing examples that I found in the documentation:
https://stackblitz.com/edit/angular-ewvsh5
Here, I discovered that I wasn't specifying the property ngModel has to connect to:
[(ngModel)]="dataItem"
Should be:
[(ngModel)]="dataItem.Date"
Ok, I changed it, but now, when I click on the date cell, instead of appearing a datepicker, it appears a regular input. Please, check this part in the components template, it's where the problem is:
<!-- This doesn't work -->
<kendo-grid-column field="Date" title="Date">
<ng-template
kendoGridCellTemplate
let-dataItem
let-rowIndex="rowIndex"
let-isEdited="isEdited"
*ngIf="editingDateCell"
>
<kendo-datepicker [(ngModel)]="dataItem.Date"></kendo-datepicker>
</ng-template>
<ng-template
kendoGridCellTemplate
let-dataItem
let-rowIndex="rowIndex"
let-isEdited="isEdited"
*ngIf="!editingDateCell"
>
{{ dataItem.Date | date }}
</ng-template>
</kendo-grid-column>
What am I doing wrong?
EDIT II: All the solutions so far show the datepicker inside the cell. That's fine, I know how to do it. The problem is that before the user clicks to edit the cell, that cell must be like a label, when the user clicks on that label, it has to become a datepicker. If the user changes the date and clicks away, the grid has to know that the data has been updated and act accordly. In summary, I need to preserve the in-cell editing behavior.
You need to use the min and max date picker properties. Please refer to this API reference link for date picker min max example.
Also refer to this forum link for an example of a date picker column template.
#Component({
selector: 'my-app',
template: `
<form novalidate #myForm="ngForm">
<kendo-grid
[data]="view | async"
[height]="533"
[pageSize]="gridState.take" [skip]="gridState.skip" [sort]="gridState.sort"
[pageable]="true" [sortable]="true"
(dataStateChange)="onStateChange($event)"
(edit)="editHandler($event)" (cancel)="cancelHandler($event)"
(save)="saveHandler($event)" (remove)="removeHandler($event)"
(add)="addHandler($event)"
[navigable]="true"
>
<ng-template kendoGridToolbarTemplate>
<button kendoGridAddCommand type="button">Add new</button>
</ng-template>
<kendo-grid-column field="ProductName" title="Product Name">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<input [(ngModel)]="dataItem.ProductName" kendoGridFocusable name="ProductName" class="k-textbox" required/>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="date" title="Date" format="{0:yyyy-MM-dd}">
<ng-template kendoGridEditTemplate let-dataItem="dataItem">
<kendo-datepicker
[format]="'yyyy-MM-dd'"
[(ngModel)]="dataItem.date"
[min]="min"
[max]="max"
name="date"></kendo-datepicker>
</ng-template>
</kendo-grid-column>
<kendo-grid-command-column title="command" width="220">
<ng-template kendoGridCellTemplate let-isNew="isNew">
<button kendoGridEditCommand type="button" class="k-primary">Edit</button>
<button kendoGridRemoveCommand type="button">Remove</button>
<button kendoGridSaveCommand type="button" [disabled]="myForm.invalid">{{ isNew ? 'Add' : 'Update' }}</button>
<button kendoGridCancelCommand type="button">{{ isNew ? 'Discard changes' : 'Cancel' }}</button>
</ng-template>
</kendo-grid-command-column>
</kendo-grid>
</form>
`
})
export class AppComponent implements OnInit {
public min: Date = new Date(2018, 2, 10);
public max: Date = new Date(2018, 11, 25);
}
Finally, what I had to do is to use the kendoGridEditTemplate instead of the kendoGridCellTemplate and use [formControl] instead of [(value)] or [(ngModel)]. If you follow the example found in the documentation, and you want to add a custom date column so you have full access to the datepicker's properties, the markup to add is this one:
<kendo-grid-column
field="StartDate"
title="Start Date"
[format]="{ date: 'dd/MM/yyyy' }"
>
<ng-template
kendoGridEditTemplate <!-- Important -->
let-column="column"
let-formGroup="formGroup"
>
<kendo-datepicker
format="dd/MM/yyyy"
[formControl]="formGroup.get(column.field)" <!-- Important -->
[min]="minimumDate"
>
</kendo-datepicker>
</ng-template>
</kendo-grid-column>
To set a minimum and maximum of a datepicker use [min] and [max]. See this demo for an example.
Here's an example code that does what you require:
#Component({
selector: 'my-app',
template: `
<p>Date</p>
<kendo-grid [data]="gridData">
<kendo-grid-column field="ProductID"></kendo-grid-column>
<kendo-grid-column field="ProductName"></kendo-grid-column>
<kendo-grid-column field="date" [format]="{ date: 'long' }">
<ng-template kendoGridCellTemplate let-dataItem let-rowIndex="rowIndex">
<kendo-datepicker [(value)]="dataItem.date" [min]="minDate" [max]="maxDate">
</kendo-datepicker>
</ng-template>
</kendo-grid-column>
</kendo-grid>
`
})
export class AppComponent {
public minDate = new Date(2018, 4, 1);
public maxDate = new Date(2018, 4, 31);
const products = [{
"ProductID": 1,
"ProductName": "Chai",
"UnitPrice": 18.0000,
"Discontinued": true,
date: new Date("2018-05-05T00:00:00-05:00")
}, {
"ProductID": 2,
"ProductName": "Chang",
"UnitPrice": 19.0000,
"Discontinued": false,
date: new Date("2018-05-07T00:00:00-05:00")
}
];
public gridData: any[] = products.map(item => {
item.date = new Date(item.date);
return item;
});
}

Nativescript + Angular2: RadListView binding

I'm attempting to bind an Observable to a RadListView, without using any Page properties, but appear to be doing something completely wrong. The following is a minified version of the code.
The component:
export class WeatherComponent implements OnInit {
public weather : ObservableArray<StringWrapper>;
constructor(private _weatherService : WeatherService) {
this.weather = new ObservableArray<StringWrapper>([]);
this.weather.push(<StringWrapper>{value:'Sunny'});
}
ngOnInit(): void {
this._weatherService.getDummyWeather().subscribe(
item => {
this.weather.push(item);
}
);
}
}
The XML:
<RadListView [items]="weather">
<template tkListItemTemplate let-item="item">
<StackLayout orientation="vertical">
<Label [text]="item.value"></Label>
</StackLayout>
</template>
</RadListView>
The simple data model:
export interface StringWrapper {
value : string;
}
The service:
#Injectable()
export class WeatherService {
public getDummyWeather() : Observable<StringWrapper> {
return Observable.of(<StringWrapper>{value:'Rainy'});
}
}
The service is correctly updating the model but the view is not reflecting the changes, leading me to believe the problem is located in the observable binding.
Any help would be deeply appreciated!
N.B. Checked related questions and none of the solutions helped, i.e. Nativescript RadListView not binding to source property solution causes a build error.
Change the HTML to
<RadListView [items]="weather" >
<template tkListItemTemplate let-item="item">
<StackLayout class="itemStackLayout" >
<Label class="titleLabel" [text]="item.value" textWrap="true" width="100%"></Label>
</StackLayout>
</template>
</RadListView>
Hint: Seems like stringwrapper is not need. You can use below code if it just a string array
<Label class="titleLabel" [text]="item" textWrap="true" width="100%"></Label>

Dynamic GridLayout in nativescript with angular 2

I'm very new to Nativescript and Angular and it's my first stab at mobile app development, so please bear with me.
I'm trying to create a basic app with Angular2 and Nativescript. What I'm trying to do, is to display 3 buttons at the bottom of the screen when the app starts (these are created dynamically on initialisation). Once you click any of these buttons, I want this row to be moved up and a second row of buttons to appear. The transition should be animated.
In the first approach I tried I couldn't add the GridLayout to my DockLayout.
The home.html file
<DockLayout #container stretchLastChild="false">
</DockLayout>
the home.component.ts file snippet
export class HomePage implements OnInit {
#ViewChild("container") container: DockLayout;
constructor(private _router: Router, private page: Page) {
var view = new View();
}
ngOnInit() {
this.page.actionBarHidden = true;
this.page.backgroundImage = this.page.ios ? "res://background_large_file.jpg" : "res://background_large_file";
console.dir(this.container);
//Create the grid layout on the page
var bottomNav = new GridLayout();
//Add the GridLayout Columns
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
//Add the GridLayout Row
bottomNav.addRow(new ItemSpec(1, GridUnitType.auto));
//Create the buttons
var FButton = new Button();
FButton.text = "F";
var BButton = new Button();
BButton.text = "B";
var CButton = new Button();
CButton.text= "C";
//Position the buttons
FButton.set('row', '0');
FButton.set('col', '0');
BButton.set('row', '0');
BButton.set('col', '1');
CButton.set('row', '0');
CButton.set('col', '2');
bottomNav.addChild(FButton);
bottomNav.addChild(BButton);
bottomNav.addChild(CButton);
//Attempt to add to DockLayout container
--This is the part I can't get working
}
}
The other approach I was considering would be to hardcode my template and only show the first row. On tap of one of the buttons in the first row I'd move it up and fade in the second row. I couldn't get the reference to the GridLayouts on the page to allow me to do this. I'm thinking I might need to use "let container = this.container;" but am a bit unsure on this.
The home.html file
<DockLayout #container stretchLastChild="false">
<GridLayout #second_row columns="*, *, *" rows="auto" dock="bottom" class="choices hidden">
<Button text="M" row="1" col="0"></Button>
<Button text="F" row="1" col="1"></Button>
<Button text="D" row="1" col="2"></Button>
</GridLayout>
<GridLayout #initial_row columns="*, *, *" rows="auto" dock="bottom" class="choices initial">
<Button text="F" row="0" col="0" (tap)="showF()"></Button>
<Button text="B" row="0" col="1" (tap)="showB()"></Button>
<Button text="C" row="0" col="2" (tap)="showC()"></Button>
</GridLayout>
</DockLayout>
The home.component.ts file
export class HomePage implements OnInit {
#ViewChild("container") container: DockLayout;
constructor(private _router: Router, private page: Page) {
var view = new View();
}
ngOnInit() {
this.page.actionBarHidden = true;
}
showC() {
let container = <View>this.container;
container.animate({
opacity: 1,
duration:200
});
console.log('in here');
}
}
The final layout I'm aiming for is (any taps on the new row of buttons - #second_row - will navigate off to other pages once I set up on tap events and routing):
<DockLayout #container stretchLastChild="false">
<GridLayout #second_row columns="*, *, *" rows="auto" dock="bottom" class="choices hidden">
<Button text="M" row="1" col="0"></Button>
<Button text="F" row="1" col="1"></Button>
<Button text="D" row="1" col="2"></Button>
</GridLayout>
<GridLayout #initial_row columns="*, *, *" rows="auto" dock="bottom" class="choices initial">
<Button text="F" row="0" col="0" (tap)="showF()"></Button>
<Button text="B" row="0" col="1" (tap)="showB()"></Button>
<Button text="C" row="0" col="2" (tap)="showC()"></Button>
</GridLayout>
</DockLayout>
I would appreciate any help and advice on the subject.
Thanks in advance!
I would suggested you to use NativeScript Angular translate animation. With its help you could move up the first GridLayout and to show the next one. You could review my sample project here .In regard to that you could also review below attached articles.
http://docs.nativescript.org/angular/ui/animation.html
http://docs.nativescript.org/angular/tutorial/ng-chapter-4
http://docs.nativescript.org/angular/tutorial/ng-chapter-0
For your first example to add your new gridlayout to your page:
page.content = bottomNav;
Would do the trick.

NativeScript: how to make view 'transparent' for any user interactions

Is there a way to make a view 'transparent' to any user interactions? For example I have a view (with transparent background) and a button under that view. I want the user could tap the button under that view. If I have a scroller view under that view I want the user interacts with scroller when scroll over that view, so the view doesn't interfere or intercept user's gestures. But only this view should be transparent to user's interactions not its children. So, if I have a button inside that view it behaves normally.
Example XML:
<AbsoluteLayout width="100%" height="100%">
<Button text="Button1" tap="onTap1" />
<GridLayout width="100%" height="100%" backgroundColor="transparent">
<Button text="Button2" tap="onTap2" horizontalAlignment="center" verticalAlignment="center"/>
</GridLayout>
</AbsoluteLayout>
Thank you for your help.
You have multiple approaches to make a view change its color in NativeScript.
For example you can directly change its backgroundColor. Another oiption is to use animation and third option is to use CSS-animation.
Here is a basic example for the first two options.
page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
<StackLayout>
<GridLayout width="300" height="300" id="myGrid" backgroundColor="transparent">
</GridLayout>
<Button text="Tap me" tap="onTap" />
<Button text="Or Tap me" tap="onAnotherTap" />
</StackLayout>
</Page>
page.js
import { EventData } from "data/observable";
import { Page } from "ui/page";
import { HelloWorldModel } from "./main-view-model";
import { GridLayout } from "ui/layouts/grid-layout";
import { Color } from "color";
var myGridView;
export function navigatingTo(args: EventData) {
var page = <Page>args.object;
page.bindingContext = new HelloWorldModel();
// get refference to the view using its id
myGridView = <GridLayout>page.getViewById("myGrid");
}
export function onTap(args:EventData) {
var color = new Color("#FF0000");
myGridView.backgroundColor = color;
}
export function onAnotherTap(args:EventData) {
myGridView.animate({
backgroundColor: new Color("#3D5AFE"),
duration: 3000
});
}
All of the options can be found described in NativeScript documenation

Resources