Class constructor with async properties set from poke-apiAsync wrapper around public APISafe async API codeCombining two classes to hold general and specific properties of an itemWPF async ObservableTaskQueue classFetch an array from API endpoint in async functionPass Async function to new Promise constructorInjecting a DbContext with an async constructor dependencyTesting async method call from constructorCreating API clients that are “async agnostic”Async API Function with Mongoose
Does multi-classing into Fighter give you heavy armor proficiency?
Tikz: place node leftmost of two nodes of different widths
Is it true that good novels will automatically sell themselves on Amazon (and so on) and there is no need for one to waste time promoting?
Dual Irish/Britsh citizens
Can a medieval gyroplane be built?
Describing a chess game in a novel
Fewest number of steps to reach 200 using special calculator
Why Choose Less Effective Armour Types?
Matrix using tikz package
Variable completely messes up echoed string
Error: "inconsistent hash". Workers crash and node is unable to connect to others
I got the following comment from a reputed math journal. What does it mean?
Is there a hypothetical scenario that would make Earth uninhabitable for humans, but not for (the majority of) other animals?
In what cases must I use 了 and in what cases not?
How to generate binary array whose elements with values 1 are randomly drawn
How can an organ that provides biological immortality be unable to regenerate?
Constant Current LED Circuit
Changing Color of error messages
What favor did Moody owe Dumbledore?
Replace four times with sed
If "dar" means "to give", what does "daros" mean?
Bash - pair each line of file
Could Sinn Fein swing any Brexit vote in Parliament?
Four married couples attend a party. Each person shakes hands with every other person, except their own spouse, exactly once. How many handshakes?
Class constructor with async properties set from poke-api
Async wrapper around public APISafe async API codeCombining two classes to hold general and specific properties of an itemWPF async ObservableTaskQueue classFetch an array from API endpoint in async functionPass Async function to new Promise constructorInjecting a DbContext with an async constructor dependencyTesting async method call from constructorCreating API clients that are “async agnostic”Async API Function with Mongoose
$begingroup$
So, this is for a clicker game I'm trying to build with Pokémon data drawn from the PokéAPI. Since I'm using Vue.js for the project, Vuex is used to store and share the data between components and I'm persisting and loading the state with LocalForage to IndexedDb.
When constructing a Pokemon-object from the Pokemon-class I'm having two different ways I get the data to construct the object.
From the API I'll get the data structured one way and from the IndexedDb I get the 1to1 JSON-stringified version of the Pokemon-object. So just the raw JSON without the class-methods or any properties tied to other custom classes or any enums. You probably already knew that, but I didn't.
(At first I was just using an init-method to set the async properties, but that changed when I started using IndexedDb)
That's why I got the basic constructor method to build the object anew from the saved object in the IndexedDb and a constuctFromApiResponse method to map the pokeapi response to my Pokemon-object.
I'm trying to figure out what's the best way to write as little code as possible to stay DRY. (while keeping any type-inferences and other goodies that PHPStorm is able to make without much usage of JSHint (although I'm fine with adding hints, just didn't add them here everywhere because I'm going back and forth between the different constructor-approaches))
Pokemon.js
import Item from './Item'
import Pokedex from 'pokeapi-js-wrapper'
import Type from './Type'
const P = new Pokedex()
/**
* @class Pokemon
*/
class Pokemon
constructor (
dexNum, name, level, types, baseStats, stats, sprites, hp, exp, item,
growthRate = '', catchRate = 0, evolutions =
)
Object.assign(this,
dexNum,
name,
level,
baseStats,
stats,
hp,
exp,
sprites,
growthRate,
catchRate,
evolutions
)
/**
* @type Type[]
*/
this.types = types.map((type) => Type[type.name.toUpperCase()])
/**
* @type Move[]
*/
this.moves = []
/**
* @type Item
*/
this.item = item ? (item instanceof Item ? item : new Item(item)) : null
/* eslint-disable-next-line camelcase */
static async constructFromApiResponse ( id, name, types, stats, base_experience, sprites , level)
const self =
dexNum: id,
name,
level,
sprites,
hp: 0,
exp: 0,
item: null
self.types = types.map((type) => Type[type.type.name.toUpperCase()])
self.baseStats =
for (const stat of stats)
self.baseStats[stat.stat.name] = stat.base_stat
/* eslint-disable-next-line camelcase */
self.baseStats.exp = base_experience
self.stats = self.baseStats
self.hp = self.stats.hp * level
const p = new Pokemon(self)
p.calculateStats()
await p.setGrowthRate()
await p.setCatchRate()
await p.setEvolutionChain()
return p
async setGrowthRate ()
this.growthRate = await this.getSpecies()
.then((response) => response.growth_rate.name)
async setCatchRate ()
this.catchRate = await this.getSpecies()
.then((response) => response.capture_rate)
async setEvolutionChain ()
const evolvesTo = (next, prevDexNum) =>
for (const ev of next)
const details = ev.evolution_details[0]
const evolution =
if (!this.evolutions.hasOwnProperty(prevDexNum))
this.evolutions[prevDexNum] = []
this.evolutions[prevDexNum].push(evolution)
// recurse
if (ev.hasOwnProperty('evolves_to') &&
ev.evolves_to.length > 0)
evolvesTo(ev.evolves_to, evolution.dexnum)
const evoChainUrl = await this.getSpecies()
.then((response) => response.evolution_chain.url)
await P.resource(evoChainUrl)
.then((response) => evolvesTo(
response.chain.evolves_to,
response.chain.species.url.split('/').slice(-2, -1)[0])
)
/**
* Get species entry
*
* @returns Promise
*/
getSpecies ()
return P.resource('/api/v2/pokemon-species/' + this.dexNum)
/**
* @param toDexNum number
*
* @returns Promise<void>
*/
async evolve (toDexNum = 0)
// [...] skipping review-unrelated methods
export default Pokemon
I was doing this.name = name before, but was looking for other ways to set the properties without repeating myself that much. But I don't know if the Object.assign method is really considered "cleaner" or better to read.
Also depending on the constructor-approach, maybe there's a better way to re-set the properties for the evolution in the evolve-method.
In addition I don't know what to do about the many function parameters. eslint with standardjs doesn't seem to have an opinion about this.
Item.js
/**
* @class Item
*/
class Item
constructor ( id, name, cost, attributes, category, sprites, quantity, effect, triggerOnHold, onUse )
Object.assign(this,
id,
name,
cost,
attributes,
category,
sprites,
quantity,
effect,
triggerOnHold,
onUse
)
static async constructFromApiResponse ( id, name, cost, attributes, category, sprites , quantity, effect, triggerOnHold, onUse)
const self =
id,
name,
cost,
attributes,
category: category.name,
sprites,
quantity,
effect,
triggerOnHold,
onUse
const item = new Item(self)
return item
// [...] skipping review-unrelated methods
export default Item
Same thing with Item, although I don't have properties here that need any special treatment just yet. That's when I thought I'd have another look at TypeScript and saw that you can just omit the constructor function body when you're just doing assignments like this.name = name. Maybe I'll rewrite it in TypeScript then if that leads me to DRY-haven. But I'm not sure about that yet.
--
Following are the two files where I'm using the two different Pokemon-constructor methods, for reference.
store.js
import Item from './classes/Item'
import LocalForage from 'localforage'
import Pokemon from './classes/Pokemon'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const lf = LocalForage.createInstance(
driver: LocalForage.INDEXEDDB,
name: 'PokeClicker',
version: 1.0,
storeName: 'pokeclicker'
)
const persistPlugin = store =>
store.subscribe((mutations, state) =>
for (const key in state)
if (!state.hasOwnProperty(key))
continue
const value = state[key]
lf.setItem(key, value)
)
const store = new Vuex.Store(
plugins: [
persistPlugin
],
state:
bags:
misc: [],
medicine: [],
pokeballs: [],
// machines: [],
berries: [],
// mail: [],
battle: [],
key: []
,
team: [],
box: [],
money: 0,
reputation: 0,
route: ,
opponent: ,
enemy:
,
mutations:
async init (state, data)
// init bag
for (const key in data.bags)
if (!data.bags.hasOwnProperty(key))
continue
const bag = data.bags[key]
for (const idx in bag)
const item = bag[idx]
data.bags[bag][key] = new Item(item)
// init team
for (const idx in data.team)
const pkmn = data.team[idx]
if (!(pkmn instanceof Pokemon))
data.team[idx] = new Pokemon(pkmn)
Object.assign(state, data)
,
// [...] skipping review-unrelated methods
,
actions:
async init ( commit )
let state =
await lf.iterate((value, key) =>
state[key] = value
)
await commit('init', state)
)
export default store
In the store's init action/mutation I re-set the raw JSON data from IndexedDb to the corresponding classes so I can use their methods properly after loading them.
Explore.vue
<template>
<div class="team">
<!-- Team -->
<team-pkmn
v-for="pokemon in $store.state.team"
:key="pokemon.id"
:pokemon="pokemon"
/>
</div>
<!-- [...] skipping review-unrelated code -->
</div>
</template>
<script>
import Pokedex from 'pokeapi-js-wrapper'
import Pokemon from '../../classes/Pokemon'
import TeamPkmn from '../Pokemon/TeamPkmn'
const P = new Pokedex()
export default
components:
TeamPkmn
,
data ()
return
,
async created ()
await this.$store.dispatch('init')
if (!this.$store.state.team.length)
const ids = [679, 10, 13, 46, 48, 165, 167, 333, 290, 557, 736, 595, 742, 751, 349, 220, 366]
for (const id of ids.reverse())
const p = await Pokemon.constructFromApiResponse(
await P.resource('/api/v2/pokemon/' + id), 5
)
this.$store.state.team.push(p)
</script>
In the component's async created method I load some Pokémon from the Api, and create the Pokemon-object with the constructFromApiResponse-method, if none are in the store, so not loaded from the client's Db.
javascript object-oriented async-await vue.js pokemon
$endgroup$
add a comment |
$begingroup$
So, this is for a clicker game I'm trying to build with Pokémon data drawn from the PokéAPI. Since I'm using Vue.js for the project, Vuex is used to store and share the data between components and I'm persisting and loading the state with LocalForage to IndexedDb.
When constructing a Pokemon-object from the Pokemon-class I'm having two different ways I get the data to construct the object.
From the API I'll get the data structured one way and from the IndexedDb I get the 1to1 JSON-stringified version of the Pokemon-object. So just the raw JSON without the class-methods or any properties tied to other custom classes or any enums. You probably already knew that, but I didn't.
(At first I was just using an init-method to set the async properties, but that changed when I started using IndexedDb)
That's why I got the basic constructor method to build the object anew from the saved object in the IndexedDb and a constuctFromApiResponse method to map the pokeapi response to my Pokemon-object.
I'm trying to figure out what's the best way to write as little code as possible to stay DRY. (while keeping any type-inferences and other goodies that PHPStorm is able to make without much usage of JSHint (although I'm fine with adding hints, just didn't add them here everywhere because I'm going back and forth between the different constructor-approaches))
Pokemon.js
import Item from './Item'
import Pokedex from 'pokeapi-js-wrapper'
import Type from './Type'
const P = new Pokedex()
/**
* @class Pokemon
*/
class Pokemon
constructor (
dexNum, name, level, types, baseStats, stats, sprites, hp, exp, item,
growthRate = '', catchRate = 0, evolutions =
)
Object.assign(this,
dexNum,
name,
level,
baseStats,
stats,
hp,
exp,
sprites,
growthRate,
catchRate,
evolutions
)
/**
* @type Type[]
*/
this.types = types.map((type) => Type[type.name.toUpperCase()])
/**
* @type Move[]
*/
this.moves = []
/**
* @type Item
*/
this.item = item ? (item instanceof Item ? item : new Item(item)) : null
/* eslint-disable-next-line camelcase */
static async constructFromApiResponse ( id, name, types, stats, base_experience, sprites , level)
const self =
dexNum: id,
name,
level,
sprites,
hp: 0,
exp: 0,
item: null
self.types = types.map((type) => Type[type.type.name.toUpperCase()])
self.baseStats =
for (const stat of stats)
self.baseStats[stat.stat.name] = stat.base_stat
/* eslint-disable-next-line camelcase */
self.baseStats.exp = base_experience
self.stats = self.baseStats
self.hp = self.stats.hp * level
const p = new Pokemon(self)
p.calculateStats()
await p.setGrowthRate()
await p.setCatchRate()
await p.setEvolutionChain()
return p
async setGrowthRate ()
this.growthRate = await this.getSpecies()
.then((response) => response.growth_rate.name)
async setCatchRate ()
this.catchRate = await this.getSpecies()
.then((response) => response.capture_rate)
async setEvolutionChain ()
const evolvesTo = (next, prevDexNum) =>
for (const ev of next)
const details = ev.evolution_details[0]
const evolution =
if (!this.evolutions.hasOwnProperty(prevDexNum))
this.evolutions[prevDexNum] = []
this.evolutions[prevDexNum].push(evolution)
// recurse
if (ev.hasOwnProperty('evolves_to') &&
ev.evolves_to.length > 0)
evolvesTo(ev.evolves_to, evolution.dexnum)
const evoChainUrl = await this.getSpecies()
.then((response) => response.evolution_chain.url)
await P.resource(evoChainUrl)
.then((response) => evolvesTo(
response.chain.evolves_to,
response.chain.species.url.split('/').slice(-2, -1)[0])
)
/**
* Get species entry
*
* @returns Promise
*/
getSpecies ()
return P.resource('/api/v2/pokemon-species/' + this.dexNum)
/**
* @param toDexNum number
*
* @returns Promise<void>
*/
async evolve (toDexNum = 0)
// [...] skipping review-unrelated methods
export default Pokemon
I was doing this.name = name before, but was looking for other ways to set the properties without repeating myself that much. But I don't know if the Object.assign method is really considered "cleaner" or better to read.
Also depending on the constructor-approach, maybe there's a better way to re-set the properties for the evolution in the evolve-method.
In addition I don't know what to do about the many function parameters. eslint with standardjs doesn't seem to have an opinion about this.
Item.js
/**
* @class Item
*/
class Item
constructor ( id, name, cost, attributes, category, sprites, quantity, effect, triggerOnHold, onUse )
Object.assign(this,
id,
name,
cost,
attributes,
category,
sprites,
quantity,
effect,
triggerOnHold,
onUse
)
static async constructFromApiResponse ( id, name, cost, attributes, category, sprites , quantity, effect, triggerOnHold, onUse)
const self =
id,
name,
cost,
attributes,
category: category.name,
sprites,
quantity,
effect,
triggerOnHold,
onUse
const item = new Item(self)
return item
// [...] skipping review-unrelated methods
export default Item
Same thing with Item, although I don't have properties here that need any special treatment just yet. That's when I thought I'd have another look at TypeScript and saw that you can just omit the constructor function body when you're just doing assignments like this.name = name. Maybe I'll rewrite it in TypeScript then if that leads me to DRY-haven. But I'm not sure about that yet.
--
Following are the two files where I'm using the two different Pokemon-constructor methods, for reference.
store.js
import Item from './classes/Item'
import LocalForage from 'localforage'
import Pokemon from './classes/Pokemon'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const lf = LocalForage.createInstance(
driver: LocalForage.INDEXEDDB,
name: 'PokeClicker',
version: 1.0,
storeName: 'pokeclicker'
)
const persistPlugin = store =>
store.subscribe((mutations, state) =>
for (const key in state)
if (!state.hasOwnProperty(key))
continue
const value = state[key]
lf.setItem(key, value)
)
const store = new Vuex.Store(
plugins: [
persistPlugin
],
state:
bags:
misc: [],
medicine: [],
pokeballs: [],
// machines: [],
berries: [],
// mail: [],
battle: [],
key: []
,
team: [],
box: [],
money: 0,
reputation: 0,
route: ,
opponent: ,
enemy:
,
mutations:
async init (state, data)
// init bag
for (const key in data.bags)
if (!data.bags.hasOwnProperty(key))
continue
const bag = data.bags[key]
for (const idx in bag)
const item = bag[idx]
data.bags[bag][key] = new Item(item)
// init team
for (const idx in data.team)
const pkmn = data.team[idx]
if (!(pkmn instanceof Pokemon))
data.team[idx] = new Pokemon(pkmn)
Object.assign(state, data)
,
// [...] skipping review-unrelated methods
,
actions:
async init ( commit )
let state =
await lf.iterate((value, key) =>
state[key] = value
)
await commit('init', state)
)
export default store
In the store's init action/mutation I re-set the raw JSON data from IndexedDb to the corresponding classes so I can use their methods properly after loading them.
Explore.vue
<template>
<div class="team">
<!-- Team -->
<team-pkmn
v-for="pokemon in $store.state.team"
:key="pokemon.id"
:pokemon="pokemon"
/>
</div>
<!-- [...] skipping review-unrelated code -->
</div>
</template>
<script>
import Pokedex from 'pokeapi-js-wrapper'
import Pokemon from '../../classes/Pokemon'
import TeamPkmn from '../Pokemon/TeamPkmn'
const P = new Pokedex()
export default
components:
TeamPkmn
,
data ()
return
,
async created ()
await this.$store.dispatch('init')
if (!this.$store.state.team.length)
const ids = [679, 10, 13, 46, 48, 165, 167, 333, 290, 557, 736, 595, 742, 751, 349, 220, 366]
for (const id of ids.reverse())
const p = await Pokemon.constructFromApiResponse(
await P.resource('/api/v2/pokemon/' + id), 5
)
this.$store.state.team.push(p)
</script>
In the component's async created method I load some Pokémon from the Api, and create the Pokemon-object with the constructFromApiResponse-method, if none are in the store, so not loaded from the client's Db.
javascript object-oriented async-await vue.js pokemon
$endgroup$
add a comment |
$begingroup$
So, this is for a clicker game I'm trying to build with Pokémon data drawn from the PokéAPI. Since I'm using Vue.js for the project, Vuex is used to store and share the data between components and I'm persisting and loading the state with LocalForage to IndexedDb.
When constructing a Pokemon-object from the Pokemon-class I'm having two different ways I get the data to construct the object.
From the API I'll get the data structured one way and from the IndexedDb I get the 1to1 JSON-stringified version of the Pokemon-object. So just the raw JSON without the class-methods or any properties tied to other custom classes or any enums. You probably already knew that, but I didn't.
(At first I was just using an init-method to set the async properties, but that changed when I started using IndexedDb)
That's why I got the basic constructor method to build the object anew from the saved object in the IndexedDb and a constuctFromApiResponse method to map the pokeapi response to my Pokemon-object.
I'm trying to figure out what's the best way to write as little code as possible to stay DRY. (while keeping any type-inferences and other goodies that PHPStorm is able to make without much usage of JSHint (although I'm fine with adding hints, just didn't add them here everywhere because I'm going back and forth between the different constructor-approaches))
Pokemon.js
import Item from './Item'
import Pokedex from 'pokeapi-js-wrapper'
import Type from './Type'
const P = new Pokedex()
/**
* @class Pokemon
*/
class Pokemon
constructor (
dexNum, name, level, types, baseStats, stats, sprites, hp, exp, item,
growthRate = '', catchRate = 0, evolutions =
)
Object.assign(this,
dexNum,
name,
level,
baseStats,
stats,
hp,
exp,
sprites,
growthRate,
catchRate,
evolutions
)
/**
* @type Type[]
*/
this.types = types.map((type) => Type[type.name.toUpperCase()])
/**
* @type Move[]
*/
this.moves = []
/**
* @type Item
*/
this.item = item ? (item instanceof Item ? item : new Item(item)) : null
/* eslint-disable-next-line camelcase */
static async constructFromApiResponse ( id, name, types, stats, base_experience, sprites , level)
const self =
dexNum: id,
name,
level,
sprites,
hp: 0,
exp: 0,
item: null
self.types = types.map((type) => Type[type.type.name.toUpperCase()])
self.baseStats =
for (const stat of stats)
self.baseStats[stat.stat.name] = stat.base_stat
/* eslint-disable-next-line camelcase */
self.baseStats.exp = base_experience
self.stats = self.baseStats
self.hp = self.stats.hp * level
const p = new Pokemon(self)
p.calculateStats()
await p.setGrowthRate()
await p.setCatchRate()
await p.setEvolutionChain()
return p
async setGrowthRate ()
this.growthRate = await this.getSpecies()
.then((response) => response.growth_rate.name)
async setCatchRate ()
this.catchRate = await this.getSpecies()
.then((response) => response.capture_rate)
async setEvolutionChain ()
const evolvesTo = (next, prevDexNum) =>
for (const ev of next)
const details = ev.evolution_details[0]
const evolution =
if (!this.evolutions.hasOwnProperty(prevDexNum))
this.evolutions[prevDexNum] = []
this.evolutions[prevDexNum].push(evolution)
// recurse
if (ev.hasOwnProperty('evolves_to') &&
ev.evolves_to.length > 0)
evolvesTo(ev.evolves_to, evolution.dexnum)
const evoChainUrl = await this.getSpecies()
.then((response) => response.evolution_chain.url)
await P.resource(evoChainUrl)
.then((response) => evolvesTo(
response.chain.evolves_to,
response.chain.species.url.split('/').slice(-2, -1)[0])
)
/**
* Get species entry
*
* @returns Promise
*/
getSpecies ()
return P.resource('/api/v2/pokemon-species/' + this.dexNum)
/**
* @param toDexNum number
*
* @returns Promise<void>
*/
async evolve (toDexNum = 0)
// [...] skipping review-unrelated methods
export default Pokemon
I was doing this.name = name before, but was looking for other ways to set the properties without repeating myself that much. But I don't know if the Object.assign method is really considered "cleaner" or better to read.
Also depending on the constructor-approach, maybe there's a better way to re-set the properties for the evolution in the evolve-method.
In addition I don't know what to do about the many function parameters. eslint with standardjs doesn't seem to have an opinion about this.
Item.js
/**
* @class Item
*/
class Item
constructor ( id, name, cost, attributes, category, sprites, quantity, effect, triggerOnHold, onUse )
Object.assign(this,
id,
name,
cost,
attributes,
category,
sprites,
quantity,
effect,
triggerOnHold,
onUse
)
static async constructFromApiResponse ( id, name, cost, attributes, category, sprites , quantity, effect, triggerOnHold, onUse)
const self =
id,
name,
cost,
attributes,
category: category.name,
sprites,
quantity,
effect,
triggerOnHold,
onUse
const item = new Item(self)
return item
// [...] skipping review-unrelated methods
export default Item
Same thing with Item, although I don't have properties here that need any special treatment just yet. That's when I thought I'd have another look at TypeScript and saw that you can just omit the constructor function body when you're just doing assignments like this.name = name. Maybe I'll rewrite it in TypeScript then if that leads me to DRY-haven. But I'm not sure about that yet.
--
Following are the two files where I'm using the two different Pokemon-constructor methods, for reference.
store.js
import Item from './classes/Item'
import LocalForage from 'localforage'
import Pokemon from './classes/Pokemon'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const lf = LocalForage.createInstance(
driver: LocalForage.INDEXEDDB,
name: 'PokeClicker',
version: 1.0,
storeName: 'pokeclicker'
)
const persistPlugin = store =>
store.subscribe((mutations, state) =>
for (const key in state)
if (!state.hasOwnProperty(key))
continue
const value = state[key]
lf.setItem(key, value)
)
const store = new Vuex.Store(
plugins: [
persistPlugin
],
state:
bags:
misc: [],
medicine: [],
pokeballs: [],
// machines: [],
berries: [],
// mail: [],
battle: [],
key: []
,
team: [],
box: [],
money: 0,
reputation: 0,
route: ,
opponent: ,
enemy:
,
mutations:
async init (state, data)
// init bag
for (const key in data.bags)
if (!data.bags.hasOwnProperty(key))
continue
const bag = data.bags[key]
for (const idx in bag)
const item = bag[idx]
data.bags[bag][key] = new Item(item)
// init team
for (const idx in data.team)
const pkmn = data.team[idx]
if (!(pkmn instanceof Pokemon))
data.team[idx] = new Pokemon(pkmn)
Object.assign(state, data)
,
// [...] skipping review-unrelated methods
,
actions:
async init ( commit )
let state =
await lf.iterate((value, key) =>
state[key] = value
)
await commit('init', state)
)
export default store
In the store's init action/mutation I re-set the raw JSON data from IndexedDb to the corresponding classes so I can use their methods properly after loading them.
Explore.vue
<template>
<div class="team">
<!-- Team -->
<team-pkmn
v-for="pokemon in $store.state.team"
:key="pokemon.id"
:pokemon="pokemon"
/>
</div>
<!-- [...] skipping review-unrelated code -->
</div>
</template>
<script>
import Pokedex from 'pokeapi-js-wrapper'
import Pokemon from '../../classes/Pokemon'
import TeamPkmn from '../Pokemon/TeamPkmn'
const P = new Pokedex()
export default
components:
TeamPkmn
,
data ()
return
,
async created ()
await this.$store.dispatch('init')
if (!this.$store.state.team.length)
const ids = [679, 10, 13, 46, 48, 165, 167, 333, 290, 557, 736, 595, 742, 751, 349, 220, 366]
for (const id of ids.reverse())
const p = await Pokemon.constructFromApiResponse(
await P.resource('/api/v2/pokemon/' + id), 5
)
this.$store.state.team.push(p)
</script>
In the component's async created method I load some Pokémon from the Api, and create the Pokemon-object with the constructFromApiResponse-method, if none are in the store, so not loaded from the client's Db.
javascript object-oriented async-await vue.js pokemon
$endgroup$
So, this is for a clicker game I'm trying to build with Pokémon data drawn from the PokéAPI. Since I'm using Vue.js for the project, Vuex is used to store and share the data between components and I'm persisting and loading the state with LocalForage to IndexedDb.
When constructing a Pokemon-object from the Pokemon-class I'm having two different ways I get the data to construct the object.
From the API I'll get the data structured one way and from the IndexedDb I get the 1to1 JSON-stringified version of the Pokemon-object. So just the raw JSON without the class-methods or any properties tied to other custom classes or any enums. You probably already knew that, but I didn't.
(At first I was just using an init-method to set the async properties, but that changed when I started using IndexedDb)
That's why I got the basic constructor method to build the object anew from the saved object in the IndexedDb and a constuctFromApiResponse method to map the pokeapi response to my Pokemon-object.
I'm trying to figure out what's the best way to write as little code as possible to stay DRY. (while keeping any type-inferences and other goodies that PHPStorm is able to make without much usage of JSHint (although I'm fine with adding hints, just didn't add them here everywhere because I'm going back and forth between the different constructor-approaches))
Pokemon.js
import Item from './Item'
import Pokedex from 'pokeapi-js-wrapper'
import Type from './Type'
const P = new Pokedex()
/**
* @class Pokemon
*/
class Pokemon
constructor (
dexNum, name, level, types, baseStats, stats, sprites, hp, exp, item,
growthRate = '', catchRate = 0, evolutions =
)
Object.assign(this,
dexNum,
name,
level,
baseStats,
stats,
hp,
exp,
sprites,
growthRate,
catchRate,
evolutions
)
/**
* @type Type[]
*/
this.types = types.map((type) => Type[type.name.toUpperCase()])
/**
* @type Move[]
*/
this.moves = []
/**
* @type Item
*/
this.item = item ? (item instanceof Item ? item : new Item(item)) : null
/* eslint-disable-next-line camelcase */
static async constructFromApiResponse ( id, name, types, stats, base_experience, sprites , level)
const self =
dexNum: id,
name,
level,
sprites,
hp: 0,
exp: 0,
item: null
self.types = types.map((type) => Type[type.type.name.toUpperCase()])
self.baseStats =
for (const stat of stats)
self.baseStats[stat.stat.name] = stat.base_stat
/* eslint-disable-next-line camelcase */
self.baseStats.exp = base_experience
self.stats = self.baseStats
self.hp = self.stats.hp * level
const p = new Pokemon(self)
p.calculateStats()
await p.setGrowthRate()
await p.setCatchRate()
await p.setEvolutionChain()
return p
async setGrowthRate ()
this.growthRate = await this.getSpecies()
.then((response) => response.growth_rate.name)
async setCatchRate ()
this.catchRate = await this.getSpecies()
.then((response) => response.capture_rate)
async setEvolutionChain ()
const evolvesTo = (next, prevDexNum) =>
for (const ev of next)
const details = ev.evolution_details[0]
const evolution =
if (!this.evolutions.hasOwnProperty(prevDexNum))
this.evolutions[prevDexNum] = []
this.evolutions[prevDexNum].push(evolution)
// recurse
if (ev.hasOwnProperty('evolves_to') &&
ev.evolves_to.length > 0)
evolvesTo(ev.evolves_to, evolution.dexnum)
const evoChainUrl = await this.getSpecies()
.then((response) => response.evolution_chain.url)
await P.resource(evoChainUrl)
.then((response) => evolvesTo(
response.chain.evolves_to,
response.chain.species.url.split('/').slice(-2, -1)[0])
)
/**
* Get species entry
*
* @returns Promise
*/
getSpecies ()
return P.resource('/api/v2/pokemon-species/' + this.dexNum)
/**
* @param toDexNum number
*
* @returns Promise<void>
*/
async evolve (toDexNum = 0)
// [...] skipping review-unrelated methods
export default Pokemon
I was doing this.name = name before, but was looking for other ways to set the properties without repeating myself that much. But I don't know if the Object.assign method is really considered "cleaner" or better to read.
Also depending on the constructor-approach, maybe there's a better way to re-set the properties for the evolution in the evolve-method.
In addition I don't know what to do about the many function parameters. eslint with standardjs doesn't seem to have an opinion about this.
Item.js
/**
* @class Item
*/
class Item
constructor ( id, name, cost, attributes, category, sprites, quantity, effect, triggerOnHold, onUse )
Object.assign(this,
id,
name,
cost,
attributes,
category,
sprites,
quantity,
effect,
triggerOnHold,
onUse
)
static async constructFromApiResponse ( id, name, cost, attributes, category, sprites , quantity, effect, triggerOnHold, onUse)
const self =
id,
name,
cost,
attributes,
category: category.name,
sprites,
quantity,
effect,
triggerOnHold,
onUse
const item = new Item(self)
return item
// [...] skipping review-unrelated methods
export default Item
Same thing with Item, although I don't have properties here that need any special treatment just yet. That's when I thought I'd have another look at TypeScript and saw that you can just omit the constructor function body when you're just doing assignments like this.name = name. Maybe I'll rewrite it in TypeScript then if that leads me to DRY-haven. But I'm not sure about that yet.
--
Following are the two files where I'm using the two different Pokemon-constructor methods, for reference.
store.js
import Item from './classes/Item'
import LocalForage from 'localforage'
import Pokemon from './classes/Pokemon'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const lf = LocalForage.createInstance(
driver: LocalForage.INDEXEDDB,
name: 'PokeClicker',
version: 1.0,
storeName: 'pokeclicker'
)
const persistPlugin = store =>
store.subscribe((mutations, state) =>
for (const key in state)
if (!state.hasOwnProperty(key))
continue
const value = state[key]
lf.setItem(key, value)
)
const store = new Vuex.Store(
plugins: [
persistPlugin
],
state:
bags:
misc: [],
medicine: [],
pokeballs: [],
// machines: [],
berries: [],
// mail: [],
battle: [],
key: []
,
team: [],
box: [],
money: 0,
reputation: 0,
route: ,
opponent: ,
enemy:
,
mutations:
async init (state, data)
// init bag
for (const key in data.bags)
if (!data.bags.hasOwnProperty(key))
continue
const bag = data.bags[key]
for (const idx in bag)
const item = bag[idx]
data.bags[bag][key] = new Item(item)
// init team
for (const idx in data.team)
const pkmn = data.team[idx]
if (!(pkmn instanceof Pokemon))
data.team[idx] = new Pokemon(pkmn)
Object.assign(state, data)
,
// [...] skipping review-unrelated methods
,
actions:
async init ( commit )
let state =
await lf.iterate((value, key) =>
state[key] = value
)
await commit('init', state)
)
export default store
In the store's init action/mutation I re-set the raw JSON data from IndexedDb to the corresponding classes so I can use their methods properly after loading them.
Explore.vue
<template>
<div class="team">
<!-- Team -->
<team-pkmn
v-for="pokemon in $store.state.team"
:key="pokemon.id"
:pokemon="pokemon"
/>
</div>
<!-- [...] skipping review-unrelated code -->
</div>
</template>
<script>
import Pokedex from 'pokeapi-js-wrapper'
import Pokemon from '../../classes/Pokemon'
import TeamPkmn from '../Pokemon/TeamPkmn'
const P = new Pokedex()
export default
components:
TeamPkmn
,
data ()
return
,
async created ()
await this.$store.dispatch('init')
if (!this.$store.state.team.length)
const ids = [679, 10, 13, 46, 48, 165, 167, 333, 290, 557, 736, 595, 742, 751, 349, 220, 366]
for (const id of ids.reverse())
const p = await Pokemon.constructFromApiResponse(
await P.resource('/api/v2/pokemon/' + id), 5
)
this.$store.state.team.push(p)
</script>
In the component's async created method I load some Pokémon from the Api, and create the Pokemon-object with the constructFromApiResponse-method, if none are in the store, so not loaded from the client's Db.
javascript object-oriented async-await vue.js pokemon
javascript object-oriented async-await vue.js pokemon
edited Mar 13 at 17:11
200_success
130k17153419
130k17153419
asked Mar 13 at 16:09
DRogueRoninDRogueRonin
386
386
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
To answer one part of your question, I think you can assign all the properties at once with Object.assign:
class Pokemon
constructor(data)
Object.assign(this, data);
getStats()
return this.species + ' ' + this.age;
const json = '"species":"pikachu", "age":12'
const obj = JSON.parse(json);
let u = new Pokemon(obj)
console.log(u.getStats()). // will log pikachu 12
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215357%2fclass-constructor-with-async-properties-set-from-poke-api%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
To answer one part of your question, I think you can assign all the properties at once with Object.assign:
class Pokemon
constructor(data)
Object.assign(this, data);
getStats()
return this.species + ' ' + this.age;
const json = '"species":"pikachu", "age":12'
const obj = JSON.parse(json);
let u = new Pokemon(obj)
console.log(u.getStats()). // will log pikachu 12
$endgroup$
add a comment |
$begingroup$
To answer one part of your question, I think you can assign all the properties at once with Object.assign:
class Pokemon
constructor(data)
Object.assign(this, data);
getStats()
return this.species + ' ' + this.age;
const json = '"species":"pikachu", "age":12'
const obj = JSON.parse(json);
let u = new Pokemon(obj)
console.log(u.getStats()). // will log pikachu 12
$endgroup$
add a comment |
$begingroup$
To answer one part of your question, I think you can assign all the properties at once with Object.assign:
class Pokemon
constructor(data)
Object.assign(this, data);
getStats()
return this.species + ' ' + this.age;
const json = '"species":"pikachu", "age":12'
const obj = JSON.parse(json);
let u = new Pokemon(obj)
console.log(u.getStats()). // will log pikachu 12
$endgroup$
To answer one part of your question, I think you can assign all the properties at once with Object.assign:
class Pokemon
constructor(data)
Object.assign(this, data);
getStats()
return this.species + ' ' + this.age;
const json = '"species":"pikachu", "age":12'
const obj = JSON.parse(json);
let u = new Pokemon(obj)
console.log(u.getStats()). // will log pikachu 12
answered 19 mins ago
KokodokoKokodoko
21117
21117
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215357%2fclass-constructor-with-async-properties-set-from-poke-api%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown