I have several lit-elements on my page, but I can't compile them all together so I would like to "share" the lit stuff through a global variable. Might be a bit unconventional, but right now it will save me a lot of bytes.
I'm using rollup to do the packaging.
I think I'm pretty close to achieve what I want, but there is something that I'm missing...
This is my component..
#customElement('tab-strip')
export class TabStrip extends LitElement {
Resulting in
var tabstrip = (function (exports, litElement, repeat, classMap) {
//SOME OTHER STUFF
exports.TabStrip = class TabStrip extends litElement.LitElement {...
.
.
.
${repeat.repeat(this._tabs, e => litElement.html
}({}, Globals, Globals.repeat, Globals.classMap))
I have create a Globals.ts file that looks like this..
import { customElement, html, LitElement, property } from "lit-element";
import { Template, TemplateResult } from "lit-html";
import { classMap } from "lit-html/directives/class-map";
import { repeat } from "lit-html/directives/repeat";
class Globals {
public html = html;
public LitElement = LitElement;
public customElement = customElement;
public property = property;
public repeat = repeat;
public classMap = classMap;
public Template = Template;
public TemplateResult = TemplateResult;
}
window["Globals"] = new Globals();
And at last my rollup.config
input: inputDir + name + ".ts",
output: {
file: outputDir + name + ".js",
name: name,
format: format,
sourcemap: true,
globals: {
"lit-element": "Globals",
'customElement': 'Globals.customElement',
'lit-html': "Globals.LitHtml",
'html': "Globals.html",
'property': "Globals.property",
'lit-html/directives/repeat': "Globals.repeat",
'lit-html/directives/class-map': 'Globals.classMap',
'Template': 'Globals.Template',
'TemplateResult': 'Globals.TemplateResult'
}
},
plugins: [
typescript({
experimentalDecorators: true
}),
resolve(),
// terser({"ecma":"2019"}),
],
external: ['lit-element', 'lit-html', "lit-html/directives/repeat", "lit-html/directives/class-map"]
Gut feeling is that I have misunderstood something in the external stuff of rollup..
AS you can see in the generated file it says litElement.LitElement instead of just litElement
Any help??
Does it work for you? This seems correct just taking it at face value.
Looking at what your Globals is, it's an object with the properties LitElement, property, html, and so on.
If we look at your tabstrip function, the second argument is litElement, which matches the Globals Object being passed in.
So class TabStrip extends litElement.LitElement
makes sense, since the litElement is referencing your Globals object, and that has the LitElement property.
Related
I am playing around with asyncapi/modelina CSharpGenerator. I would like to add inheritance to the generated class something like this
public class UserCreated: IEvent
{
}
Is that possible? Can we add additional dependencies other than the generated ones?
Inheritance is, unfortunately, one of those features that have gotten put on the backburner, and still is.
Fortunately, it is possible to accomplish it, but it does require you to overwrite the entire rendering behavior, which might not be maintainable in the long run. You can find the full example in this PR: https://github.com/asyncapi/modelina/pull/772
const generator = new CSharpGenerator({
presets: [
{
class: {
// Self is used to overwrite the entire rendering behavior of the class
self: async ({renderer, options, model}) => {
//Render all the class content
const content = [
await renderer.renderProperties(),
await renderer.runCtorPreset(),
await renderer.renderAccessors(),
await renderer.runAdditionalContentPreset(),
];
if (options?.collectionType === 'List' ||
model.additionalProperties !== undefined ||
model.patternProperties !== undefined) {
renderer.addDependency('using System.Collections.Generic;');
}
const formattedName = renderer.nameType(model.$id);
return `public class ${formattedName} : IEvent
{
${renderer.indent(renderer.renderBlock(content, 2))}
}`;
}
}
}
]
});
What is happening here is that we create a custom preset for the class renderer and overwrite the entire rendering process of itself.
This will generate based on this input:
public class Root : IEvent
{
private string[] email;
public string[] Email
{
get { return email; }
set { email = value; }
}
}
Regarding dependencies, please see https://github.com/asyncapi/modelina/blob/master/docs/presets.md#adding-new-dependencies. You can do this in the self preset hook.
You can read more about the presets here: https://github.com/asyncapi/modelina/blob/master/docs/presets.md
hi I am trying to get city name from google api but getting that error below is my code
appcomponent class
import {Component, OnInit} from 'angular2/core';
import {marketComponent} from './market.component';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {introComponent} from './intro.component';
import {geoService} from './service.geo';
import {JSONP_PROVIDERS} from 'angular2/http';
declare var google: any;
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES],
providers: [JSONP_PROVIDERS, geoService]
})
#RouteConfig([
{ path: '/intro', name: 'Intro', component: introComponent, useAsDefault: true },
{ path: '/market', name: 'Market', component: marketComponent },
])
export class AppComponent {
constructor(private _http: geoService) { }
public maps;
public cat_error: Boolean = false;
public xml_Latitude :string;
public xml_Lang: string;
ngOnInit() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
var input: any = document.getElementById('google_places_ac');
var autocomplete = new google.maps.places.Autocomplete(input, {});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
console.log(place)
});
}
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => { this.maps = data },
err => { this.cat_error = true }
);
var result = this.maps.results;
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
}
}
and geoservice file
import {Injectable} from 'angular2/core';
import { Response, Jsonp} from 'angular2/http';
import 'rxjs/add/operator/map';
#Injectable()
export class geoService {
constructor(private http: Jsonp) { }
public xml_Latitude: string;
public xml_Lang: string;
public getPlaces(xml_Latitude, xml_Lang) {
return this.http.get(`http://maps.googleapis.com/maps/api/geocode/json?latlng=
'${this.xml_Latitude}','${this.xml_Lang}'&sensor=true`)
.map((res: Response) => res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return error.json().error || 'Server error';
}
}
error also says getplaces is not a function, I think I am missing something but don't know what....
In addition to the callback ordering problem identified by Thierry, you have a lost this context on this line:
navigator.geolocation.getCurrentPosition(this.showPosition);
The Problem
You have the classic JavaScript problem known as the incorrect this context.
The this keyword in JavaScript behaves differently than in does in other languages like C# and Java.
How this works
The this keyword, in a function, is determined as follows:
* If the function was created through a call to .bind, the this value is the argument provided to bind
* If the function was invoked through a method call, e.g. expr.func(args), then this is expr
* Otherwise
* If the code is in strict mode, this is undefined
* Otherwise, this is window (in a browser)
Let's look at how this works in practice:
class Foo {
value = 10;
doSomething() {
// Prints 'undefined', not '10'
console.log(this.value);
}
}
let f = new Foo();
window.setTimeout(f.doSomething, 100);
This code will print undefined (or, in strict mode, throw an exception).
This is because we ended up in the last branch of the decision tree above.
The doSomething function was invoked, the function wasn't a result of a bind call, and it wasn't invoked in a method syntax position.
We can't see the code for setTimeout to see what its invocation looks like, but we don't need to.
Something to realize is that all doSomething methods point to the same function object.
In other words:
let f1 = new Foo();
let f2 = new Foo();
// 'true'
console.log(f1.doSomething === f2.doSomething);
We know that setTimeout can only see the function we passed it, so when it invokes that function,
there's no way for it to know which this to provide.
The this context has been lost due to our referencing the method without invoking it.
The Red Flag
Once you know about this problems, they're easy to spot:
class Foo {
value = 10;
method1() {
doSomething(this.method2); // DANGER, method reference without invocation
}
method2() {
console.log(this.value);
}
}
The Solution
You have a few options here, each with its own trade-offs.
The best option depends on how often the method in question is invoked from differing call sites.
Arrow Function in Class Definition
Instead of using the normal method syntax, use an arrow function to initialize a per-instance member.
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
// OK
console.log(this.status);
}
}
let d = new DemonstrateScopingProblems();
window.setTimeout(d.run); // OK
Good/bad: This creates an additional closure per method per instance of your class. If this method is usually only used in regular method calls, this is overkill. However, if it's used a lot in callback positions, it's more efficient for the class instance to capture the this context instead of each call site creating a new closure upon invoke.
Good: Impossible for external callers to forget to handle this context
Good: Typesafe in TypeScript
Good: No extra work if the function has parameters
Bad: Derived classes can't call base class methods written this way using super.
Bad: The exact semantics of which methods are "pre-bound" and which aren't create an additional non-typesafe contract between your class and its consumers.
Function Expression at Reference Site
Shown here with some dummy parameters for explanatory reasons:
class DemonstrateScopingProblems {
private status = "blah";
public something() {
console.log(this.status);
}
public run(x: any, y: any) {
// OK
console.log(this.status + ': ' + x + ',' + y);
}
}
let d = new DemonstrateScopingProblems();
// With parameters
someCallback((n, m) => d.run(n, m));
// Without parameters
window.setTimeout(() => d.something(), 100);
Good/bad: Opposite memory/performance trade-off compared to the first method
Good: In TypeScript, this has 100% type safety
Good: Works in ECMAScript 3
Good: You only have to type the instance name once
Bad: You'll have to type the parameters twice
Bad: Doesn't easily work with variadic parameters
I think that you should move the result block into the subscribe callback associated the getPlaces method call:
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => {
this.maps = data;
var result = this.maps.results; // <----------
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
},
err => { this.cat_error = true }
);
}
It's because this.maps is undefined before the callback is called. And you try tyo get the result attribute before (this.maps.results).
Edit
I also see a problem at the line navigator.geolocation.getCurrentPosition. You could refactor your code this way:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => { // <----
this.showPosition(position);
});
} else {
alert("Geolocation is not supported by this browser.");
}
I have a page that uses a module for defining form content. It is rather generically usable but needs different resulting pages after form actions have been triggered (button click, etc.).
class CreateOrganisationPage extends Page {
static url = "#contact/organisation/create"
static at = {
form.displayed
}
static content = {
form(wait: true) {
module BusinessEntityFormModule, businessEntityName: 'organisation'
}
}
}
The module implementing the form content contains a UI control saveCommand that requires that a page ViewBusinessEntityPage is navigated to after submitting. In order to keep that module more reusable across different tests I wanted to provide that page as a parameter.
What is the best approach to do so?
class BusinessEntityFormModule extends Module {
String businessEntityName = "entity"
String idPrefix = "edit"
static content = {
self {
def id = "$idPrefix-" + StringUtils.capitalize(businessEntityName)
$("form", id: id)
}
saveCommand(to: ViewBusinessEntityPage) {
$('[data-command="save"]')
}
}
}
Simply define a property for the destination page in your module and use it in the content definition:
class BusinessEntityFormModule extends Module {
Class postSavePage
static content = {
saveCommand(to: postSavePage) {
$('[data-command="save"]')
}
}
}
And then set it when defining the module as part of the page:
class CreateOrganisationPage extends Page {
static content = {
form(wait: true) {
module BusinessEntityFormModule, businessEntityName: 'organisation', postSavePage: ViewBusinessEntityPage
}
}
}
Is there a way to define a property in a TypeScript module?
None of these compile:
module My {
// doesnt work
get Value(): number { return 42; }
// doesn't work either
get function Value(): number { return 42; }
// nope
function get Value(): number { return 42; }
}
Right now I'm forced to use this:
module My {
declare var Value: number;
Object.defineProperty(My, "Value", {
get: () => 42
});
}
The second form seems messy to me and the code hinting doesn't really treat it as a read-only property but as a plain variable.
Is there any standard way of defining properties directly inside modules?
No, there's not a way to declare a property on a module in TypeScript using any documented language features.
You can do it in several slightly round-about techniques.
A module can extend an existing class or function. So, I've created a class with a static property, and then later created a module that uses the same name as the class.
class My
{
static get Value():Number {
return 42;
}
}
module My {
var works: boolean = true;
}
alert(My.Value);
It does generate one oddity in the JavaScript generated code that you wouldn't do manually (and should be removed by most optimizers anyway) ... it will redeclare the variable My when the module is created. This does not cause a run-time issue as the variable was already lifted in JavaScript and will not conflict with the first usage.
Here's another option:
module Global {
class Inner {
get Value():Number {
return 42;
}
}
export var My;
My = new Inner();
}
var My = Global.My;
alert(My.Value);
While it presents an extra namespace, you can manipulate it however you'd like and use the inner class or change it as needed. This way, the My variable is global, just like it would be as a module.
Instead of using the module keyword, consider instead using export, which will allow you to do what you want to do, treating the file itself as a module (which is how CommonJS and AMD both work).
// in My.ts
var My = {
get value() {
return 42;
}
};
export = My;
// in foo.ts
import My = require('My');
console.log(My.value);
I describe this in greater detail in a blog post, The Definitive Guide to TypeScript.
I tried the singleton
let My = {
get value() {
return 42;
}
}
export My
but ran into an issue where the emitted JS still said get value() and didn't work on older versions of Node. I tried Object.defineProperty but then lost TypeScript compatibility. Here's my bridge that fixes both cases:
interface My {
value: number
}
// type assertion fixes TypeScript usage
let my = <My>{}
// defineProperty fixes JS usage
Object.defineProperty(my, 'value', {
get: () => 42
});
export = my;
It's used like a module in typescript
import * as my from './my'
my.property // returns 42
// my.property = doesn't work
I know it's a "little" late for this but using typescript 4.8 you can do this:
export module MyModule {
export var myVariable: string = "test";
}
then use it like:
MyModule.myVariable = "something else";
I have a class named BMW defined in BMW.ts as follows:
///<reference path="../Thing.ts"/>
module Entities.Cars {
import e = Entities;
export class BMW extends Vehicle {
public series: string;
constructor ( model : string, series : string) {
super("BMW", model)
this.series = series;
}
drive() {
alert("driving a bimmer is a different kind of feeling");
}
toString() : string
{
return this.getName() + " " + this.series + " " + this.getType();
}
}
}
In another file Thing.ts, I have Vehicle and Thing classes defined as follows:
module Entities {
// Class
export class Thing {
private _name: string;
private _type: string;
// Constructor
constructor (public name: string, public type: string) {
this._name = name;
this._type = type;
}
getName(): string { return this._name; }
setName(name: string) { this._name = name; }
getType(): string { return this._type; }
setType(name: string) {
this._type = name;
}
toString() : string
{
return "Entities.Thing";
}
}
export class Vehicle extends Thing {
public cargoCapacity: number;
public fuelType: string;
public owner: string;
constructor (make: string, model : string) {
super(make, model)
}
drive() {
}
toString(): string {
return "Entities.Vehicle";
}
}
}
When I attempt to execute the following code after referencing Thing and BMW TypeScript files:
var car = new Entities.Cars.BMW("335i", "E90");
car.drive();
I get an exception with the following error " Microsoft JScript runtime error: Unable to get value of the property 'BMW': object is null or undefined". The generated Javascript for BMW has an error. What is wrong with my above snippet?
There is nothing wrong with your code so it seems like your import order for the generated javascript files is wrong. The specification says the following:
Initialization order of the source files that make up the global module ultimately depends on the order in which the generated JavaScript files are loaded at run-time (which, for example, may be controlled by tags that reference the generated JavaScript files).
I have generated a file app.ts as follows:
///<reference path='Things.ts'/>
///<reference path='bmw/BMW.ts'/>
var car = new Entities.Cars.BMW("335i", "E90");
car.drive();
At this point you have two options:
Let the compiler determine the correct order for executing the files by generating a single output file
tsc --out app.js app.ts
Then you only have to source app.js.
Specify the correct order manually. For me, the following is the only order that works without throwing errors.
<html>
<head>
<script src="Things.js"></script>
<script src="bmw/BMW.js"></script>
<script src="app.js"></script>
</head>
<body>
</body>
</html>
Your code is fine.
My guess is that you are not placing script tags in your head element properly(wrong order, or ommitting some).
The simplest way to solve this, and to not have to remember about proper declaration sequence is to use single .js output file from tsc compiler by setting --out option.
EDIT: Depending on which js scenario you are working on(WSH, web app or some other js environment), you need to link js source files differently.
With wsh for instance, you could use FileSystemObject to read a source file, then evaluate it.
Or you could use AMDs...