Using WinJS with Angular2 - winjs

Trying to use a property to configure a WinJS control from within Angular2, so far I couldn't find a solution, e.g. this code below is throwing 'Can't bind to 'dataWinOptions' since it isn't a known property of the '' element'.
#View({
template: `<div id="rating" data-win-control='WinJS.UI.Rating' [data-win-options]='jsonRating'></div>`
})
class MyRating {
rating: number;
get jsonRating() {
return '{averageRating: ' + this.rating + '}';
}
constructor() {
this.rating = 1.5;
}
}
Any hint?

#ericdes about your last comment I think this would be the best option. Assuming you have Nth WinJS controls
Consider the following code. I'm specifying differents values for the averageRating property in options.
<winjs-control [options]="{averageRating: '1.5', someMoreOptions : 'x'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.4', differentOptionsForThisOne :'Z'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.3'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.2'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.1'}"></winjs-control>
// more and more...
The component will read this options property and will pass it to the view. Forget about the directive, it isn't necessary after all.
We pass options through attr.data-win-options since it isn't a property of div but an attribute.
#Component({
selector : 'winjs-control',
properties : ['options']
})
#View({
template : `<div data-win-control="WinJS.UI.Rating" [attr.data-win-options]="jsonRating"></div>`,
})
class WinJSComponent implements OnInit, AfterViewInit {
constructor() {}
// We specify onInit so we make sure 'options' exist, at constructor time it would be undefined
// And we stringify it or otherwise it will pass an object, we need to convert it to a string
onInit() {
this.jsonRating = JSON.stringify(this.options);
}
// We process WinJS after view has been initialized
// this is necessary or 'data-win-options' won't be fully processed
// and it will fail silently...
afterViewInit() {
WinJS.UI.processAll();
}
}
Here's a plnkr for this case.
That's one option and IMHO I think this is the easiest one. Another one, having the same HTML content, would be to communicate the parent with its children and I haven't tested your case with that approach.

Related

React Typescript take part of props

I'm relatively new to React & TypeScript. I'm trying to extend an existing component by making a wrapper around it, but I am having issues trying to add my own values to the properties.
I want it so that the default properties (in a predefined type "TextFieldProps" from the MUI library) carry over, and I can add my own values to it. I'm doing this by making my own type as such:
type PinnableTextFieldProps = TextFieldProps & {
pinned: boolean;
onPin: (newValue: boolean) => void;
};
I then use it as follows:
export function PinnableTextField(props: PinnableTextFieldProps) {
return (
<TextField
{...props}
InputProps={{}}
/>
);
}
This works fine, except that the "pinned" and "onPin" values are copied over to the TextField while they shouldn't be (TextField doesn't know what they are, and an error is printed to the console because of it)
I tried to cast it using ...(props as TextFieldProps) but it still included the properties in the spread.
How would I properly split up the props spread to only include all values inside of the TextFieldProps type, so excluding the 2 values I added?
I hope someone can point me in the right direction!
Many thanks!
You could do something like this:
export function PinnableTextField(props: PinnableTextFieldProps) {
const { pinned, onPin, ...rest } = props; // <= Splitting props into pinned, onPin and all other properties into rest
// Use pinned, onPin here
// Pass the rest of the props down to the TextField
return (
<TextField
{...rest}
InputProps={{}}
/>
);
}

transform value if falsy

I'm validating my DTOs with the class-validator package. I enabled the transformation via
app.useGlobalPipes(
new ValidationPipe({
transform: true,
}),
);
in my main.ts file as described in the docs
https://docs.nestjs.com/techniques/validation#transform-payload-objects
I have a optional configuration field in my DTO. This field should be transformed to an empty object if it doesn't exist. The transformation decorator is described here
https://docs.nestjs.com/techniques/serialization#transform
I was hoping to come up with this solution:
export class MyDTO {
#IsObject()
#IsOptional()
#Transform(configuration => configuration || {})
public configuration: object;
}
When I call my API route
#Post()
public create(#Body() myDTO: MyDTO): void {
console.log(myDTO);
}
with an empty body, so without the field configuration my MyDTO instance is
{}
although I would expect it to be
{
configuration: {}
}
What is wrong or what am I missing? I tried to debug the code and it never hits the transformation function. So the #Transform doesn't trigger.
Update
It seems I have to do this
#IsObject()
#IsOptional()
#Transform(configuration => configuration || {}) // will be used if the field exists
public configuration: object = {}; // will be used if the field doesn't exist
The initial value will be used if you pass in an empty body. The transformation only runs if you pass in the field but assign a value like null to it.
Gonna go ahead n put this here too: why not just let typescript manage the default value with setting the value like
export class MyDTO {
#IsObject()
#IsOptional()
public configuration: object = {};
}
That way, if you get a value, great, and if it isn't there, class-transform will put the correct value there.
Looks like there is more discussion about solutions here.

