Angular4: Component.name doesn't work on production - javascript

So I've been facing this weird issue but I'm not sure if it's a bug or it's me missing something here.
So I have a component called TestComponent and in the AppComponent I have a button when I click on it I get the name of the TestComponent by doing this TestComponent.name.
AppComponent:
import { TestComponent } from './test/test.component';
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
template: '<button (click)="showName()">Show</button>'
})
export class AppComponent {
showName(){
console.log('component name: "' + TestComponent.name + '"');
}
}
As you can see by clicking on the button I want to get the TestComponent's name which basically "TestComponent"
The problem is that this works well on development mode and if I call ng build too works fine and this is what I get in the console:
And when I call ng build --prod, to compress files and reduce their size, I get this result:
Is this normal ?!

Function name contains actual function name. If the function is minified to one-letter name, it loses its original name. It should never be relied on in applications that can possibly be minified at some point i.e. every client-side application. There may be exceptions for Node.js.
name is read-only in some browsers and and thus can't be overwritten. If a class needs identifier, it should be specified explicitly under different name:
class Foo {
static id = 'Foo';
...
}
Here is a related question.

Yes, this is normal since angular-cli/webpack and other tools are changing the class name by minifying the JavaScript code. So after minification in production build, you'll always only see one letter or something similar.
Don't use this name in your logic because it will break in production.

I just answered a related question here
To workaround mangling class names, you can create a function and check if it is equal to the class you want to test. In this case for TestComponent, you could use this:
getType(o: any): string {
if (o === TestComponent)
return 'TestComponent';
}
And calling it will print the Component name:
console.log(getType(TestComponent)); // will print 'TestComponent'

The best way I found was to use a library that renames the class at compile time, works similar to the C # nameof.
nameof<MyInterface>();
Result: "MyInterface"
https://github.com/dsherret/ts-nameof

Related

Working with native dynamic ES2020 modules in TypeScript (without Node.js) and typing things

I have a TypeScript application working with es16 modules, most of them are imported statically. I want to use a (validator-) module now that is only imported in debug mode. It's all working, but I don't understand how to type things so that I get code completion and error-checking.
in my main class in main.ts I have:
...
if(debug){
import('./validator.js').then((module) => this.validate(module))
}
...
the validate method looks like that:
private validate(module):void{
new module.Validator(dataToValidate);
}
validator.js contains:
export class Validator{
coonstructor(data:MyDatatype){
stuff going on here...
}
}
what I would like to know/do is:
in the validate method:
private validate(module:someMeaningfulType){...}
and also I'd like to import the Validator class, without actually importing it.
if I wrote
import {Validator} from './validate.ts'
at the top of main.ts I would load the file regardles of I need it, which defeats the whole point of dynamic imports.
I might try to whrite a type declartaion for module and Validator in main.ts, but even if that wouldn't conflict somehow, I would have to manually keep it in sync with the actual module, which is not what I want - obviously.
I might miss something obvious, but I cannot find out what. I find id hard to search for the (pure) use of native es2020/2022 modules with Typescrit, as there is so much information about node-modules etc. overshadowing it.
You can actually use import with typeof to get the type of the imported module:
private validate(module: typeof import("./validator.js")) { ... }
Alternatively, you can use a type-only import, which will be erased in the output:
import type * as ValidatorModule from "./validator.js";
// ...
private validate(module: ValidatorModule) { ... }

Ember required component arguments

How can I require a parameter in an ember component.
E.g.:
class MyComponent extends Component {
get my_value() {
return this.args.my_argument + 1
}
}
I want the component to throw an error when the component is instantiated and not when the function is called.
The only way I can think of is like this:
class MyComponent extends Component {
constructor(owner, args) {
super(owner, args)
assert(!!args.my_argument, "MyComponent requires my_argument")
}
...
}
This however seems a bit tedious and does not look right :/
EDIT:
It would be even better if I could specify the type of the argument.
I would say that you are on the right track with your assert. If you want to make sure that it is of the right type as well you can always assert with a typeof.
class MyComponent extends Component {
constructor(owner, args) {
super(owner, args)
assert("MyComponent requires my_argument", typeof args.my_argument === "string")
}
...
}
What to remember however is that asserts are only available during development builds, it is removed from any production builds. So if you need this check in your production build you'll have to write your own assertion method.
Assertions are removed from production builds, so they can be freely added for documentation and debugging purposes without worries of incuring any performance penalty. However, because of that, they should not be used for checks that could reasonably fail during normal usage.
https://api.emberjs.com/ember/release/functions/#ember%2Fdebug/assert

How to design a global class for storing settings in Angular?

