kendo masked text box mask change does not work - kendo-ui-angular2

I tried one of the example given on the MaskedTextBox component page but while assigning the mask or rules on ngOnInit, it gives error :
Expression has changed after it was checked. Previous value:
'undefined'. Current value: '__ ___'
How to achieve changing mask property on the fly?
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template:
<kendo-maskedtextbox
name="value1"
[(ngModel)]="value"
[mask]="mask"></kendo-maskedtextbox>
})
export class AppComponent {
public value: string;
public mask: string;
public maskValidation: boolean = true;
ngOnInit(){
this.mask = "99 999";
}
}
Attached plunker:
Kendo Masked Text Box

This seems like a bug to me. You should definitely open an issue in the GitHub repository.

Related

Layout/style for a repeatable child via slots

I'm trying to create a drop down control, where the user of the control knows the shape of the options/selected item and can display/style them appropraitely. I have some code that looks something like the following:
interface DropDownVM {
selected?: object;
options: object[];
}
interface MyPageVM {
animal: DropDownVM;
}
interface Animal {
name: string;
age: number;
species: string;
}
#customElement('drop-down')
export class DropDown extends LitElement {
#property() public vm: DropDownVM;
render() {
return html`<slot name="selected-template"></slot><slot name="options-template"></slot>`;
}
}
#customElement('my-page')
export class MyPage extends LitElement {
#property() public vm: MyPageVM;
render() {
return html`
<drop-down .vm=${this.animal}>
<!-- Something needs to go in here -->
</drop-down>
`;
}
}
Notes:
I cannot make the above interface generic.
I've only put in the code that I believe is relevant, I've cut out
all of the functionality from DropDown.
So my question is, how can I style and layout the selected and options without DropDown knowing the shape of Animal or whatever other model I use for DropDown in future. In addition, I don't want to map over options, I simply want to provide a template for laying out one option and DropDown do the rest. MyPage knows that its Animal so it makes sense for the layout/styling to be in there.
I have a feeling I should be using a <template> but I can't quite see the end target with the lit html templates involved.

Jest branch code coverage - problem with nullable values

At first glance problem looks really easy, but unfortunately I have a problem with covering test scenario when nullable getter is null. Considering the sample below:
#Component({
selector: 'sample-form-test',
templateUrl: './sample-form-test.component.html'
})
export class SampleFormTestComponent implements ngOnInit {
form?: FormGroup;
get name(): FormControl | null {
return this.form?.get('name') as FormControl;
}
constructor(private formBuilder: FormBuilderService) {}
ngOnInit() {
this.form = this.formBuilder.build();
}
It is always underlined in a code coverage report that the question mark inside the getter above is not covered by the tests. Even if I try to cover it by checking directly whether is that getter null right after a component creation:
describe('create SampleFormTestComponent', () => {
let spectator = Spectator<SampleFormTestComponent>; // from #ngneat/Spectator
const createComponent = createComponentFactory({ // from #ngneat
component: SampleFormTestComponent,
providers: MockProvider(FormBuilderService, {
build: () => {
new FormGroup({name: new FormControl})
}
}
});
it('should create', () => {
spectator = createComponent();
expect(spectator).toBeTruthy();
expect(spectator.component.name).toBeNull();
});
}
It doesn't help at all and branch coverage is always lowered by that nullable getters. The worst scenario is when I have them multiple inside the class - in that scenario it is not possible to meet code coverage requirements. Do you know how can I easily solve that problem?
UPDATE
The easiest way is to initialize form in a constructor – it will allow to access the form properties (even if those are empty) without necessity to check if those values are null ot not.
#Component({
selector: 'sample-form-test',
templateUrl: './sample-form-test.component.html'
})
export class SampleFormTestComponent implements ngOnInit {
form: FormGroup;
get name(): FormControl {
return this.form.get('name') as FormControl;
}
constructor(private formBuilder: FormBuilderService) {
this.form = this.formBuilder.build();
}
Here is the updated Stackblitz solution

kendo numeric text box ng model binding creating a never ending loop

i am trying to modify the value of the numeric text box but while updating it creates a never ending loop. the model binding should update only once
Component below
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `<kendo-numerictextbox
[spinners]="showButtons"
[restrictDecimals]="true"
[round]="false"
[decimals]="decimals"
[format]="c2"
[ngModel]="value"
(ngModelChange)="onValueChange($event)"></kendo-numerictextbox> `
})
export class AppComponent {
public showButtons: boolean = false;
public format: string;
public decimals: number = 2;
public value: number = 0;
onValueChange(value: string) {
this.value = value + ' USD';
alert(value);
}
}
attached plnkr : http://plnkr.co/edit/kjl7e1wrFJGmpKa008cT
Note that the component works with digits only and the value that you are passing is invalid.

Angular 2 SimpleChanges Object throws error at first npm start

