import {InitState} from './states/InitState';
import {SelectState} from './states/SelectState';
someFunction()
{
this._game = new Phaser.Game(800,400,Phaser.AUTO);
this._game.state.add('INIT_STATE',InitState);
this._game.state.add('SELECT_STATE',SelectState);
this._game.state.start('INIT_STATE');
}
And my phaser state class looks like this:
export class InitState extends Phaser.State
{
constructor()
{
super();
console.log("[InitState] constructor");
}
preload()
{
console.log("[InitState] preload");
}
create()
{
console.log("[InitState] create");
this.game.state.start('SELECT_STATE');
}
}
So now when I start my first state (“INIT_STATE'”) everything works fine, but when I try to switch to another state “SELECT_STATE” nothing happens.
If someone see something wrong please tell me.
When I debug.
First state have params:{ game: P…r.Game, key: "SELECT_STATE"} etc.
Second state haven’t any params:{ game: null, key: "", add: null, make: null, camera: null}
Why this happened?
I try to run Phaser 2.8.0 with es6 babel .
Regards
Related
Is it possible to clone a JSON-generated object or string into a Typescript class which I created? We are building a model of our API using Typescript classes. There’s a base class which they all extend which has common/helper methods. When we do JSON.parse(response) to auto-generate objects it creates simple objects and not our custom objects.
Is there a way we can convert those JSON-generated objects into our custom objects, so long as the field names match up? And, to make things more robust, can this but done where our custom objects’ fields are other custom objects and/or arrays of them?
Here is our code, with comments of what we’d like to achieve.
base-model.ts
export class BaseModelObject {
uuid: string; // All of our objects in our model and JSON have this required field populated
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
}
child-model.ts
import { BaseModelObject } from 'base-model';
export class Child extends BaseModelObject {
}
parent-model.ts
import { BaseModelObject } from 'base-model';
import { Child } from 'child-model';
export class Parent extends BaseModelObject {
children: Child[];
}
JSON payload
{
'uuid': '0632a35c-e7dd-40a8-b5f4-f571a8359c1a',
'children': [
{
'uuid': 'd738c408-4ae9-430d-a64d-ba3f085175fc'
},
{
'uuid': '44d56a0d-ad2d-4e85-b5d1-da4371fc0e5f'
}
]
}
In our components and directives and such, we hope to use the helper function in BaseModelObject:
Component code
let parent: Parent = JSON.parse(response);
console.log(parent.uuid); // Works! 0632a35c-e7dd-40a8-b5f4-f571a8359c1a
// Want this to print ‘true’, but instead we get TypeError: parebt.matchUUID is not a function
console.log(parent.matchUUID(‘0632a35c-e7dd-40a8-b5f4-f571a8359c1a’));
// Want this to print ‘true’, but instead we get TypeError: parent.children[0].matchUUID is not a function
console.log(parent.children[0].matchUUID(‘d738c408-4ae9-430d-a64d-ba3f085175fc’));
The problem is that JSON.parse() is not creating our classes, it’s creating simple objects with key/value pairs. So we’re thinking of “cloning” the JSON-generated object into an instance of our class, like this:
base-model.ts
export class BaseModelObject {
[key: string]: any;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj['uuid'] == this['uuid'];
}
cloneFields(obj: any) {
for (let prop in obj) {
this[prop] = obj[prop];
}
}
}
Component code
let parent: Parent = new Parent(); // Creates instance of our class
parent.cloneFields(JSON.parse(response)); // Copy JSON fields to our object
console.log(parent.matchUUID('0632a35c-e7dd-40a8-b5f4-f571a8359c1a')); // prints 'true'
console.log(parent.children[0].matchUUID('d738c408-4ae9-430d-a64d-ba3f085175fc')); // Still throws TypeError: parent.children[0].matchUUID is not a function
The problem now rests in the fact that the cloning of the Parent object did not recursively clone the JSON-generated Child objects into instances of our custom Child class.
Since our Parent object is typed at compile-time and it knows that the data type of the children array is Child[] (our custom class), is there a way to use reflection to instantiate the right class?
Our logic would need to say:
Create an instance of our custom class
Tell our instance to clone the fields from the JSON-generated object
Iterate over the fields in the JSON-generated object
For each field name from the JSON-generated object, find the "type definition" in our custom class
If the type definition is not a primitive or native Typescript type, then instantiate a new instance of that "type" and then clone it's fields.
(and it would need to recursively traverse the whole JSON object structure to match up all other custom classes/objects we add to our model).
So something like:
cloneFields(obj: any) {
for (let prop in obj) {
let A: any = ...find the data type of prop...
if(...A is a primitive type ...) {
this[prop] = obj[prop];
} else {
// Yes, I know this code won't compile.
// Just trying to illustrate how to instantiate
let B: <T extends BaseModelUtil> = ...instantiate an instance of A...
B.cloneFields(prop);
A[prop] = B;
}
}
}
Is it possible to reflect a data type from a class variable definition and then instantiate it at runtime?
Or if I'm going down an ugly rabbit hole to which you know a different solution, I'd love to hear it. We simply want to build our custom objects from a JSON payload without needing to hand-code the same patterns over and over since we expect our model to grow into dozens of objects and hundreds of fields.
Thanks in advance!
Michael
There are several ways to do that, but some requires more work and maintenance than others.
1. Simple, a lot of work
Make your cloneFields abstract and implement it in each class.
export abstract class BaseModelObject {
uuid: string;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
abstract cloneFields(obj: any);
}
class Parent extends BaseModelObject {
children: Child[];
cloneFields(obj: any) {
this.children = obj.children?.map(child => {
const c = new Children();
c.cloneFields(child);
return c;
});
}
}
2. Simple, hacky way
If there is no polymorphism like:
class Parent extends BaseModelObject {
children: Child[] = [ new Child(), new ChildOfChild(), new SomeOtherChild() ]
}
Property names mapped to types.
const Map = {
children: Child,
parent: Parent,
default: BaseModelObject
}
export class BaseModelObject {
uuid: string;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
cloneFields(obj: any) {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
this[prop] = obj[prop]?.map(child => { // You have to check it is an array or not,..
const c = new (Map[prop])();
c.cloneFields(child);
return c;
});
}
}
}
}
You can serialize hints into that JSON. Eg. property type with source/target type name and use it to resolve right types.
3. Reflection
Try tst-reflect. It is pretty advanced Reflection system for TypeScript (using custom typescript transformer plugin).
I'm not going to write example, it would be too complex and it depends on your needs.
You can use tst-reflect to list type's properties and get their types. So you'll be able to validace parsed data too.
Just some showcase from its README:
import { getType } from "tst-reflect";
function printTypeProperties<TType>()
{
const type = getType<TType>(); // <<== get type of generic TType ;)
console.log(type.getProperties().map(prop => prop.name + ": " + prop.type.name).join("\n"));
}
interface SomeType {
foo: string;
bar: number;
baz: Date;
}
printTypeProperties<SomeType>();
// or direct
getType<SomeType>().getProperties();
EDIT:
I created a package ng-custom-transformers that simplifies this a lot. Follow its README.
DEMO
EDIT old:
Usage with Angular
Angular has no direct support of custom transformers/plugins. There is a feature request in the Angular Github repo.
But there is a workaround.
You have to add ngx-build-plus. Run ng add ngx-build-plus.
That package defines "plugins".
Plugins allow you to provide some custom code that modifies your webpack configuration.
So you can create plugin and extend Angular's webpack configuration. But here comes the sun problem. There is no public way to add the transformer. There were AngularCompilerPlugin in webpack configuration (#ngtools/webpack) which had private _transformers property. It was possible to add a transformer into that array property. But AngularCompilerPlugin has been replaced by AngularWebpackPlugin which has no such property. But is is possible to override method of AngularWebpackPlugin and add a transformer there. Getting an instance of the AngularWebpackPlugin is possible thanks to ngx-build-plus's plugins.
Code of the plugin
const {AngularWebpackPlugin} = require("#ngtools/webpack");
const tstReflectTransform = require("tst-reflect-transformer").default;
module.exports.default = {
pre() {},
post() {},
config(cfg) {
// Find the AngularWebpackPlugin in the webpack configuration; angular > 12
const angularWebpackPlugin = cfg.plugins.find((plugin) => plugin instanceof AngularWebpackPlugin);
if (!angularWebpackPlugin) {
console.error("Could not inject the typescript transformer: AngularWebpackPlugin not found");
return;
}
addTransformerToAngularWebpackPlugin(angularWebpackPlugin, transformer);
return cfg;
},
};
function transformer(builderProgram) {
return tstReflectTransform(builderProgram.getProgram());
}
function addTransformerToAngularWebpackPlugin(plugin, transformer) {
const originalCreateFileEmitter = plugin.createFileEmitter; // private method
plugin.createFileEmitter = function (programBuilder, transformers, getExtraDependencies, onAfterEmit, ...rest) {
if (!transformers) {
transformers = {};
}
if (!transformers.before) {
transformers = {before: []};
}
transformers.before = [transformer(programBuilder), ...transformers.before];
return originalCreateFileEmitter.apply(plugin, [programBuilder, transformers, getExtraDependencies, onAfterEmit, ...rest]);
};
}
Then it is required to execute ng commands (such as serve or build) with --plugin path/to/the/plugin.js.
I've made working StackBlitz demo.
Resources I've used while preparing the Angular demo:
https://indepth.dev/posts/1045/having-fun-with-angular-and-typescript-transformers
https://medium.com/#morrys/custom-typescript-transformers-with-angular-for-angular-11-12-and-13-40cbdc9cca7b
How can I use map to return this output
get funcone() { return this.Form.get(funcone"); }
get functwo() { return this.Form.get("functwo"); }
get functhree() { return this.Form.get("functhree"); }
get funcfour() { return this.Form.get("funcfour"); }
I used this array
FormValues=['funcone','functwo','functhree','funcfour'];
And this map
FormValues.map(Value=>{
get Value() { return this.Form.get(Value); }
})
I would appreciate any help!
Presumably you want to define those functions as getters on some object, or class. Let's assume it's a class.
That syntax can't work - it creates a Value getter, rather than funcone getter. Now, while you can define a getter using a variable for the getter's name:
let propName = "foo";
class Foo {
get [propName]() { return "You're getting a foo"; }
}
new Foo().foo
// => "You're getting a foo"
as far as I know, there's no way to make a loop inside the class declaration, nor a way to keep reopening a class and adding new stuff to it like in Ruby, so class definition inside a loop also won't work.
However, class syntax is just a sugar for the older prototypal inheritance, so everything we can do with the class syntax, we can also do without it (though vice versa does not hold). In order to add new stuff to the class, we just need to stick it to the class's prototype object. We can explicitly define a getter method using Object.defineProperty.
class Foo {
constructor() {
this.Form = {
one: 1,
two: 2,
three: 3,
four: 4
}
}
}
let props = ['one', 'two', 'three', 'four'];
props.forEach(propName =>
Object.defineProperty(Foo.prototype, propName, {
get: function() { return this.Form[propName]; }
})
);
new Foo().three
// => 3
It would be almost the same code to give the getters to an object rather than a class; you'd just be defining properties on the object itself, rather than on a prototype.
I'm having a issue with babel transpile down to ES5. For the majority of my
application all other classes are compiled down correctly.
One of the classes however has a problem. When it is transpiled, then none of the methods are present on the instances anymore.
When the class constructor is executed, it raises a exception:
Uncaught TypeError: this.basket.setCurrency is not a function
Here is the class.
export class Basket extends ItemSet {
static get currencies() {
return [
{ code: 'gbp', symbol: '£', title: 'Pounds' },
{ code: 'usd', symbol: '$', title: 'US Dollars' },
{ code: 'eur', symbol: '€', title: 'Euros' }
];
}
constructor(currency, ...args) {
super(...args);
this.store = window.localStorage;
this.setCurrency(currency);
this.load();
}
setCurrency(code) {
// Only set the currency if it's valid for our Basket
Basket.currencies.forEach((currency) => {
if (currency.code == code) {
this.currency = currency;
this.store.cxCurrency = JSON.stringify(this.currency);
}
});
}
... <snip> ...
}
The class that it's extending, ItemSet can be found in basket-weaver:
https://github.com/andrewebdev/basket-weaver/blob/master/src/items.js#L72-L80
export class ItemSet extends Array {
getTotal(...args) {
let subTotals = this.map(item => { return item.getTotal(...args); });
if (!subTotals) throw "Cannot call getTotal() on an empty ItemSet";
return sum(...subTotals);
}
}
Finally, here is the code that is being generated by babel when it's
transpiled, just pasting the relevant part for brevity:
var Basket =
/*#__PURE__*/
function (_ItemSet3) {
babelHelpers.inherits(Basket, _ItemSet3);
function Basket() {
babelHelpers.classCallCheck(this, Basket);
return babelHelpers.possibleConstructorReturn(this, (Basket.__proto__ || Object.getPrototypeOf(Basket)).apply(this, arguments));
}
babelHelpers.createClass(Basket, [{
key: "setCurrency",
value: function setCurrency(code) {
var _this7 = this;
// Only set the currency if it's valid for our Basket
Basket.currencies.forEach(function (currency) {
if (currency.code == code) {
_this7.currency = currency;
_this7.store.cxCurrency = JSON.stringify(_this7.currency);
}
});
}
}, {
... <snip lots of other methods & properies> ...
}]);
return Basket;
}(_items.ItemSet);
_exports.Basket = Basket;
Last bit of background: I'm using polymer build to do the transpiling
since my components are mostly polymer elements.
Does anyone know what may be causing this?
Turns out this is because es5 would not let you extend arrays. There are some workarounds which I found here: https://stackoverflow.com/a/46898347/433267
I've implemented this in basket-weaver on a special es5-compat branch.
I have a LitElement that represents a file upload for multiple files.
This uses a sub-component that represents each file.
I'm struggling to find examples of the best practice for propagating changes into the sub component using LitElements as it appears to be very different from Polymer 3
Here's a cut down example of what I'm trying:
import './uploadFile.js';
class Upload extends LitElement {
...
static get properties() { return { files: Object } }
_render({files}) {
return html`
<input type="file" multiple onchange="...">
${this.renderFiles(files)}`
}
renderFiles(files) {
const filesTemplate = [];
for (var i = 0; i < files.length; i++) {
filesTemplate.push(html`
<upload-file file="${files[i]}"></upload-file>
`);
}
return filesTemplate;
}
}
When I update the status of a file the upload component re-renders but the upload-file component does not.
What am I doing wrong here? There aren't may examples of LitElement usage out there.
TIA
Best practice is "properties down, events up"; meaning that parent elements should share data with children by binding properties to them, and child elements should share data with parents by raising an event with relevant data in the detail of the event.
I can't comment on what you're doing wrong as I can't see how you're updating the status of the files, or your implementation of the child element.
One thing to be aware of is that because of dirty checking, lit-element can only observe changes to the top-level properties that you've listed in the properties getter, and not their sub-properties.
Something like
this.myObj = Object.assign({}, this.myObj, {thing: 'stuff'});
will trigger changes to an object and its sub-properties to render, while
this.myObj.thing='stuff';
will not.
To get sub-property changes to trigger a re-render, you would need to either request one with requestRender() or clone the whole object.
Here is some sample code showing a basic "properties down, events up" model:
Warning: lit-element is still pre-release and syntax will change.
parent-element.js
import { LitElement, html} from '#polymer/lit-element';
import './child-element.js';
class ParentElement extends LitElement {
static get properties(){
return {
myArray: Array
};
}
constructor(){
super();
this.myArray = [
{ val: 0, done: false },
{ val: 1, done: false },
{ val: 2, done: false },
{ val: 3, done: false }
];
}
_render({myArray}){
return html`
${myArray.map((i, index) => {
return html`
<child-element
on-did-thing="${(e) => this.childDidThing(index, i.val)}"
val="${i.val}"
done="${i.done}">
</child-element>
`})}
`;
}
childDidThing(index, val){
this.myArray[index].done=true;
/**
* Mutating a complex property (i.e changing one of its items or
* sub-properties) does not trigger a re-render, so we must
* request one:
*/
this.requestRender();
/**
* Alternative way to update a complex property and make
* sure lit-element observes the change is to make sure you
* never mutate (change sub-properties of) arrays and objects.
* Instead, rewrite the whole property using Object.assign.
*
* For an array, this could be (using ES6 object syntax):
*
* this.myArray =
* Object.assign([], [...this.myArray], {
* [index]: { val: val, done: true }
* });
*
*/
}
}
customElements.define('parent-element', ParentElement);
child-element.js
import { LitElement, html} from '#polymer/lit-element';
class ChildElement extends LitElement {
static get properties(){
return {
val: Number,
done: Boolean
};
}
_render({val, done}){
return html`
<div>
Value: ${val} Done: ${done}
<button on-click="${(e) => this.didThing(e)}">do thing</button>
</div>
`;
}
didThing(e){
var event = new CustomEvent('did-thing', { detail: { stuff: 'stuff'} });
this.dispatchEvent(event);
}
}
customElements.define('child-element', ChildElement);
Hope that helps.
I'm writing a small application in scala. The application processes simple log files. Because the processing takes some time, I've decided to let my application core extend Actor.
class Application extends Actor {
def react() {
loop {
react {
case Process(file) => // do something interesting with file...
}
}
}
}
The processing of a log file is triggered by clicking a button in the gui. The gui uses scala swing.
object Gui extends SimpleSwingApplication {
val application = new Application().start()
def top = new MainFrame {
val startButton = new Button
reactions += {
case ButtonClicked(`startButton`) => application ! Process(file)
}
}
}
Now, the application core needs to notify the gui about the current progress.
sender ! Progress(value) // whenever progress is made
I've solved this by creating a separate actor inside the gui. The actor is executed inside the edt thread. It listens to messages from the application core and updates the gui.
object Gui extends SimpleSwingApplication {
val actor = new Actor {
override val scheduler = new SchedulerAdapter {
def execute(fun: => Unit) { Swing.onEDT(fun) }
}
start()
def act() {
loop {
react {
case ForwardToApplication(message) => application ! message
case Progress(value) => progressBar.value = value
}
}
}
}
}
Since the application core needs to know about the sender of the message, I also use this actor to forward messages from the gui to the application core, making my actor the new sender.
reactions += {
case ButtonClicked(`startButton`) => actor ! ForwardToApplication(Process(file))
}
This code works just fine. My question: Is there a simpler way to do this? It whould be nice to simple use the reactions mechanism for my application messages:
reactions += {
case Progress(value) => progressBar.value = value
}
Any ideas how to achieve this?
I have extended on gerferras idea of making my application a swing.Publisher. The following class acts as intermediator between a swing.Reactor and an Actor.
import actors.Actor
import swing.Publisher
import swing.event.Event
import swing.Swing.onEDT
case class Send(event: Any)(implicit intermediator: Intermediator) {
intermediator ! this
}
case class Receive(event: Any) extends Event
case class Intermediator(application: Actor) extends Actor with Publisher {
start()
def act() {
loop {
react {
case Send(evt) => application ! evt
case evt => onEDT(publish(Receive(evt)))
}
}
}
}
Now my reactions can include both swing events and application events.
implicit val intermediator = Intermediator(application)
listenTo(intermediator, button)
reactions += {
case ButtonClicked(`button`) => Send(Process(file))
case Receive(Progress(value)) => progressBar.value = value
}
Note how the case class Send provides some syntactic sugar to easily create events and pass them to the intermediator.
Maybe this is simpler but don't know if it's better. Instead of making your application backend an actor, you can create an anonymous actor every time you need to process the file:
reactions += {
case ButtonClicked(`startButton`) => application.process(file, { v: Int => Swing.onEDT(progressBar.value = v) })
}
For the progress update part, you can pass a callback to the process method to be executed every time a new progress is made:
import scala.actors.Actor.actor
def process(f: File, progress: Int => Unit) {
actor {
// process file while notifying the progress using the callback
progress(n)
}
}
Alternatively (haven't tested) you could make your application a scala.swing.Publisher and, instead of using the callback, publish and event every time. So the code could be:
listenTo(startButton, application) //application is a Publisher
reactions += {
case ButtonClicked(`startButton`) => application.process(file)
case Progress(v) => progressBar.value = v
}
And in the application:
import scala.actors.Actor.actor
def process(f: File) {
actor {
// process file while notifying the progress using an scala.swing.event.Event
publish(Progess(n))
}
}