Aurelia - injecting a service, but its properties remaining undefined - caching

I have a service:
import {eventsData} from 'services/eventsData';
import {singleton} from 'aurelia-framework';
#singleton()
export class DataRepository{
constructor(){
}
getEvents(){
var promise = new Promise((resolve,reject)=>{
if(!this.events){
setTimeout(_=>{
this.events = eventsData;
resolve(this.events);
},2000)
}
else{
resolve(this.events);
}
});
return promise;
}
getEvent(eventId){
return this.events.find(event => event.id == eventId);
}
}
which I am initiating as is in:
import {inject} from 'aurelia-framework';
import {DataRepository} from 'services/datarepository';
#inject(DataRepository)
export class events{
constructor(dataRepository, lazyOfImLazy){
dataRepository.getEvents().then(events => this.events = events);
}
createAndUseLazy(){
console.log("About to use lazy");
this.lazyOfImLazy().doStuff();
}
}
And then in :
import {inject} from 'aurelia-framework';
import{DataRepository} from 'services/dataRepository';
#inject(DataRepository)
export class EventDetail{
constructor(dataRepository){
this.dataRepository = dataRepository;
}
activate(params, routeConfig){
console.log(params.eventId);
console.log(this.dataRepository);
this.event = this.dataRepository.getEvent(1);
}
}
But when calling this.dataRepository.getEvent(1), then in dataRepository return this.events.find(event => event.id == eventId);, the this.events is undefined. I would have thought that by defining it as a singleton it would preserve this.events, when being instantiated in the promise. And the this.events is then populated properly. The view of events is using it.
Am I missing something?

Figured it out: The imports are case sensitive.
import{DataRepository} from 'services/datarepository'; and import{DataRepository} from 'services/dataRepository'; notice the lower cased r in datarepository in the first line and the capital one in dataRepository in the second.
It creates two different instances of the same service, due to the different case sensitivity in the string after from.
For those who ever encounter this, and spent hours trying to figure it out :)

Related

How to change Context.sender in a NEAR Smart Contract unit test (web assembly)?

