How to get reference to the current router from ViewModel - aurelia

In my viewmodel class, how do I get a reference to the current Router?
What I really want to do is get the current ModuleId.
In Durandal, there was system.getModuleId, but there is no system in Durandal, so I figure the router is going to have that information.

The way to get a reference to the current router:
import {inject} from 'aurelia-framework'
import {router} from 'aurelia-router'
#inject(router)
constructor(router)
{
this.router = router; //Do something with router.
}
Note: Do not inject "AppRouter" It's a different router. If you add a route to AppRouter, it will not work. It will work if you import Router.

One way (not sure, the optimal one) to access current moduleId is in activate hook of your class:
activate(params, routeConfig) {
console.log(routeConfig.moduleId);
}

Related

Vue 3 access to app level provided instances from vue-router

I want to write some complicated guard logics in vue-router in Vue 3 to protect entering some routes according to store and my other provided modules. For example, I want to check if user profile info is present or not:
router.afterEach((to, from) => {
console.log('store: ', useStore());
const puex = usePuex();
puex.isReady().then(() => {
const me = puex.me.compute();
watch(me, (...params) => console.log('router: ', ...params));
});
});
In the above code, useStore and usePuex both try to inject store and puex instances from Vue app which are provided while being used in main.js bootstrap. But both use functions return undefined and I guess that the inject in this scope searches a different place where app-level provided instances do not exist.
So how can I inject them in the router file, or in other words how can I get store and puex instance using useStore and usePuex here?
I have found a way according this question but I still don't know if it is the best available solution. I can export the app instance from main.js file and then use app.$store and app.$puex instead. Although it works, I still think about a better solution to inject the store and puex instance using use functions (inject).
You still can add the navigation guards after that your app has mounted in main.js/ts, the code would look like:
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
const vm = createApp(App)
.use(puex)
.use(router)
.mount('#app');
router.afterEach((to, from) => {
const me = vm.puex.me.compute();
watch(me, (...params) => console.log('router: ', ...params));
});
You still can export that vm, to import it in the router file and use it the same way, but I really find somehow confusing, as main.js/ts is already importing the router file.

How to use ActivatedRoute in Angular 5?

I am trying to do exactly the same thing as in this post: Angular 4 get queryString
I am using Angular 5.2.5.
ActivatedRoute seems to be the thing to use to retrieve querystring values off the URL when the user first visits the website. However, I am unable to figure out what I need to import to be able to use ActivatedRoute.
Could someone specify exactly what needs to be added to the app.module.ts file, and the component.ts file where I am trying to use ActivatedRoute?
This post specifies adding routing to the imports array of the #NgModule: No provider for ActivatedRoute - Angular 2 RC5. However, I don't have an app.routing.ts file. Do I have to create an app.routing.ts file to use ActivatedRoute?
ActivatedRoute Contains the information about a route associated with a component loaded in an outlet.
It can also be used to pass data from one component to another component using route such as Id, flag, state etc.
http://localhost:4200/quiz/edit_quiz/032
032 being id of the quiz you wanna edit.
Get this id in your component(in my case let it be edit_quiz.compontent.ts) to use by using Activated Route.
Step 1: Import ActivatedRoute from Router module.
import { ActivatedRoute } from '#angular/router';
Step 2: Inject ActivatedRoute in constructor.
constructor(private activatedRoute: ActivatedRoute) { }
Step 3: Get id on a local variable named quizId in ngOnInit(){}
ngOnInit() {
this.quiz_id = this.activatedRoute.snapshot.params['id'];
}
Now we have id in edit_quiz.component.ts to use.
I made the two changes Arun suggested. Then, to fix the "No provider for ActivatedRoute" error, I made the changes shown below.
1) I added this line to the app.module.ts:
import { RouterModule } from '#angular/router';
2) I added this line to the imports array of the #NgModule in app.module.ts:
RouterModule.forRoot([])
This article gave me the fix: Angular error: no provider for ActivatedRoute
Now it compiles. Hooray!
You need to import ActivatedRoute from #angular/router like
import { ActivatedRoute } from '#angular/router';
then add this line to the imports array of the #NgModule in app.module.ts:
imports:[
........,
RouterModule.forRoot()
],
then you can use any where as below:
constructor(private route: ActivatedRoute) {
console.log(route.snapshot.queryParamMap); // this
}
// or
queryString : string;
getQueryString(){
this.queryString = this.route.queryParamMap.get('myQueryParam');
}
No. You don't need app.routing.ts if you don't have to navigate pages within your app.
How to Get Route Parameters:
The Angular Router provides two different methods to get route parameters:
a. Using the route snapshot(ActivatedRoute),
b. Using Router Observables
ActivatedRoute in Angular:
Provides access to information about a route associated with a component that is loaded in an outlet
Step-1 Import the ActivatedRoute interface
import { Router, ActivatedRoute } from '#angular/router';
Step-2 Inject the ActivatedRoute in Constructor
constructor(private route: ActivatedRoute, private router: Router) {}
Step-3 To fetch a employee object by the given id and assign that object to its local employee property.
ngOnInit() {
this.employee = new Employee();
this.id = this.route.snapshot.params['id'];
Note: Property Description
snapshot: The current snapshot of this route
ActivatedRoute
I'm late to the conversation but hope the following works for the future programmers who encounter the same issue.
import the ActivatedRoute
import { ActivatedRoute } from '#angular/router';
Inject the dependency injection
constructor(
private route: ActivatedRoute,
) { }
and to grab the id from the link you can use the following
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.product = products[+params.get('productId')];
});
}

