#Vinay in this TypeScript + AngularJS 1: How to connect enum with select directive? question shows a relatively simple way to get a array for building a select drop-down in angular.
Unfortunately, I try to ape this code and I get errors ... first upon declaring the 'colors' array if I use var or let... (but it works if I don't). Unfortunately, that just moves the error to the next variable declaration in the setup of the for loop. Unfortunately, here, I can't not put in a let or a var.
I'm sure this is simple, but I'm just banging me head and missing it.
enum Color {
Green = <any>"Green",
Red = <any>"Red",
Blue = <any>"Blue"
}
export class ClassName {
colors: string[] = []; // <-- get error here if I declare var or let
for (var item in Color) { // <-- get error here
if (Color.hasOwnProperty(item)) {
this.colors.push(item);
}
}
}
Property declarations belong in the body, but executable code goes in the constructor:
export class ClassName {
colors: string[] = []; // <-- get error here if I declare var or let
constructor() {
for (var item in Color) { // <-- get error here
if (Color.hasOwnProperty(item)) {
this.colors.push(item);
}
}
}
}
Related
I'm new to typescript to bear with me here if this is not how things are supposed to work.
I have a couple of goals in converting this js to ts.
Item = {}
Item.buy = function (id) {}
Item.sell = function (id) {}
I'm trying to get intellisense to autocomplete on Item. either buy or sell. I would also want to use dot notation to create these methods in arbitrary files without putting everything in the initial bracket. So I have something like this:
interface Item {}
const Item: Item = {};
interface Item {
buy?: Function
}
Item.buy = function () {
Item.render()
return "bought"
}
interface Item {
sell?: Function
}
Item.sell = function () {
Item.render()
return "sold"
}
interface Item {
render?: Function
}
Item.render = function () {
return 1
}
The problem here now is that render is an optional property and hence I get this error:
Cannot invoke an object which is possibly 'undefined'.
How can I make ts not check for this error? Since Item is not a class there's only ever going to be 1 item and it'll definitely have the render method, there is not ever going to be an instance where that error checking is useful. Or to put it another way, it's not actually optional, I only set it to be optional to work around const Item: Item = {}; erroring if I don't have it be optional.
Is there a way to let ts know that or use a different pattern in the first place?
SOLUTION 1:
Since you have not defined any method inside Item
interface Item {}
So you can check whether render method exist or not on Item as:
Item.buy = function () {
if(Item.render) Item.render(); // CHANGE
return "bought";
}
SOLUTION 2:
Best solution would be to add type of render on interface Item as:
interface Item {
render: () => void;
}
and then you can use it as:
Item.buy = function () {
Item.render();
return "bought";
}
My inclination here would be to use namespaces instead of an interface to hold these functions. It could look like this:
namespace Item {
export const buy = function () {
Item.render()
return "bought"
}
}
namespace Item {
export const sell = function () {
Item.render()
return "sold"
}
}
namespace Item {
export const render = function () {
return 1
}
}
Then you'd be able to access them the same way, as methods on the singleton Item value:
// elsewhere
console.log(Item.sell()); // "sold"
Note that namespace is a TypeScript specific feature, and nowadays new code is generally encouraged to use modules instead where possible. I don't really know if there's a good way to get this sort of behavior with modules, because the part we're using, merging different things into a common JS value, is not really how modules works. Maybe declaration merging and importing would give this to you, but I don't know.
Anyway, as long as you're okay with a TS-specific feature, then namespace would be an idiomatic way to represent this sort of gradual building of a singleton.
Playground link to code
Apologies if this is a newbie question but I'm learning how to use classes in JavaScript and am running into something I'm not sure is possible. Maybe my syntax is incorrect or there's a better way to go about this.
I have 2 files, each contain a class to construct an object which I'm calling in a third file (my questions are below this sample JS):
file1
import { Second } from "./file2";
class First {
constructor(
parameter1,
parameter2
) {
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
method1() {
this.parameter1 = this.parameter1 + " " + Second.parameterA
return this.parameter1;
}
}
export { First };
file2
class Second {
constructor(
parameterA,
parameterB
) {
this.parameterA = parameterA;
this.parameterB = parameterB;
}
}
export { Second };
file3
import { First } from "./file1";
import { Second } from "./file2";
const first = new First(
"someStringForParameter1",
"someStringForParameter2"
);
const second = new Second(
"someStringForParameterA",
"someStringForParameterB"
);
console.log(first.method1());
The issue is when I call method1() in the third file I get parameter1 but parameterA is undefined in the console. I'm not sure why this is the case. Is this a legal use of imported classes within another class?
I've tried importing the files in different orders (not surprised that didn't work) and moving the the Second class into the same file as the First class. I'm able to console.log() all parameters of the First and Second class from the third file successfully but the method I'm calling only returns "someStringForParameter1 undefined". My goal is for console to state "someStringForParameter1 someStringForParameterA". There are no errors in console and the linter (ESLint) I'm using in VS Code isn't highlighting anything. Any help on this would be much appreciated!
The issue doesn't have anything to do with modules. It's Second.parameterA - that will reference a property directly on the Second class, but no such property exists.
To see it when you reference Second.parameterA, you'd have to do
class Second {
static parameterA = 'something'
or, after the class definition
Second.parameterA = 'something';
But if you want the argument passed into Second to be shown, you'll have to get First to have a reference to that Second instance somehow - perhaps with a parameter to method1, like:
method1(secondInstance) {
this.parameter1 = this.parameter1 + " " + secondInstance.parameterA
return this.parameter1;
}
console.log(first.method1(second));
class First {
constructor(
parameter1,
parameter2
) {
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
method1(secondInstance) {
this.parameter1 = this.parameter1 + " " + secondInstance.parameterA
return this.parameter1;
}
}
class Second {
constructor(
parameterA,
parameterB
) {
this.parameterA = parameterA;
this.parameterB = parameterB;
}
}
const first = new First(
"someStringForParameter1",
"someStringForParameter2"
);
const second = new Second(
"someStringForParameterA",
"someStringForParameterB"
);
console.log(first.method1(second));
Or pass in the instance when calling First's constructor, or somehing like that.
I do not know why I am being asked to place commas where periods should be. const cards is saying I can't set it there when this is the way to set a variable.
I have tried to change from const to let and restructure .forEach syntax
class TabLink {
constructor(tabElement) {
// assign this.tabElement to the tabElement DOM reference
this.tabElement = tabElement;
// Get the `data-tab` value from this.tabElement and store it here
this.tabData = this.tabElement.dataset.tab;
// We need to find out if a user clicked 'all' cards or a specific category. Follow the instructions below to accomplish this task:
// Check to see if this.tabData is equal to 'all'
if (this.tabElement.dataset.tab === "all") {
// If `all` is true, select all cards regardless of their data attribute values
this.cards = document.querySelectorAll(".card");
} else {
// else if `all` is false, only select the cards with matching this.tabData values
this.cards = document.querySelectorAll(`.card[tabData="${this.tabData}"]`);
}
// Map over the newly converted NodeList we just created in our if statement above. Convert each this.cards element into a new instance of the TabCard class. Pass in a card object to the TabCard class.
this.cards = Array.from(this.cards).map((card) => new TabCard(card));
// Add a click event that invokes this.selectTab
this.tabElement.addEventListener('click', () => { this.selectTab() });
}
selectTab() {
// Select all elements with the .tab class on them
// const tabs = document.querySelectorAll();
const tabs = document.querySelectorAll('.tab');
// Iterate through the NodeList removing the .active-tab class from each element
Array.from(tabs).forEach((tabs))
link.classList.remove(".active-tab");
}
//
// Select all of the elements with the .card class on them
const cards = document.querySelectorAll('.card');
// Iterate through the NodeList setting the display style each one to 'none'
cards.forEach(card => {
card.style.display = "none";
})
// Add a class of ".active-tab" to this.tabElement
this.tabElement = ".active-tab";
// Notice we are looping through the this.cards array and invoking selectCard() from the TabCard class. Just un-comment the code and study what is happening here.
this.cards.forEach(card => card.selectCard());
console.log(this.cards);
}
'const' can only be used in a .ts file.
';' expected.
',' expected.
',' expected.
Unexpected token. A constructor, method, accessor, or property was expected.
Declaration or statement expected.
I want to assign to a random color of the array. It actually first works but then I get an error message: "ExpressionChangedAfterItHasBeenChecked" I even see how it one second before the message come the color of the chip change very fast. So it somehiw works... How can I fix this issue. (I also added the code and funcs for the createion of the chips) I know that I first have to initialize it. But I dont know how to accomplish that, so it would be nice if you could write directly in my code. I have been trying many things, but nothing worked.
HTML
<ion-chip [color]="color[getRandomInt(color.length)]" class="chip" #chip *ngFor="let tag of tagName">
TS
export class Tag {
color = ["ok", "nice","awesome","danger","white"];
colorSelected = null;
tag: string;
constructor(values: Object = {}) {
Object.assign(this, values);
}
ngAfterViewInit() {
this.colorSelected = this.color[this.getRandomInt(this.color.length)];
}
}
...
color: string [] = ["ok", "nice","awesome","danger","white"]
tagName: Tag[] = [];
...
add(): void {
let id = this.tagName.length + 1;
this.tagName.push(new Tag({ tag: "tag" + id }, ));
}
remove(tag: Tag) {
let id = this.tagName.indexOf(tag);
this.tagName.splice(id, 1);
}
getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
If you add a console.log in getRandomInt method you will see it is called many many times which is not good.
Instead I would suggest to assign the color to your object once, (you need to add color to your Tag class as well)
something like this
add(): void {
let id = this.tagName.length + 1;
this.tagName.push(new Tag({ tag: "tag" + id, color: this.color[this.getRandomInt(this.color.length)] }, ));
}
then in html it will be like
<ion-chip [color]="tag.color" class="chip" #chip *ngFor="let tag of tagName">
What you need to do is ensure you're only setting the color once, so you need to move the method call out of the template. Instead, you should do:
this.tagName = this.tagName.map((tag) => tag.color = this.color[this.getRandomInt(this.color.length)]);
Then in the HTML:
<ion-chip [color]="tag.color"
I'm using babylonjs library and created a "Building" class with typescript. Using typescript for the whole thing BTW. I create this new "Building" from my main game.ts "Game" class and when trying to access a member of "Building" I get "undefined" variable errors. However this only happens within another class method but seems work correctly in the constructor. I'm assuming it has something to do with the "this" scoping in javascript/typescript. I have tried modifying the function by doing:
Create = ...(...)=> {
...
I have tried creating the variable via:
private rect: = () => Rectangle
but this still does not work
Is this really an issue with "this" scoping because nothing seems to be working.
Below I marked exactly where this variable works and where this doesnt work.
class Building {
private rect : Rectangle
private buildingMesh:string[]
private buildingId:string
constructor(rect: Rectangle, id:string) {
this.rect = rect
console.log("TL in b const: " + this.rect.topLeft.x) // <--- This works here
this.buildingId = id
}
Create(scene:BABYLON.Scene) {
BABYLON.SceneLoader.ImportMesh(this.buildingId, "models/","tree.babylon", scene, function (newMeshes) {
var idx = 0
console.log("TL in b: " + this.rect.topLeft.x) // <--- this gives me undefined
var wall =newMeshes[0].createInstance(this.buildingId + idx)
wall.position.x = this.rect.topLeft.x
wall.position.y = this.rect.topLeft.y
this.buildingMesh.push(this.buildingId + idx)
idx++
});
}
}
I guess that you are almost there. Arrow function ( => ) syntax is what we need, but even on the BABYLON.SceneLoader.ImportMesh call:
BABYLON.SceneLoader
.ImportMesh(this.buildingId, "models/","tree.babylon", scene,
function (newMeshes) {
...
// here we do not have this kept by TS for us
});
we should use
BABYLON.SceneLoader
.ImportMesh(this.buildingId, "models/","tree.babylon", scene,
(newMeshes) => {
...
// here the magic would happen again
// and compiler will keep this to be what we expect
});