How I can make a function Global in Aurelia - aurelia

Here is a scenario. I want to access Aurelia function, outside the Aurelia Application. Like while running my application if I call a method "GetNotification(string Message)" through browser Console, then it should get called.
The reason is that my Aurelia Application will run in a .Net Application browser. So I want to communicate between my native application(.Net) and Aurelia application. As in .Net browser control, we can call any Javascript function. But I am unable to call Aurelia Function, as it is not exposed externally.

I wouldn't recommend exposing your methods directly to global namespace. What you could do would be to register a custom event handler from within your viewmodel class and then trigger it from the .net site like ...
// ViewModel within aurelia
export class MyViewModel {
attached(){
document.body.addEventListener('custom-event', event => {
this.myViewModelMethod(event.detail); // just keep in mind the scope
}, false);
}
myViewModelMethod(data) {
console.log('data', data);
}
}
// .NET (outside the aurelia app)
// keep in mind CustomEvent is supported by most browsers but for IE it's only IE11
// see: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
document.body.dispatchEvent(new CustomEvent('custom-event', {
detail: {
myData: {
prop1:'prop1'
}
}
}));

Related

How to use Vue hooks inside a custom plugin?

I am writing custom plugin and need to create CSS custom properties from inside of it. In SPA mode everything is fine, but SSR mode is where the troubles coming. What I need is just to put my method inside of a mounted() hook. Is it possible?
export default {
install(app, options) {
createCSSVariables()
function createCSSVariables(options) {
const root = document.documentElement // this can't be working on server side :(
root.style.setProperty('--font-family', options.font_family)
root.style.setProperty('---accent', options.colors.accent)
}
}
}
I am using nuxt and could just use 'client' flag in plugin, but since it is for UI library, this will not help much - in this scenario all the elements would flicker right after mount

Nuxt class-based services architecture (register globally; vs manual import)

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()
}
}

nuxt anonymous middleware, how to set data on component

Does anyone know how to set data on a component in nuxt from within an anonymous middleware? For example:
data() {
return {
title: null
};
},
middleware(context) {
context.???.title="fred";
}
I'm running this within a Nuxt Universal App and it needs to be done server side rather than client side. Is this even possible?
Thanks,
David
I solved this problem using the asyncData function rather than Nuxt middleware:
asyncData(context) {
// Do stuff with the context
return {
title: "My Title created from context"
}
}
asyncData runs before loading the page component and will be called server side on the first request. This is what I was looking for, as I needed access to the context object while needing to set component variables before loading the component in the client.

Access data model in VueJS with Cypress (application actions)

I recently came across this blog post: Stop using Page Objects and Start using App Actions. It describes an approach where the application exposes its model so that Cypress can access it in order to setup certain states for testing.
Example code from the link:
// app.jsx code
var model = new app.TodoModel('react-todos');
if (window.Cypress) {
window.model = model
}
I'd like to try this approach in my VueJS application but I'm struggling with how to expose "the model".
I'm aware that it's possible to expose the Vuex store as described here: Exposing vuex store to Cypress but I'd need access to the component's data().
So, how could I expose e.g. HelloWorld.data.message for being accessible from Cypress?
Demo application on codesandbox.io
Would it be possible via Options/Data API?
Vue is pretty good at providing it's internals for plugins, etc. Just console.log() to discover where the data sits at runtime.
For example, to read internal Vue data,
either from the app level (main.js)
const Vue = new Vue({...
if (window.Cypress) {
window.Vue = Vue;
}
then in the test
cy.window().then(win => {
const message = win.Vue.$children[0].$children[0].message;
}
or from the component level
mounted() {
if (window.Cypress) {
window.HelloWorld = this;
}
}
then in the test
cy.window().then(win => {
const message = win.HelloWorld.message;
}
But actions in the referenced article implies setting data, and in Vue that means you should use Vue.set() to maintain observability.
Since Vue is exposed on this.$root,
cy.window().then(win => {
const component = win.HelloWorld;
const Vue = component.$root;
Vue.$set(component, 'message', newValue);
}
P.S. The need to use Vue.set() may go away in v3, since they are implementing observability via proxies - you may just be able to assign the value.
Experimental App Action for Vue HelloWorld component.
You could expose a setter within the Vue component in the mounted hook
mounted() {
this.$root.setHelloWorldMessage = this.setMessage;
},
methods: {
setMessage: function (newValue) {
this.message = newValue;
}
}
But now we are looking at a situation where the Cypress test is looking like another component of the app that needs access to state of the HelloWorld.
In this case the Vuex approach you referenced seems the cleaner way to handle things.

QT Quick UI Forms: how expose custom signal from QML component and handle in javascript

The QT Quick UI Forms example describes how to separate the declarative UI from the imperative javascript.
However, it does not describe how to make a custom signal that is exposed (declared?) at the declaritive level and handled in the javascript file.
The form is to be loaded in a shell application that needs to call fire custom signals we are calling: init and shutdown.
It seems I should be able to do this::
// MyComponent.ui.qml
Item {
signal init()
}
// MyComponent.qml javascript file
MyComponent {
onInit : {
// do some initialization
}
}
// Usage in shell
MyComponent {
id: mycomp
}
// somewhere
button.clicked: mycomp.init()
The order of the references was incorrect.
Needs to be:
Loader => MyComponent.qml (javascript) => MyComponentForm.ui.qml
But was:
Loader => MyComponentForm.ui.qml
// MyComponent.qml (javascript) was not referenced
Our loader was configured to load the declarative qml directly. Fixed by loading the javascript qml file. (which in turn referred to the MyComponentForm)
This way, our javascript init function was exposed. The module with imperative javascript extends the declarative qml with javascript functions.
// MyComponentForm.ui.qml
Item {
// property aliases as required by imperative code
// declarative ui stuff
}
// MyComponent.qml javascript file
MyComponentForm {
onInit : {
// do some initialization
}
}
Loader {
anchors.fill: parent
id: pageLoader
}
Button {
onClicked {
source = "qrc://MyComponent.qml";
pageLoader.item.init();
}
}