Aurelia - View render before custom element is ready/bound

I have a very simple app with one view and one custom element.
app.html:
<template>
<require from="./content"></require>
hello
<content></content>
</template>
content.html:
<template>
${message}
</template>
content.js:
import {HttpClient} from 'aurelia-http-client';
import {inject} from 'aurelia-framework';
#inject(HttpClient)
export class Content {
constructor(http) {
this.http = http;
}
bind() {
return this.http.get('https://api.github.com/users/aurelia/repos')
.then(c => this.message = 'world');
}
}
Inside the bind life cycle event I have a REST service call (with a promise) which fetches me some data. The problem I have here is that if this asynchronous call take some time the view app.html will render before and then when the data is fetched it will be bound to the element. What I mean is that first the browser renders Hello and then after some time it renders world. I don't like this behavior on the specific site I'm working on. Instead I would prefer the browser to be blank and render all when everything is ready.
Much like if I'm working with server side rendering. Then the browser is waiting for the server to build up the complete response before it gets it and then renders it all.
The event activate() works in a view model but now I have a custom element. Is it possible to do something similar?
I need to fetch the data in the custom element not in the view model. Otherwise I know I could fetch it in the view model and later bind it to the element through a property. This is not possible for me.
Also I looked at this link but could not get it to work. Not sure if it is the same case as I have.
ANSWER
The link do in fact provide the correct answer. I can use the CompositionTransaction to have the view wait for the element. I believe I had some cached files and when changing the code it didn't work before I deleted the cache in my browser (chrome).
The answer is indeed to use CompositionTransaction in the case above.
This link shows an example of that.
The solution in my example is to change the view model content.js to something like this:
import {HttpClient} from 'aurelia-http-client';
import {inject, CompositionTransaction } from 'aurelia-framework';
#inject(HttpClient, CompositionTransaction)
export class Content {
constructor(http, compositionTransaction) {
this.http = http;
this.compositionTransactionNotifier = compositionTransaction.enlist();
}
bind() {
return this.http.get('https://api.github.com/users/aurelia/repos')
.then(c => {
this.message = 'world';
this.compositionTransactionNotifier.done();
});
}
}

How to inject child router to the dialog view-model

In our project we have bunch of custom elements like this:
<entity-link id="entity.id>
basically it just renders a link to edit entity screen
<template>
<a class="entity-link"
route-href="route: ENTITY_EDIT; params.bind: { id: entity.id }"
>${entity.name}
</a>
</template>
the problem is that this doesn't work at all within Aurelia Dialog context.
href attributed is not populated at all.
I tried to investigate the issue, I injected the router directly to dialog's view-model
import {Router} from 'aurelia-router';
#inject(DialogController, Router)
export default class RecordDetailsDialog {
constructor(dialogController:DialogController, router:Router) {
this.controller = dialogController;
this.router = router; /// WRONG INSTANCE!!!
}
}
and figured out the wrong instance of Router is being injected.
Main router (AppRouter) doesn't define ENTITY_EDIT route, it's added dynamically in child route configureRoute function.
I don't understand why the injected router is the main one instead of the one passed to the view which initiate dialog opening.
Any advice please
so after 2 hours of reading aurelia's source code I found out that DialogService instance is created within the root DI container which is associated with the root Router which is unaware about children routes.
I worked around our problem by registering DialogService instance manually within Child view-model container
import {Container} from 'aurelia-dependency-injection';
import {CompositionEngine} from 'aurelia-templating';
import {DialogService} from 'aurelia-dialog';
export class Main {
constructor(container:Container, compositionEngine:CompositionEngine){
container.registerInstance(DialogService, new DialogService(container, compositionEngine))`
}
...
}
but it feels hacky, still wondering if there is a more clean way to solve the problem.

Aurelia module global configuration

I have installed toastr in my Aurelia app. I am able to import and use it in different views, but I can't figure out how to set/modify its global options.
Ideas?
The most trivial place to do that is in you application constructor. You must also have your toaster script loaded at this point. Here's how your app.js could look like:
import {Router} from 'aurelia-router';
import toastr from 'toastr';
export class App {
static inject() { return [Router]; }
constructor(router) {
toastr.options.closeButton = true;
// ... setup your routing etc
}
}
I assume you already have your toaster script loaded at this point. You can also move toastr initialization to any other Aurelia constructor, or any other place, I don't see any technical restrictions.