Call LitElement method from outside the element

I have a simple LitElement component like so:
class MyElement extends LitElement {
constructor() {
super();
}
customMethod(data) {
// do something with the passed parameter
}
render() {
return html`<div id="element"></div>`;
}
}
customElements.define('my-element', MyElement);
And I want to be able to call that customMethod from outside of my element.
So for example if I add the element to web page like so:
<my-element></my-element>
I then want to be able to add some JavaScript to the page and call that customMethod.
I tried:
var element = document.getElementById('element');
element.shadowRoot.customMethod('example data');
But it claims it's not available... How can I call a method on an instance of LitElement?
You don't need to use shadowRoot in the call :
var element = document.getElementById('element');
element.customMethod('example data');
but you need to be able to locate your element
<my-element id='element'></my-element>
I had a very similar problem and the existing answers did not seem to fix it. The reason for my issue was caused by the fact that LIT Element scripts are exported as modules, meaning that they are loaded and executed after the initial DOM has been parsed. So if you are using a script to access the public method - make sure that it is also in a module (or you can alternatively place the code into an appropriate timeout).
So when defining an element in LIT Element as follows:
#customElement('my-element')
export class MyElement extends LitElement {
#state()
text = '';
customMethod(data) {
this.text = 'Custom method was called!';
}
render() {
return html`<div id="element">${this.text}</div>`;
}
}
And adding a script in my index.html page:
<my-element id='element'></my-element>
<script type="module">
const element = document.getElementById('element');
element.customMethod();
</script>
Make sure that the script tag contains type="module". Otherwise you will see the following error in the console: Uncaught TypeError: element.customMethod is not a function
Link to LIT Element Playground.
Also, here is a great article that explains how scripts are loaded in detail.

Ionic 3 Passing objects using navparams to 3 pages