I have added a class like this.
export class Settings{
public data: string = "blopp";
}
When I try to access the data, it seems that the field I'm trying to assign that value to sees the class Settings itself but it doesn't recognize the data thingy.
How do I redesign the class to provide settings for other components?
I've read about #Output decorator but since I won't be binding to the values, it seems not the correct approach. I've made sure that the class is imported and recognized withing the component that's supposed to consume it. I've also tried the corresponding exposure but using a function in the class with settings - the same, failed result.
If you're using angular-cli and going to store in this class environment specific settings - you already have built in support for this.
Put the setting into environment.ts. For example:
export const environment = {
production: false,
someSetting: 'foo',
};
Then it can be consumed from anywhere within the app:
import { environment } from "../environments/environment"; //fix according to your project structure
#Injectable()
export class SampleService {
private foo = environment.someSetting;
}
Here you can find more info on how to add more environments and build you project with specific environment settings.
Your best bet for storing global settings is to use a service. The code for that looks like this:
import { Injectable } from '#angular/core';
#Injectable()
export class DataService {
serviceData: string;
}
I have a blog post about this here: https://blogs.msmvps.com/deborahk/build-a-simple-angular-service-to-share-data/
And a plunker here: https://plnkr.co/edit/KT4JLmpcwGBM2xdZQeI9?p=preview
Also, the #Output decorator is only for communication between a child component and a parent component where the child is nested within the parent.

Angular2 not working with basic javascript library

There seems to be an error in angular2-polyfills.js when getting a Javascript library (with its related Typescript typings) working in Angular 2 in the Browser.
It's a weird problem: I seem to be able to build and run when there is only 1 line of code from the library, but when I enter a 2nd line, it builds, but doesn't run?!?.
The library is AutobahnJS. It's a single .js file and the exact same library is designed to work in both the Browser and Node.
It works perfectly in both the Browser and Node as a normal (non-angular2) javascript library.
I have it working in Node using Typescript and it's related Typings (There is an example test at the Definitely Typed github page)
I have followed the basic Angular 2 Quickstart for Typescript and got it working no problems.
HERE is the code and output which DOES work. Note the last line in testAutobahn() is commented out:
import {Component} from 'angular2/core';
import autobahn = require('autobahn');
#Component({
selector: 'my-app',
template: `
<h1>Project: {{_projectName}}</h1>
`
})
export class AppComponent {
constructor() {}
private _projectName : string = "EXAMPLE7";
testAutobahn() {
var options: autobahn.IConnectionOptions =
{ url: 'wss://demo.crossbar.io/ws', realm: 'realm1' };
//var connection = new autobahn.Connection(options); // <- THIS line commented out, app runs fine
}
}
HERE is the code and output which does NOT work. Note the last line in testAutobahn() is left in:
import {Component} from 'angular2/core';
import autobahn = require('autobahn');
#Component({
selector: 'my-app',
template: `
<h1>Project: {{_projectName}}</h1>
`
})
export class AppComponent {
constructor() {}
private _projectName : string = "EXAMPLE7";
testAutobahn() {
var options: autobahn.IConnectionOptions =
{ url: 'wss://demo.crossbar.io/ws', realm: 'realm1' };
var connection = new autobahn.Connection(options); // <- THIS line left in, app not run!
}
}
The only difference is the commenting/uncommenting of that line.
The error seems to come from angular2-polyfills.js and system.src.js
I have used 'tsd' to install all the typings. They all look correct and my editor Atom has intellisense on the autobahn.* types just fine.
Possible issues?:
Using AutobahnJS v0.9.9 but the Typings seem to be for v0.9.6
-> I can't see this being a real problem
When building, Typescript emits the Javascript code, but it does give an error:
"Subsequent variable declarations must have the same type. Variable
'$' must be of type 'cssSelectorHelper', but here has type
'JQueryStatic'."
-> I have resolved this error by simply commenting out the line /* declare var $: JQueryStatic; */ in the jquery.d.ts file (I'm not using JQuery anyway)
Using Typescript v1.7.5, Angular 2 Beta 0 with the package.json, tsconfig.json and boot.ts files as per the Angular 2 Quickstart for Typescript.
My end goal is to get an Angular2 Service working with AutobahnJS (but just at the baby steps at the moment).
Any help would be greatly appreciated....
Well I have eventually got this working now and made some progress towards an AutobahnJS and Angular 2 Service. You may refer to my plunker:
[plunker](https://plnkr.co/edit/Dgipr76Rbhgh31PH4pmM)
however I'm still quite stuck. The problem is I can't simply 'call' a function (where this function will then go-ahead and execute the Autobahn session.call(...) method) which returns a value. For example, I'm wanting to do something simple like:
var x = myMessageService.DoFunct1( someParameter );

TypeScript: access global var from a class without import

I got this module like this:
module MyModule {
export class Constants {
public static WIDTH:number = 100;
public static HEIGHT:number = 100;
....
}
}
export = MyModule;
Now I need to use MyModule.Constants.WIDTH in another class but I can't use import (I need to deliver this class js to a third party, and they don't use requirejs). Right now I use reference to get code checking but it keep giving this error (at transpilling time)
error TS2095: Could not find symbol 'MyModule'
What should I do now so I can use autocomplete and get rid of this error?
I hope you're not mindplay on the TypeScript forum otherwise I'm about to repeat myself.
export and import work together. You should either be using both or neither. If you check what the generated code looks like with and without the export keyword you'll see that export causes a module to be built. Since the third party can't use RequireJS I don't think this is what you want.
I would structure my classes like the following:
// file pkg/Foo.ts
module company.pkg {
export class Foo {}
}
// file pkg2/Bar.ts
module company.pkg2 {
export class Bar{}
}
Putting everything into the name space of your company minimizes the chance of a conflict with another library. Classes know about each other using a reference /// <reference path="..." /> which will allow it to compile.
Since you're not doing modules I would also compile to a single file using --out filename.js. That gets all the files included in (usually) the right order.

Categories