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();
});
}
}
Related
The calendly widget works at first, but if you refresh the page it stops working but only when the website is live. In local development, no such issue occurs.
Also noticed that when I route to the page through navigation, it works. But if I enter the link to the specific page directly, it doesn't work.
Here's the code:
<template>
<client-only>
<vue-calendly url="link" :height="650"></vue-calendly>
</client-only>
</template>
<script>
import Vue from 'vue';
export default {
created() {
if (process.isClient) {
const VueCalendly = require('vue-calendly').default;
Vue.use(VueCalendly);
}
}
};
</script>
The Vue application is running on Gridsome so it's SSR. I set the widget to only display in client side. Not sure what the issue is.
There is a solution possible to integrate Calendly without using their widget. You can try it as well. This solution should not produce the error mentioned and was tried in an SSR application.
<template>
<!-- Calendly inline widget begin -->
<div class="calendly-inline-widget" data-url="YOUR_CALENDLY_URL" style="min-width:320px;height:630px;"></div>
<!-- Calendly inline widget end -->
</template>
<script>
export default {
mounted () {
const recaptchaScript = document.createElement('script')
recaptchaScript.setAttribute('src', 'https://assets.calendly.com/assets/external/widget.js')
document.head.appendChild(recaptchaScript)
}
}
</script>
From this link, we can see that he is importing the component with
import Vue from 'vue';
import VueCalendly from 'vue-calendly';
Vue.use(VueCalendly);
Then with
<vue-calendly url="your Calendly URL" :height="600"></vue-calendly>
I'm not sure if you are trying to use a syntax like es2020 import here but the require('vue-calendly').default is probably the issue here.
Try importing it in the basic way as suggested above, and then you will be able to make some lazy-loading of it later on if you wish.
Also, you may use your devtools to see why your Calendly instance is not present.
Addy Osmani did a great article on how to import on interaction if you're interested into optimizing your loading time. If it's not that much needed, simply use the usual method or even simpler, load the vanilla JS solution.
I have an Aurelia project with navigation in app.html and app.js. The project includes a home page that has a different style to it, including navigation that is different than the non-home page views.
I would like to turn off navigation for the home view so I tried setting a variable (showMenu) to toggle the visibility. In fact, I am able to use jQuery to do this, but I wonder if there is an Aurelia way of doing it. If I set this.showMenu to true it shows the menu container, and false hides it. Like this for example:
app.html
<div class="container" if.bind="showMenu">
app.js
constructor(router){
this.router = router;
this.showMenu = true;
...other things
}
What I would like to do is set showMenu to false from home.js. I tried this (among 20 or so other attempts), but it does not work.
home.js
activate() {
this.showMenu = false;
}
Is there a way through $parent or some other means to hide the menu in app.html using a view model?
EDIT
This works but it feels a little like a hack.
home.js
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
#inject(Router)
export class Home {
constructor(router) {
this.router = router;
}
attached(){
$("#navbarMenu").hide();
this.router.refreshNavigation();
}
}
You should be able to use router to achieve that. Since this is required for one page only, you can have something like this assuming your route name is home (or you could use other properties of RouteConfig that you have set in configureRouter):
<div class="container" if.bind="router.currentInstruction.config.name !== 'home'">
I approach this problem by using separate shells. By default Aurelia will start your app with app.js (or ts). But you can change that default and also use the same command to redirect to a new shell after authentication.
In your main.ts (or .js) you will have a line to start your aurelia app:
aurelia.start().then(() => aurelia.setRoot());
This line is telling aurelia to start and to set the root view model for your app, when aurelia.setRoot() has no value given it defaults to app.ts (or .js).
So I create a landing for my app where I can display with the page and styles I wish completely separately from the main app, including a limited router and navigation.
export function configure(aurelia: Aurelia) {
aurelia.use
.standardConfiguration()
if (environment.debug) {
aurelia.use.developmentLogging();
}
if (environment.testing) {
aurelia.use.plugin('aurelia-testing');
}
aurelia.start().then(() => aurelia.setRoot('authPage'));
}
authPage.ts is my usual app.ts with a router configuration but it will only have the authPage configured in it and perhaps one or two other welcome pages.
The authPage takes care of authentication and obtaining appropriate tokens. I use a 3rd party for authentication services so all I have on this page is a link. Either way after successful authentication is confirmed you now just want to redirect to an alternative aurelia shell.
#autoinject
export class AuthPage {
private app : Aurelia;
private router : Router;
constructor(router : Router, app: Aurelia) {
this.app = app;
this.router = router;
}
authenticate {
//some kind of authentication procedure...
if(authenticationSuccess) {
this.router.navigate('/', { replace: true, trigger: false});
this.router.reset();
this.router.("authenticatedApp");
}
}
The lines this.router.navigate('/', { replace: true, trigger: false}); and this.router.reset(); are provided to deal with issues mentioned here and also on SO here. The shell switch line this.router.("authenticatedApp"); doesn't work for me without the other two.
My authenticatedApp configures a full router and navigation menu for the user in just the same way as you would normally do with app.ts but now separated into its own shell.
Of course there is nothing to prevent someone linking straight to authenticatedApp but at this point there is no data displayed without an api call which all require an access token to be presented.
This is a useful link on building an Aurelia app with multiple shells for authentication.
The end result is a separated landing pages and application pages which can have different styles and different navigation.On logout you can do the same thing in reverse to reload the auth page.
I want to create an application in which I want following functionality:
Some links needs to be opened in router-view which is working fine.
But some links i want to open in current window(means full page). But if I click on page having routerview, then it starts opening url in that routerview only.
How can we stop this?
I believe you're looking for a way to set the root component. Like this:
import { Aurelia } from 'aurelia-framework';
export class MyCustomElement {
static inject = [Aurelia];
constructor(aurelia) {
this.aurelia = aurelia;
}
//call this method by using click.delegate in a button
goToAnotherPlace() {
this.aurelia.setRoot('./your-full-page-component');
}
}
By doing this, you're changing the whole page (everything inside aurelia-app attribute). You might have to configure another router.
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.
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.