I am an absolute beginner and am self taught using Ionic3
I have a problem that is driving me wild that I hope someone can assist on.
I have a master-detail-detail setup where the following happens:
Master page has a list of reports (taken from a JSON file), click on that report and it takes to a details page, click on that and it opens up another page with even more details, without having to define all the individual parts.
All I want to do is simply pass the whole object from the second page to the third page so that I can use its parameters again
master page (Report),
Second page (Reportmenu),
Third page (GenOverview)
So passing between master and second page is fine and works as it should using navparams (not shown here) but I want to use that same object and pass all the data again to the 3rd page.
I thought it would be just as simple as assigning it to a new variable and then passing that again using navparams but I get undefined
export class ReportmenuPage {
name: any;
overallscore: any;
reportdate: any;
coach: any;
age: any;
TechOverall: any;
TactOverall: any;
PhysOverall: any;
PsychOverall: any;
Logo: any;
data2: any;
constructor(public navCtrl: NavController, public navParams: NavParams,
public postsService: Posts, private toastCtrl: ToastController) {
this.overallscore = this.navParams.get('OverallScore');
this.reportdate = this.navParams.get('ReportDate');
this.name = this.navParams.get('Name');
this.coach = this.navParams.get('Coach');
this.age = this.navParams.get('Age');
this.TechOverall = this.navParams.get('TechOverall');
this.TactOverall = this.navParams.get('TactOverall');
this.PhysOverall = this.navParams.get('PhysOverall');
this.PsychOverall = this.navParams.get('PsychOverall');
this.Logo = this.navParams.get('Logo');
console.log(this.navParams.data);
this.data2 = this.navParams.data;
}
myClickHandlerOverview(data2) {
this.navCtrl.push(GenOverviewPage, data2);
So this is all fine and gives an expected output
So all I want to do now is get this to the GenOverviewPage
Here is the Reportmenu.html
<ion-item (click)="myClickHandlerOverview(data2)" color="primary">
<ion-icon name="arrow-dropright" item-end></ion-icon>
GENERAL OVERVIEW
</ion-item>
And Finally the bit that doesn't work
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { ReportmenuPage } from '../reportmenu/reportmenu';
#IonicPage()
#Component({
selector: 'page-gen-overview',
templateUrl: 'gen-overview.html',
})
export class GenOverviewPage {
constructor(public navCtrl: NavController, public navParams: NavParams) {
let data3 = this.navParams.data;
console.log(data3); //this shows as {}
}
ionViewDidLoad() {
console.log('ionViewDidLoad GenOverviewPage');
}
}
I am pretty certain I am going to get ridiculed as I think this relates to my lack of understanding of objects/ arrays and how they are passed, but I have searched high and low and cannot grasp what I am doing wrong.
and why console.log(data3) shows the second empty array but as you can see I can transfer it fine between page 1 and 2
Many thanks
In your second page, you are saving the data in a local variable that is contained within your constructor.
let data2 = this.navParams.data;
You need to save it in the class variable to be passed to your myClickHandlerOverview. Change it to:
this.data2 = this.navParams.data;
Also may need to change datatype from array to object or any.
data2: any;
A better approach seems to be to store it in a common provider.
Very simple in the end thanks to Suraj
I was missing a this!
myClickHandlerOverview(data2) {
this.navCtrl.push(GenOverviewPage, this.data2);

Angular2 object property typed as "number" changes to string

I have a very simple Angular2 app running locally. I'm using a service to send an instance of an object to a webservice API. The API validates JSON against a schema, and ID's must be numbers (i.e. NOT quoted in the JSON).
My problem is that when I try to send the object to the webservice, my ID field has quotes around it, even though it's typed to be a number in Typescript.
The behaviour is only observed when the nameproperty of the object contains "special characters".
I've tested and found that it doesn't seem to be the JSON.stringify I use — please see code below.
The app is written in Typescript.
Unit class:
export class Unit {
id: number; // This is the problem child
name: string; // This is the string that can contain special characters
short: string;
triggers_plural: number;
is_headline: boolean;
}
Method to save:
My code for saving an instance of Unit to the webservice:
updateUnit(unit: Unit): Promise<Unit>
{
var objSend = {unit_data: [unit]}; // Webservice expects an array of units
console.log(objSend.unit_data[0].id === 2); // Yields false when ID is 2
console.log(objToReturn); // Logs ID to verify it is 2 when testing
// Code for actual request
return this.http.put(`${this.unitUrl}/${unit.id}`, JSON.stringify(objSend),{headers:this.headers})
.toPromise()
.then(()=> unit)
.catch(this.handleError);
}
When running the code and calling the method, the console will log that the ID is NOT equal when the Unit object's name property contains special characters.
Example without special characters (no problem, id is a number):
Example WITH special characters (eek! Id is a string!):
The updateUnit method is called from my unit-detail component where you can edit a unit:
export class UnitDetailComponent implements OnInit {
unit: Unit; // this.unit later on
constructor(
private unitService: UnitService,
private route: ActivatedRoute
){}
ngOnInit(): void
{
this.route.params.forEach((params: Params) => {
let id = +params['id']; // The routing will give id to look for
this.unitService.getUnit(id)
.then(unit => this.unit = unit); // Here the unit is instanciated in the first place
});
}
save(): void
{
this.unitService.updateUnit(this.unit).then(this.goBack); // Here is the call to updateUnit method
}
}
It's bound to an input in the template:
<div *ngIf="unit">
<div>
<label>Edit unit</label>
<div>
<input type="text" [(ngModel)]="unit.name" />
</div>
</div>
<button class="btn btn-default" type="button" (click)="save()">Save</button>
</div>
Maybe the problem arises already when the two-way data binding is filling in the name property when you write something in the <input> but I don't understand how the type of the id can change?
Link to github repos of the whole project: https://github.com/djoike/ng2-cookbook/tree/master-so
A small solution is to cast the field to any , and then convert it to a number using parseInt(), I faced a similar problem just today
for more info about casting, check the Type assertions section here

Resources