Aurelia CLI - au run --watch misses obvious type errors - aurelia

Using AureliaCLI and TypeScript.
I have a service which returns a specific type and a component which incorrectly assigns the returned object to a variable of another type:
import { ItemService } from "./itemService";
import { Item } from '../server/backend';
export class ItemDetails {
item: Item = null;
constructor(private itemService: ItemService) {
}
activate() {
this.item = this.itemService.getItem();
}
}
and
import { Seat } from "../server/backend";
export class ItemService {
item: Seat;
constructor() {
this.item = null;
}
getItem(){
return this.item;
}
setItem(item: Seat){
this.item = item;
}
}
This will generate an error when 'au run --watch' is run the first time, but any subsequent change to either file does not produce an error.
Can I configure AureliaCLI to look at dependant files also?
Thanks

Right, as you can probably guess, I am new to TypeScript.
I forgot to add a return type to the service method...
This will cause the error to be triggered:
getItem(): Seat {
return this.item;
}

Related

Modelina Csharp Generator Add Inheritance

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

mobx challenge: getters and setters into an observable array

I'm trying to write getters and setters into an observable array and it isn't working. The code below gives me the following error: Error: [MobX] No annotations were passed to makeObservable, but no decorator members have been found either
I've tried different combinations of decorators, but nothing seems to work. The behavior I want is whenever AppModel.text is updated, any UI rending the getter for text should update. Also whenever gonext() is called on the object, then any UI rending from AppModel.text should update and render data from the new 0 item on the array.
class DataThing
{
#observable text?: string = "foo";
}
class AppModel
{
get text() { return this.items[0].text}
set text(value: string | undefined) { this.items[0].text = value;}
items: DataThing[] = observable( new Array<DataThing>());
constructor() {
makeObservable(this);
this.items.push(new DataThing());
}
gonext() { this.items.unshift(new DataThing()); }
}
EDIT:
I ended up doing the following, but would still like to understand how to index into an array in an observable way.
class DataThing
{
#observable text?: string = "zorp";
constructor(){makeObservable(this);}
}
class AppModel
{
#observable _current?:DataThing;
get current() {return this._current;}
items: DataThing[] = observable( new Array<DataThing>());
constructor() {
makeObservable(this);
this.gonext();
}
gonext() {
this.items.unshift(new DataThing());
this._current = this.items[0];
}
}

How to expand the Start task with new scenarios

I've just learned about serenity-js and am giving it a go. I'm following the tutorial and noticed the follow example:
james.attemptsTo(
Start.withAnEmptyTodoList(),
AddATodoItem.called('Buy some milk')
)
The task for Start:
export class Start implements Task {
static withATodoListContaining(items: string[]) { // static method to improve the readability
return new Start(items);
}
performAs(actor: PerformsTasks): PromiseLike<void> { // required by the Task interface
return actor.attemptsTo( // delegates the work to lower-level tasks
// todo: add each item to the Todo List
);
}
constructor(private items: string[]) { // constructor assigning the list of items
} // to a private field
}
I really like this syntax and would like to continue this setup with more starting scenario's.
What would be the proper approach to accomplish this?
For anyone having the same question this is how I resolved it (found a similar setup going through the serenity-js repo):
// Start.ts
export class Start {
public static withATodoListContaining = (items: string[]): StartWithATodoListContaining => new StartWithATodoListContaining(items);
}
// StartWithATodoListContaining.ts
export class StartWithATodoListContaining implements Task {
static withATodoListContaining(items: string[]) {
return new StartWithATodoListContaining(items);
}
performAs(actor: PerformsTasks): PromiseLike<void> {
return actor.attemptsTo(
// todo: add each item to the Todo List
);
}
constructor(private items: string[]) {
}
}

Angular 5 BehaviorSubject data not update UI

I use to change dynamic child component in body and keep static header, bottom and menu.
My problem: When use BehaviorSubject as shared-data between components, then UI (*ngFor) not be updated event shared-data transferred well. I am using Angular 5.2.0, RxJs 5.5.6
My app has flow:
user click search button on Layout-top.component.ts -> fetch data from Backend server by Home.service.ts-> set data in BehaviorSubject object.
On Home.component.ts constructor always subscribe shared-data from Home.service.ts -> change data of Home.component.ts -> display them.
1. App.compoenet.ts
#Component({
selector: 'xxx',
template:
`
<gotop position="200"></gotop>
<layout-top></layout-top>
<router-outlet></router-outlet>
<layout-bottom></layout-bottom>
`
})
export class AppbComponent implements OnInit, AfterViewInit{
public ngAfterViewInit(): void {
this.spinner.hide();
}
message:string;
constructor(private spinner:Spinner){
}
public ngOnInit(){
this.spinner.show();
}
}
Layout-top.component.ts
public doSearch(){
let filter = {
xx:'XXX'
};
this.homeService.setData(filter);
}
3.Home.service.ts
#Injectable()
export class HomeService extends BaseService{
public data =new BehaviorSubject<DataType>(<DataType>{});
public eventFilter: EventEmitter<{}> = new EventEmitter();
public constructor(private http: HttpClient,
private _const: Const,
private util:Util,
private appref: ApplicationRef) {
super(_const, util);
}
public listProduct(filter):Observable<any>{
const url = url to my backend api
let headers:HttpHeaders = this.util.header(this._const, null, 'application/json');
return this.http.post(
url,
filter,
{headers})
.map(res => {
return res;
});
}
public getData():Observable<DataType>{
return this.data.asObservable();
}
public setData(filter:any):void {
const listProduct$ = this.listProduct(filter);
listProduct$.subscribe(res => {
this.data.next({res:res, filter:filter});
});
}
public cleanData() {
this.data.next(null);
}
}
layout-top.html
5.home.html
<div class="product-item"
*ngFor="let item of listProducts">
<!--display some thing here-->
</div>
6.home.component.ts
constructor(private service: HomeService,
private cdRef:ChangeDetectorRef,
private zone:NgZone,private appref: ApplicationRef ){
this.subsListProduct = this.service.getData().subscribe(obj=>{
this.zone.run(()=>{
$("#in-blur").css("display", "block");
if(!obj){
return;
}
const res = obj.res;
const filter = obj.filter;
if(res && filter){
this.listProducts = res.list;
this.cdRef.detectChanges();
}
});
setTimeout(()=>{
$("#in-blur").css("display", "none");
}, 1000);//for test loading spinner. will be remove in product
});
}
"this.listProducts = res.list;" work fine, ther listProducts be updated, but UI is not any change.
Many people advised use zone.run() or ChangeDetectorRef.detectChanges() but not work in my app. Plz support me.
The best way to map observable data to views is to use the async pipe - https://angular.io/api/common/AsyncPipe - this way all the change management and unsubscribing form the observable is handled for you. So in your example:
The view:
<div class="product-item"
*ngFor="let item of (listProducts | async)?.list">
<!--display some thing here-->
</div>
The ? before .list makes the property optional, so nothing will break if list is is null or undefined
The home component:
constructor(private service: HomeService,
private appref: ApplicationRef ){
this.listProducts = this.service.getData();
}
If you need to manipulate any of the data from the observable before displaying in the view do that in a .map, eg.
constructor(private service: HomeService,
private appref: ApplicationRef ){
this.subsListProduct = this.service.getData()
.map(obj => {
if (obj.list && obj.filter) {
return obj;
} else {
return null;
}
});
}
You should also consider dropping jquery for doing your css changes and use ngClass in your view to do this instead - https://angular.io/api/common/NgClass

can not read get property of undefined angular 2 error

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.");
}