I'm trying to make a website where i use Aurelia and Javascript and ES6.
I have a simple class (Status) that needs to get some data on a interval from a server.
Update
I have added CalcData to the injector as sugessted by Fabio Luz, but i still get the same error. Good call btw ;).
The class looks like this:
import {inject} from "aurelia-framework"; // for the inject decorator
import { StatusData } from "./statusData"; // MovieData, the module that will be injected
import { CalcData } from "./Calc"
#inject(StatusData, CalcData) // Inject decorator injects MovieData
export class Status {
constructor(StatusData, CalcData) {
this.statusData2 = StatusData;
this.CalcData = CalcData;
}
activate() {
setInterval(this.updateCalc, 3000);
}
updateCalc() {
this.CalcData.hello()
.then(statusData => this.statusData2 = statusData);
}
updateStatus() {
return statusData2.getX()
.then(statusData => this.statusData2 = statusData);
}
update() {
return 1;
}
}
The updateCalc function is called but when this happens the browser says it that CalcData is undefined.
Uncaught TypeError: Cannot read property 'hello' of undefined
at updateCalc (status.js:17)
updateCalc # status.js:17
status.js:17 Uncaught TypeError: Cannot read property 'hello' of undefined
at updateCalc (status.js:17)
updateCalc # status.js:17
The CalcData class looks like this:
import { inject } from "aurelia-framework"; // for the inject decorator
import { HttpClient } from "aurelia-http-client"; // for the http client that will be injected
let baseUrl = "/movies.json";
#inject(HttpClient)
export class CalcData {
constructor(httpClient) {
this.http = httpClient;
}
hello() {
return this.http.get(baseUrl)
.then(response => {
return response.content;
});
}
}
I can't seem to find the problem, i have looked around but can't find a solution. I must say that i'm new to Aurelia.
Any help is much appreciated!
Your problem is down to capitalization, most likely.
Let's look at the beginning of your code:
import {inject} from "aurelia-framework"; // for the inject decorator
import { StatusData } from "./statusData"; // MovieData, the module that will be injected
import { CalcData } from "./Calc"
#inject(StatusData, CalcData) // Inject decorator injects MovieData
export class Status {
constructor(StatusData, CalcData) {
this.statusData2 = StatusData;
this.CalcData = CalcData;
}
Notice that your constructor is taking parameters whose names exactly match the names of the classes you want to inject. This is causing confusion for the runtime, as you are likely ending up setting this.Calcdata to the class CalcData (and the same for StatusData). The class does not have a function called hello(), only instances of the class have that function. If you change the parameter names to not exactly match, your issues should go away.
#inject(StatusData, CalcData) // Inject decorator injects MovieData
export class Status {
constructor(statusData, calcData) {
this.statusData = statusData;
this.calcData = calcData;
}
I've also lower-cased the property names to match JavaScript naming conventions.
Seems like i had to bind "this" to pass the object reference. When calling this in hello it how read gets the right object.
E.g.
import {inject} from "aurelia-framework";
import {StatusService} from "./statusService"
#inject(StatusService)
export class Status{
message = 'unknown yet';
statusService: StatusService;
constructor(statusService){
this.statusService = statusService;
}
activate(){
setInterval(this.updateStatus.bind(this), 3000);
}
updateStatus = function () {
this.message = this.statusService.getX();
}
}
Related
I am trying to get into stimulusJS
import { Controller } from 'stimulus'
export default class extends Controller {
static targets = [
'foo',
]
connect() {
const fooValue = this.fooTarget.value
console.log(this.fooValue) // 7
this.someFunction()
}
someFunction(){
console.log(this.fooValue) // undefined
}
}
I want to be able to get this value on connect as I want to know if it has changed.
Your code declares const variable within the scope of connect() function. But you should use this (Stimulus Controller) property instead:
...
connect() {
this.fooValue = this.fooTarget.value
...
I am trying to reuse some working code from AngularJS 1 services written in plain JavaScript in an Angular 2 environment.
The services look, for instance, like the following example:
(function () {
angular.module('myapp.mysubmodule').factory('myappMysubmoduleNormalService', ['someOtherService',
function (someOtherService) {
var internalState = {
someNumber: 0
};
var service = {};
service.someFunction = function () {
internalState.someNumber++;
};
someOtherService.getValues().forEach(function (v) {
service[v] = function () {
console.log(v + internalState.someNumber);
};
});
return service;
}]);
})();
I have found various examples of how to convert AngularJS 1 services to Angular 2 services (such as this one), all of which have in common that instead of the service factory, I have to export a class.
This should look roughly as follows:
import { Injectable } from '#angular/core';
#Injectable()
export class myappMysubmoduleNormalService {
someFunction: function () {
// ?
}
}
Now, the question is how to incorporate the internal state and the dynamically added properties.
Is it really the way to go to do all that in the constructor, i.e. fill each instance of the class upon initialization, like so:
import { Injectable } from '#angular/core';
#Injectable()
export class myappMysubmoduleNormalService {
constructor() {
var internalState = {
someNumber: 0
};
var service = {};
this.someFunction = function () {
internalState.someNumber++;
};
this.getValues().forEach(function (v) {
service[v] = function () {
console.log(v + internalState.someNumber);
};
});
}
}
Or is there any other way? The above probably works (save for the missing dependency injection, that I still have to find out about how to do in Angular 2). However, i am wondering whether it is a good way because I have not come across any samples that did much of a member initialization in their constructor.
You can use just the same approach in Angular with factory providers:
export function someServiceFactory(someOtherService) {
var internalState = {
someNumber: 0
};
var service = {};
service.someFunction = function () {
internalState.someNumber++;
};
someOtherService.getValues().forEach(function (v) {
service[v] = function () {
console.log(v + internalState.someNumber);
};
});
return service;
};
#NgModule({
providers: [
{
token: 'myappMysubmoduleNormalService',
useFactory: someServiceFactory,
deps: ['someOtherService']
}
]
})
Both in Angular and AngularJS the value returned by the factory function is cached.
A service is just a class that you can inject into components. It will create a singleton in the scope where it is named a provider.
import { Injectable. OnInit } from '#angular/core';
#Injectable()
export class myappMysubmoduleNormalService implements OnInit {
internalState: number;
constructor() {}
ngOnInit(){
this.internalState = 0;
}
incrementSomeNumber() {
this.internalState++;
console.log(this.internalState};
}
}
I realize this is not logging a distinct internal state for multiple functions but you get the idea.
Register this as a provider in the app.module (if you want a singleton for app scope)
When you import into a component and then inject in the constructor
constructor(private _myservice : myappMysubmoduleNormalService) {}
you can now use the _myservice methods
myNumber : number = 0 ;
componentFunction() {
_myservice.incrementSomeNumber();
this.myNumber = _myservice.internalState;
}
Of course you could have the service method return the incremented number (or data or a promise of data)
This is rough but gives you the idea. Very little code belongs in the constructor. A service should be injected. what is shown in component constructor is shorthand to a get private variable referencing the service. The service will be a singleton for the scope in which it is provided. (can be overridden within the scope but that seems a code smell to me)
To pass back a value :
In service
incrementSomeNumber(): number {
this._internalState++;
console.log(this._internalState};
return this._internalState;
}
In component:
mynumber: number;
componentFunction() {
this.mynumber = _myservice.incrementSomeNumber();
}
Not sure what you're trying to accomplish but just wanted to show example of getting information from services. Most common use of services for me is a dataservice, so the code would be a little more complex as it is asynch.
I encounter a strange problem with my custom class in angular2 after passing it through an Observable chain.
I always receive the error:
EXCEPTION: f.mapToParams is not a function
ORIGINAL STACKTRACE:
TypeError: f.mapToParams is not a function
at SafeSubscriber._next (filter.component.ts)
...
Uncaught TypeError: f.mapToParams is not a function
at Safesubscriber._next (filter.component.ts)
Here is my coding:
filter.ts:
import { Params } from '#angular/router';
export class Filter {
public text:String = '';
public mapToParams():Params {
let params:Params = {};
// Do some mapping here...
return params;
}
}
filter.component.ts
import { Component, OnInit Output, EventEmitter } from '#angular/core';
import { Router, Params } from '#angular/router';
import { Filter } from './filter';
import { Observable, Subject } from 'rxjs/Rx';
export class FilterComponent implements OnInit {
private _filter:Filter;
private _filterStream = new Subject<Filter>();
ngOnInit() {
this._filter = new Filter();
this._filterStream
.debounceTime(300)
.switchMap((f:Filter) => Observable.of(f))
.subscribe((f:Filter) => {
let params:Params = {};
console.log(f.text); // <-- No problem here
// params = this._map(f); // <-- This would work
params = f.mapToParams(); // <-- Here occurs the error
});
}
private _map(f:Filter):Params {
// Do some mapping here
}
public onInputChanged(searchText:String):void {
this._mergeFilter( {
map(f:Filter) {
f.text = searchText;
}
})
}
private _mergeFilter(callback:FilterMergeCallback):void {
let f:Filter = JSON.parse(JSON.stringify(this._filter));
callback.map(f);
this._filterStream.next(f);
}
}
I have tried to comment out the debounceTime and switchMap statement but with no success.
At a different point in my coding the filter.mapToParams method can be called without any problems. It seems to me like the Observable chain strips all methods from my object.
Here is my angular config:
#angular/cli: 1.0.0.-beta.32.3
#angular/common: ^2.4.0
#angular/compiler ^2.4.0
#angular/core ^2.4.0
rxjs: ^5.1.0
Can anyone help me on this?
I think I got it:
I copied the current _filter Object to a new filter variable by
JSON.parse(JSON.stringify())
By this, all methods get stripped from the new object.
Means, I have to find a new method to clone the object...
Thanks all for you replies!
I have TypeScript code with classes of components. And I want to use somehow remote js file to extend this classes remote. So I want when my app starts to get js file remote and use code this for extend of needed class.
How to extend class I know. For example:
Import { UsersBlocksMyOrders } from "../pages/users/blocks/myorders";
declare module "../pages/users/blocks/myorders" {
interface UsersBlocksMyOrders {
logit(): void;
}
}
UsersBlocksMyOrders.prototype.logit = function () { console.log(this); }
In component file the code is:
import { APP_CONFIG } from "../../../app/app.config";
#Component({
selector: 'menu-blocks-menupage',
templateUrl: APP_CONFIG.appDomain + '/mobilesiteapp/template/?path=pages/menu/blocks/menupage'
})
export class MenuBlocksMenuPage{
constructor(){
this.logit();
}
}
My problem is that I use the Webpack to compile code. Webpack create final file where name of function is different. That's why I can't access to class directly.
How to be in this situation?
Create service to get file and extend class. You need to have variable inside class which is would store object where have keys. Keys it is names of classes to extend. And values with imported classes. Inside init function we are loop extending with prototype method.
import { Http } from "#angular/http";
import { MenuBlocksMenuPage } from "../pages/menu/blocks/menupage";
export class ExtendService {
allModules = {
...
MenuBlocksMenuPage : MenuBlocksMenuPage,
...
};
constructor(public http: Http){ }
init()
{
//Load json map to extend class
this.http.get(APP_CONFIG.appDomain + '/modules/mobilesiteapp/view/js/extend.json').toPromise()
.then((res) => {
let json = res.json();
//Loop each class to extend
Object.keys(json).forEach((cl) => {
//Add new functions and methods
Object.keys(json[cl]).forEach((func) => {
this.allModules[cl].prototype[func] = eval(json[cl][func]);
});
});
}).catch((e) => {
console.error(e);
});
}
}
Request json file with functions to eval.
{
"MenuBlocksMenuPage": {
"logit": "(function (){console.log(this);})"
}
}
I'm using angular2 with Typescript. I'm trying to create a base class that can be inherited by other classes and within the base class, a service is injected. So far I can not get the ajaxService injected correctly into the base class that is being inherited into the user class. Specifically when a user is instantiated, and then the save() method is called from the user instance, the following line in the base class: return _this._ajaxService.send(options); doesn't work since _ajaxService is undefined.
Here is a user class that extends the base class:
import {Base} from '../utils/base';
export class User extends Base {
// properties
id = null;
email = null;
password = null;
first_name = null;
last_name = null;
constructor(source) {
_super.CopyProperties(source, this);
}
}
Here is the base class:
import {Component} from 'angular2/core';
import {AjaxService} from './ajax.service';
#Component({
providers: [AjaxService]
})
export class Base {
constructor(private _ajaxService: AjaxService) { }
// methods
public static CopyProperties(source:any, target:any):void {
for(var prop in source){
if(target[prop] !== undefined){
target[prop] = source[prop];
}
else {
console.error("Cannot set undefined property: " + prop);
}
}
}
save(options) {
const _this = this;
return Promise.resolve()
.then(() => {
const className = _this.constructor.name
.toLowerCase() + 's';
const options = {
data: JSON.stringify(_this),
url: className,
action: _this.id ? 'PATCH' : 'POST';
};
debugger;
return _this._ajaxService.send(options);
});
}
}
This works fine except that AjaxService is not being injected into the base class. I guess this makes sense since user is being instantiated not base.
So how can I use AjaxService in the Base module when when `Base module is being extended on another class?
I guess when I instantiate user, the constructor in the user class is called but the constructor in the base class that injects the service is not being called.
Here's the AjaxService:
import {Injectable} from 'angular2/core';
#Injectable()
export class AjaxService {
// methods
send(options) {
const endpoint = options.url || "";
const action = options.action || "GET";
const data = options.data || {};
return new Promise((resolve,reject) => {
debugger;
$.ajax({
url: 'http://localhost:3000' + endpoint,
headers: {
Authentication: "",
Accept: "application/vnd.app.v1",
"Content-Type": "application/json"
},
data: data,
method: action
})
.done((response) => {
debugger;
return resolve(response);
})
.fail((err) => {
debugger;
return reject(err);
});
});
}
}
It's ok to inject services in the base, but you have to pass it in from the User class regardless. You can't inherit the actual instantiation of the service from the Base, so you have to pass it down to the Base from User. This is not a limitation of TypeScript, but rather a feature of how DI works in general.
Something like this:
class User extends Base
constructor(service: AjaxService) {
super(service);
}
If the Base instantiated the service for you, you would not be able to affect the instantiation from User. This would negate a lot of the benefits of DI overall since you would lose control by delegating dependency control to a different component.
I understand that you might be trying to reduce code duplication by specifying this in the Base, but this goes against the principle of DI.
Each class in Angular 2 that you want to inject you must annotate. If it is not component, you must annotate it with #Injectable() annotation. If you inject class that already inject other class, you must create provider for that.
import {Injectable} from 'angular2/core';
import {Base} from './base';
#Injectable()
export class User extends Base {
}
I created Plunker for you, i hope that it will solve your problem:
http://plnkr.co/edit/p4o6w9GjWZWGfzA6cv41?p=preview
( look at console output )
PS. Please use Observable instead of Promises