In my angular 2 application there is a component containing an array of objects that is passing the chosen (clicked) one to it's direct child component. This does display the data more detailed. I'm using the "SimpleChanges" feature to watch in this child component if the object given changed to make another http request to get the related comments from a database.
If I try to build it with npm I get an error, saying :
app/displayEntry.component.ts(23,41): error TS2339: Property 'entry' does not exist on type 'SimpleChanges'
If I just comment this part out, start npm and finally put it in there again and save it, there is no Problem anymore ( no erro and it works ).
My question is, is there a way to work around this behavior and can this cause any trouble later I don't foresee or should I just ignore it? Thanks for your help
Parent component:
import { Component, OnInit } from '#angular/core';
import { entry } from './Objekte/entry';
import { entryService } from './entry.service'
#Component({
templateUrl: 'app/Html_Templates/displayLastEntrys.template.html'
})
export class displayLastEntrys implements OnInit{
public entrys : entry[];
private entryChoosen: boolean;
private ChoosenEntry : entry;
constructor ( private entryservice : entryService){
this.entryChoosen = false;
}
ngOnInit() : void {
this.getData();
}
getData() {
this.entryservice.getFirstEntrys().then(entrys => this.entrys = entrys);
}
entryClicked(ent: entry){
this.entryChoosen = true;
this.ChoosenEntry = ent;
}
leaveEntry () {
this.entryChoosen = false;
}
voted( upordown : boolean ) {
}
}
Child component:
import { Component, Input, Injectable, OnChanges , SimpleChanges, Output, EventEmitter} from '#angular/core';
import { entry} from './Objekte/entry';
import { entryService } from './entry.service';
import { comment } from './Objekte/comments';
#Component({
selector: 'display-entry',
templateUrl: 'app/Html_Templates/displayEntry.template.html'
})
export class displayComponent implements OnChanges{
#Input() public entry : entry;
public comments : comment[];
private changecounter : number;
constructor(private service : entryService) {
this.changecounter = 0;
}
ngOnChanges(changes : SimpleChanges){
this.service.getComments(changes.entry.currentValue.id)
.then(com => this.comments = com )
.catch();
this.entry.viewed++;
// To implement :: change database
}
votedUp () : void {
this.entry.votes ++;
// To implement :: change database
}
votedDown () : void {
this.entry.votes --;
// To implement :: change database
}
}
The accepted solution is suboptimal for TypeScript, as you're defeating the type system.
SimpleChanges does not have an entry property, so the compiler quite rightly balks. The solution is to treat the changes object as an array:
ngOnChanges(changes : SimpleChanges){
if (changes['entry']) {
this.service.getComments(changes['entry'].currentValue.id)
}
}
Then you can continue to strongly type the ngOnChanges method.
To make the compiler not complain just change your method definition for parameter one from SimpleChanges to any:
ngOnChanges(changes: any) {
//...
Maybe it's changed a lot now but this works these days
import {Component, Input, OnChanges, SimpleChanges} from '#angular/core';
import {ConfigModel} from './config.model'
#Component({
selector: 'selector',
templateUrl: './template.html',
styleUrls: ['./styles.scss']
})
export class BlaComponent implements OnChanges {
#Input() config: ConfigModel;
ngOnChanges(changes: SimpleChanges): void {
if (changes.config && changes.config.currentValue) {
let config = <ConfigModel>changes.config.currentValue;
// do more
}
}
}
I myself got the compile error because i wasn't using .currentValue after calling changes.config
If you are completely dependent on the IDE's auto-completion, make sure to actually use SimpleChanges instead of just SimpleChange. A very thing to be overlooked at.

Angular 2 how to pass variable from parent component to router outlet

All the tutorials and answers that I have found show only how to pass a variable from parent component to child component using inputs but what is this child component is contained within the router outlet and not directly in the parent template ??
e.g:
Main component
#Component({
selector: 'my-app',
template: `
Main page
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES]
})
#RouteConfig([
{ path: '/contact', name: 'Contact', component: ContactComponent},
])
export class AppComponent{
public num:Number = 123;
}
#Component({
selector: 'contact-page',
template: 'contact page'
})
export class ContactComponent{
public num:Number;
}
So in this example the main component template contain a router outlet where the child contact component will be rendered but how can I get variable "num" value in the child component evaluated inside a router outlet from the parent app component ??
I just stumbled over this question, here is how I have solved similar issues.
I would use a service to solve this. Then it is possible for all children and the parent to set the property, and the changes are propagated out for all subscribers. First I would create a service with a private BehaviorSubject which have a public getter and setter, to encapsulate ReplaySubject and only return Observable:
private _property$: BehaviorSubject<number> = new BehaviorSubject(1);
set property(value: number) {
this._property$.next(value);
}
get property$(): Observable<number> {
return this._property$.asObservable();
}
The reason for using new BehaviorSubject(1), is to set the initial value to 1, so there is something to subscribe to.
In the parents onInit, I would se the default value of property (num):
private _propertySubscribtion: Subscription
ngOnInit() {
// set default value to 5
this._componentService.property = 5;
// If property is updated outside parent
this._componentService.property$.subscribe(p => {
this.property = p;
});
}
ngOnDestroy() {
this._propertySubscribtion.unsubscribe();
}
In one or more of of the child components, it possible to subscribe for changes:
private _propertySubscribtion: Subscription
ngOnInit() {
this._propertySubscribtion = this._componentService.property$.subscribe(p => {
this.property = p;
});
}
ngOnDestroy() {
this._propertySubscribtion.unsubscribe();
}
And if some child or parent updates the property:
updateProperty() {
// update property
this._componentService.property = 8;
}
All subscribers will know about it.
Currently you can't bind to components added by the router. Use a shared service instead (there are tons of examples here on SO already) to pass data from or to the added component or to subscribe to events.
See also this issue https://github.com/angular/angular/issues/4452 especially this comment https://github.com/angular/angular/issues/4452#issuecomment-153889558
If your data is immutable, you can use RouteData.
#Component({...})
#RouteConfig([
{ path: '/contact', name: 'Contact', component: ContactComponent, data: {num: 123}},
])
export class AppComponent {
}
#Component({...})
export class ContactComponent {
public num:Number;
constructor(data: RouteData) {
this.num = data.get('num');
}
}
Could be useful to pass some configuration options you don't want to hardcode in child routes. But if you're expecting data to change, you'll need some other method.

Resources