I have a function, addWord(word:string), which prohibits the same Context.sender from adding more than one consecutive word. I store the last signee in the contract. Forgive me if there are some syntax errors in the code below, I only added parts of my current code.
export class Contract {
private lastSignedBy: string = '';
private words: PersistentVector<string> = new PersistentVector<string>('w');
#mutateState()
addWord(word: string): number {
// can't add more than one consecutive word
if(this.lastSignedBy != Context.sender){
this.words.push(word);
this.lastSignedBy = Context.sender;
return 1;
}
return 0;
}
}
Now, I want to test this function, but I don't know how to change the sender in the test. I think Maybe I need to mock the Context, but I'm not sure how to do that. The test framework is as-pect
Here is my current test.
let contract: Contract;
beforeEach(() => {
contract = new Contract();
});
describe('Contract', () => {
it('can add word if different sender than last time', () => {
expect(contract.addWord('word one')).toStrictEqual(1);
// TODO change Context.sender somehow.
expect(contract.addWord('second word')).toStrictEqual(1);
});
}
You can use a reference to VMContext like this
import { VMContext } from "near-sdk-as";
...
it("provides access to most recent player", () => {
VMContext.setSigner_account_id('player1')
});
https://github.com/Learn-NEAR/NCD.L1.sample--lottery/blob/2bd11bc1092004409e32b75736f78adee821f35b/src/lottery/__tests__/index.unit.spec.ts#L68

return an observable after more subscribe in Angular6

Hi i have a global service for several applications and i wish to make a method with several subscribes in order to stock and initialize all my datas and i wish make a subscribe to this method in my appComponent but i don't know how to make that
In my service
private initData(isLogged: boolean) {
this.http.get('/api/conf').subscribe(
conf => {
this.http.get('api/param').subscribe(
tokResp => {
this.appParams.token = tkResp.queoval;
this.appParams.culture = tkResp.culture;
this.appParams.GMT = tkResp.gmt;
this.http.get('/api/trad').subscribe(
trad => {
this.label = trad
// return an Observable
}
)
}
)
}
)
}
In my AppComponent
this.service.initData().subscribe(
result => {
this.test = result
}
How can i make that? I can't find the information in the documentation. Thank you for your help. It's important for my work, i used so much time to research for nothing :(
So since you want to make multiple async requests one after the other, you should use the observable function ".flatMap" (this is very similar to a Promises ".then"). The ".flatMap" function allows you to wait until the first request is completed before you continue.
So in your case, you would want your service to look something like this:
private initData(isLogged: boolean) {
return this.http.get('/api/conf').flatMap(
conf => {
return this.http.get('api/param');
}
).flatMap(
tokResp => {
this.appParams.token = tkResp.queoval;
this.appParams.culture = tkResp.culture;
this.appParams.GMT = tkResp.gmt;
return this.http.get('/api/trad');
}
).flatMap(
trad => {
this.label = trad;
return trad;
}
);
}
This function has all of the async requests chained together through ".flatMap" so that they are only called after the previous request completes.
The component file looks fine and should work with this new service.
As a general note, you should never subscribe to the observable inside
of the service. You should instead use functions like map, flatMap,
forkJoin ...

rxjs BehaviorSubject that emits via distinctUntilChanged

I'm wanting to implement an Observable / Subject with 3 particular attributes
Remember last emitted value and be able to surface it via a getter (BehaviorSubject)
Only emit when value changes
It must have a strong type such that the getter is known to be available by a consumer (aka. BehaviorSubject.getValue())
I'm thinking of just extending BehaviorSubject but want to make sure I'm not introducing any potential gotchas based on my novice understanding.
export class DistinctUntilChangedBehaviorSubject<T, TValue> extends BehaviorSubject<T> {
constructor(
initialValue: T,
private _distinctKeySelector?: (value: T) => TValue,
private _comparer?: _Comparer<TValue, boolean>
) {
super(initialValue);
}
public subscribe() {
// I'm particularly interested in knowing if this has any gotchas.
// Mostly things like creating subscriptions that don't get disposed as expected.
return super.distinctUntilChanged(
this._distinctKeySelector,
this._comparer
).subscribe.apply(this, arguments);
}
}
So 2 questions:
Does this seem like a reasonable approach / are there any gotchas here?
Is there another preferred way of doing this?
I do not know really why, but I tend to prefer composition over extension.
So I would do something along these lines
import {BehaviorSubject} from 'rxjs';
export class BehaviourSubjectAugmented<T> {
bs: BehaviorSubject<T>;
constructor(initialValue: T, private comparer: (p: T, q: T) => boolean) {
this.bs = new BehaviorSubject(initialValue);
}
getValue() {
return this.bs.getValue();
}
asObservable() {
return this.bs.asObservable()
.distinctUntilChanged(this.comparer);
}
complete() {
return this.bs.complete();
}
next(value: T) {
return this.bs.next(value);
}
}
Turns out my original idea causes a call stack exceeded issue. I'm assuming that distinctUntilChanged must call subscribe internally thus causing infinite recursion.
I ended up finding a simpler way to get what I needed by simply adding a method to an ISubject instance.
function distinctUntilChangedBehaviorSubject(
initialValue: number
): ISubject<number> & { getValue(): number } {
const observer = new BehaviorSubject<number>(initialValue);
const observable = observer.distinctUntilChanged();
const subject: ISubject<number> = Subject.create(
observer,
observable
);
return Object.assign(
subject,
{
getValue: () => observer.getValue()
}
);
}

Ajax call in MobX: MobX observer: Store is not available! Make sure it is provided by some Provider

I'm migrating from a traditional React application with the tree-structure to a state management structure with MobX.
Currently, my individual components propagate down data from an Ajax call made by the parent/grandparent, which works fine.
Now I wish to change this so that I don't work in the tree structure anymore due to the change of complexity and dependency of parallel grandchildren/children.
Say I do an axios.get in componentDidMount of a <Parent1/> React class. How do I then access the data using MobX?
What I've tried so far:
Creating a store.jsx that looks as such:
import { observable } from 'mobx';
const axios = require('axios');
class Store {
#observable parentdata;
loadParent = () => {
let that = this;
axios.get("/api/parent").then(function(response){
that.parentdata = response.parentdata;
}).catch(function(error){
// Error handling
})
};
}
export default Store;
My ReactDOM is rendered in container.jsx which contains all parents, meaning also <Parent1/>. In container.jsx we do the following:
import { Provider } from 'mobx-react';
import Store from './store/store.jsx';
let store = new Store();
and
ReactDOM.render(
<Provider store={store}>
<Main />
</Provider>,
document.getElementById('content')
);
.. in the end.
In the render method of container.jsx I don't do anything with <Parent1/> - it simply renders the component as normal(that's the idea here, right?)
In parent1.jsx, I remove the previous axios.get and add these:
import { inject, observer } from 'mobx-react';
#inject('store') #observer
export default class Parent1 extends React.Component {
// .....
componentDidMount () {
this.props.store.loadParent();
After that, the error is provided: MobX observer: Store 'parentdata' is not available! Make sure it is provided by some Provider
Where did I go wrong in binding the data here?
Edit: Removing #inject and only having #observer results in: TypeError: Cannot read property 'loadParent' of undefined
Few things to address:
Assuming you named your parentData store 'store' and not 'Store' like the classname, the Provider looks like it's configured correctly
Here's how I'd change the store itself:
import { observable } from 'mobx';
const axios = require('axios');
class Store {
#observable parentdata;
loadParent = () => {
// There's no need to clone this if you're using arrow functions
axios.get("/api/parent").then((response) => {
this.parentdata = response.parentdata;
}).catch(function(error){
// Error handling
})
};
}
export default Store;
In the component, I might do this:
import { inject, observer } from 'mobx-react';
#inject('store') #observer
export default class Parent1 extends React.Component {
// ... //
componentDidMount () {
this.props.store.loadParent();
}
render() {
if (!this.props.store.parentData) { return (<div>Loading...</div>) }
const { parentData } = this.props.store
return (<pre>{JSON.stringify(parentData, null, 4)}</pre>)
}
}
My suspicion is that your store is case sensitive and mistakenly named.
A nifty tricks to debugging could be to console.log important variables or attach them to a window object to test.
eg:
class Parent1 {
componentDidMount() {
window.Parent1 = this
window.Parent1Props = this.props
window.Store = this.props.store // or `this.props.Store` check case sensitivity!
}
// Open browser console and try to access your now global variables

Angular2 and TypeScript enums - dos and don'ts

I have a bunch of enums in my Angular2 app and for each enum I've created a corresponding utils class that provides some additional functionality. This simply goes around the TypeScript limitation that I can't add functions to an enum the same way I can do it in Java.
Here's an example of my country enum and the corresponding utils class:
import {Injectable} from "#angular/core";
export enum Country {
ANDORRA = <any>"ANDORRA",
UNITED_ARAB_EMIRATES = <any>"UNITED_ARAB_EMIRATES",
AFGHANISTAN = <any>"AFGHANISTAN",
// ...
// more countries skipped for brevity
// ...
ZAMBIA = <any>"ZAMBIA",
ZIMBABWE = <any>"ZIMBABWE"
}
#Injectable()
export class CountryUtils {
private _emptyEntry: Array<string> = ["", "", ""];
private countryData: any = {
ANDORRA: ["Andorra", "AD", "AND"],
UNITED_ARAB_EMIRATES: ["United Arab Emirates", "AE", "ARE"],
AFGHANISTAN: ["Afghanistan", "AF", "AFG"],
// ...
// more countries skipped for brevity
// ...
ZAMBIA: ["Zambia", "ZM", "ZMB"],
ZIMBABWE: ["Zimbabwe", "ZW", "ZWE"]
}
getData(country: Country): any {
if (!country) {
return this._emptyEntry;
}
return this.countryData[Country[country]];
}
getName(country: Country): any {
return this.getData(country)[0];
}
getCode2(country: Country): any {
return this.getData(country)[1];
}
getCode3(country: Country): any {
return this.getData(country)[2];
}
}
Now, as you can see I've marked the utils class as Injectable so that I can use it with DI where I need it.
The problem I'm facing is that these enums are also used in my core TS/JS model classes that are not Angular2 dependent so I can't use DI to inject an instance of my utils classes (like the one above) inside the core model classes.
The only work around that I see is to drop the Injectable() approach and to mark the methods in the utils classes as static. So this way I can just import each utils class together with the enum and use it anywhere I want.
My question is, how bad of a design decision would that be? Is it acceptable to do something like this in Angular2 or is using statics a complete no-no? I can't see anything particularly harmful other that it's like an eye sore compared to the regular DI approach.
Any advice would be very much appreciated. Thanks in advance!
I don't see a static class as a problem. Also, you can use Declaration Merging to add your util functionality to your enum. No need of extra class. Check below.
enum Country {
ANDORRA = <any>"ANDORRA",
UNITED_ARAB_EMIRATES = <any>"UNITED_ARAB_EMIRATES",
AFGHANISTAN = <any>"AFGHANISTAN",
// ...
// more countries skipped for brevity
// ...
ZAMBIA = <any>"ZAMBIA",
ZIMBABWE = <any>"ZIMBABWE"
}
namespace Country {
const _emptyEntry: Array<string> = ["", "", ""];
const countryData: any = {
ANDORRA: ["Andorra", "AD", "AND"],
UNITED_ARAB_EMIRATES: ["United Arab Emirates", "AE", "ARE"],
AFGHANISTAN: ["Afghanistan", "AF", "AFG"],
// ...
// more countries skipped for brevity
// ...
ZAMBIA: ["Zambia", "ZM", "ZMB"],
ZIMBABWE: ["Zimbabwe", "ZW", "ZWE"]
};
export function getData(country: Country): any {
if (!country) {
return this._emptyEntry;
}
return this.countryData[Country[country]];
}
export function getName(country: Country): any {
return this.getData(country)[0];
}
export function getCode2(country: Country): any {
return this.getData(country)[1];
}
export function getCode3(country: Country): any {
return this.getData(country)[2];
}
}
Country.getCode2(Country.AFGHANISTAN);

Resources