Currently I'm using Babel to write a Node.js backend in ES6. Unfortunately I encountered a strange behaviour when extending a specific class. Some of my methods, defined in the extending class, are undefined. Unless I use the ES7 syntax to bind them to a property.
This is the actual code that gives me this strange behaviour:
import { Router } from 'express';
class MyCustomRouter extends Router
{
constructor() {
super();
this.methodWorks(); // works like a charm.
this.methodDoesnt(); // throws TypeError: _this.methodDoesnt is not a function
}
methodWorks = () => {
// some content
}
methodDoesnt() {
// some content
}
}
This is actually extends the Router from the expressjs library. So right now I'm just curious if someone could explain this behaviour and/or if there is a way to fix this.
I went looking inside the code of ExpressJS itself to find some explanation. Apparently they like to return a whole new and different context from the Router's constructor. Which explains why this is totally different and not containing my methods...
Related
In my Nuxt app I'm registering app services in a plugin file (e.g. /plugins/services.js) like this...
import FeatureOneService from '#/services/feature-one-service.js'
import FeatureTwoService from '#/services/feature-two-service.js'
import FeatureThreeService from '#/services/feature-three-service.js'
import FeatureFourService from '#/services/feature-four-service.js'
import FeatureFiveService from '#/services/feature-five-service.js'
export default (ctx, inject) => {
inject('feature1', new FeatureOneService(ctx))
inject('feature2', new FeatureTwoService(ctx))
inject('feature3', new FeatureThreeService(ctx))
inject('feature4', new FeatureFourService(ctx))
inject('feature5', new FeatureFiveService(ctx))
}
After doing this I can access any of my service on vue instance like this.$feature1.someMethod()
It works but I've once concern, that is, this approach loads all services globally. So whatever page the user visits all these services must be loaded.
Now I've 20+ such services in my app and this does not seem optimal approach to me.
The other approach I was wondering is to export a singleton instance within each service class and import this class instance in any component which needs that service.
So basically in my service class (e.g. feature-one-service.js) I would do like to do it like this..
export default new FeatureOneService() <---- I'm not sure how to pass nuxt instance in a .js file?
and import it my component where it is required like so...
import FeatureOneService from '#/services/feature-one-service.js'
What approach do you think is most feasible? if its the second one, then how to pass nuxt instance to my singleton class?
Yep, loading everything globally is not optimal in terms of performance.
You will need to either try to use JS files and pass down the Vue instance there.
Or use mixins, this is not optimal but it is pretty much the only solution in terms of reusability with Vue2.
Vue3 (composition API) brings composables, which is a far better approach regarding reusability (thing React hooks).
I've been struggling a lot with it and the only solution is probably to inject services to the global Vue instance at the component/page level during the initialisation (in created hook), another option is to do that in the middleware (or anywhere else where you have access to the nuxt context. Otherwise you won't be able to pass nuxt context to the service.
I usually set up services as classes, call them where necessary, and pass in the properties of the context which the class depends on as constructor arguments.
So for example, a basic MeiliSearchService class might look like:
export class MeilisearchService {
#client: MeiliSearch
constructor($config: NuxtRuntimeConfig) {
super()
this.#client = new MeiliSearch({
host: $config.services.meilisearch.host,
apiKey: $config.services.meilisearch.key
})
}
...
someMethod() {
let doSomething = this.#client.method()
...
}
...
}
Then wherever you need to use the service, just new up an instance (passing in whatever it needs) and make it available to the component.
data() {
const meiliSearchService = new MeiliSearchService(this.$config)
return {
meiliSearchService,
results,
...
}
},
methods: {
search(query) {
...
this.results = this.meiliSearchService.search(query)
...
}
}
As I'm sure you know, some context properties are only available in certain Nuxt life-cycle hooks. I find most of what I need is available everywhere, including $config, store, $route, $router etc.
Don't forget about Vue's reactivity when using this approach. For example, using a getter method on your service class will return the most recent value only when explicitly called. You can't, for example, stick the getter method in a computed() property and expect reactivty.
<div v-for='result in latestSearchResults'>
...
</div>
...
computed: {
latestSearchResults() {
return this.#client.getLatestResults()
}
}
Instead, call the method like:
methods: {
getLatestResults() {
return this.#client.getLatestResults()
}
}
I set the aurelia-store up as per the docs; in the main.ts at the bottom of all the plugins (from the skeleton app with dotnet core) I have as the last plugin defined:
aurelia.use.standardConfiguration()
.plugin(PLATFORM.moduleName('aurelia-store'), { initialState })
Then my app needs to login the user and save their bearer token.
await aurelia.start();
await aurelia.setRoot(PLATFORM.moduleName("modules/login/login.vm"));
In the login class I am trying to use the #connectTo decorator. However it never sets the dependency property. So I am stuck on this simple part at the very start of the app and my work already suggested not to use Aurelia but I said I wanted to for fast POC.
I've copied the docs exactly and still have the issue. Notably, I had to turn off strictNullCheck in the tsconfig to make the doc code parse.
Login.ts
#connectTo({
target: 'state',
selector: {
userToken: (store) => store.state.pipe(pluck('userToken')),
loginRedirected: (store) => store.state.pipe(pluck('loginRedirected'))
}
})
export class Login {
static inject = [Aurelia, Store]
public state: State;
app: Aurelia;
constructor(Aurelia, private store: Store<State>) {
this.app = Aurelia
store.registerAction('ChangeUserToken', this.changeUserToken)
store.registerAction('LoginRedirected', this.loginRedirect)
}
activate() {
... this.state is always undefined.
if (!this.state.loginRedirected) { //error
}
}
}
I expect the this.state property to have a state object populated from the global state store with the initialState values.
e.g.
{ userToken: "", loginRedirected: false }
I just need to set the userToken in login and retrieve it in app.js. This is not possible; what could be missing to make this basic function actually work?
ConnectTo is a helper decorator to avoid manual state subscriptions since the Stream of states is a vanilla rxjs observable. If you take a closer look at the official plugin documentation you will notice that it sets up the subscription in a different lifecycle hook.
That said connectTo cant solve everything and with manual subscription you have the most flexibility.
Dont give up with your quest you just had bad luck of falling into a more complicated scenario of startup timing right at the begin which easy enough might bite you with lots of other Frameworks/Libraries as well. Also make sure to visit the official discourse.aurelia.io forum and post back solutions to SO.
I have an Aurelia app using Aurelia Store. I'm having some trouble when using the #connectTo decorator in an Aurelia pipeline step.
I have added the following step to my config pipeline:
config.addPipelineStep('authorize', AuthorizeStep);
And this step looks like:
#connectTo()
export class AuthorizeStep {
state: State;
run(navigationInstruction, next) {
if (navigationInstruction.getAllInstructions().find(x => x.config.isAdmin))
{
if (!this.state.user.isAdmin) {
return next.cancel();
}
}
return next();
}
}
However, my state is always undefined. Looking at other parts of my project, I can see the state and user are being populated, but it seems like in this AuthorizeStep it doesn't seem to work.
I think this issue may be due to the fact that my AuthorizeStep doesn't have a bind lifecycle method, but if so, what can I do about this?
The maintainers of Aurelia responded (only after I raised an issue on their GitHub) here.
Basically, as the bind lifecycle does not exist within this class, the #connectTo decorator won't work. Instead, I will need to manually inject the Store and subscribing to the state.
I'm creating a plugin and I just wonder why I can't access it in main.js file. Here's how Auth.js looks like:
const Auth = {
install(Vue) {
Vue.prototype.$isGuest = function () {
console.log('This user is a guest.');
}
Vue.prototype.$getAuthToken = function () {
console.log('Auth token will be returned.');
}
}
}
export default Auth
This is main.js:
import Auth from '#/helper/Auth'
Vue.use(Auth)
However, when I execute console.log(this.$isGuest()), it doesn't work. It actually returns the following:
main.js?1c90:25 Uncaught TypeError: this.$isGuest is not a function
The problem is that this method works when I call it in components such as Dashboard.vue and things like that.
I have a way to avoid calling isGuest method within main.js (I can call it in Layout.vue), but I'm more curious why it doesn't work in main.js.
Maybe because Vue hasn't been initialized yet, but even if I put the console.log() line at the end of the file, still doesn't work.
Thanks,
N.
If you are calling this.$isGuest() outside of Vue, you will get the error you describe. That's because this is not a Vue object. What this is depends on how you are building your code, but given you are using import it's probably the module.
Also, you are adding $isGuest to the prototype of Vue. That means that the function is only going to be available on actual instances of Vue objects. That is why it is available in your components.
If you want to use it in the main script, the only place you will be able to get to it is inside the Vue object in a lifecycle handler, method, or computed. For example:
new Vue({
mounted(){
console.log(this.$isGuest()) // this should work
}
})
Basically, the question says it all :). The scenario is as follows: I have some python models that are being passed between the browser, and the server. Python convention for naming things is to use underscores, and js convention is to camelCase everything. So I figured, I'd just create an http request interceptor to convert between python_models and jsModels. Is there a simple way to accomplish that?
I'm also looking for a way to do the inverse, so camelCase to that-case :)
If you're using aurelia-http-client, you can use a reviver.
import {HttpClient} from 'aurelia-http-client';
import {Person} from './models';
export class PersonService {
constructor(){
this.http = new HttpClient().configure(x=> {
x.withReviver((k,v) => {
return typeof v === 'object' ? new Person(v) : v;
});
});
}
getPeople(){
return this.http.get('/people');
}
}
This only works for aurelia-http-client and not aurelia-fetch-client. It has been talked about in the fetch spec, but I don't believe it is currently implemented.
Check the following for more information:
(my) Best Practices in Aurelia: The Model
https://github.com/whatwg/fetch/issues/104