MeteorJS Package : Accessing "events" and "helpers" of a Template inside a package (internally) - events

I created a local package and added to my project, and added templating.
package.js
`Package.onUse(function(api) {
api.versionsFrom('1.4.2.3');
api.use('templating', 'client');
api.addFiles('server/main.js', 'server');
api.addFiles('client/main.js', 'client');
api.addFiles('client/main.html', 'client');
api.mainModule('pkgName.js');
});`
I created a template in client/main.html.
<template name="myTemplate">
<button>Test</button>
</template>
Then in client/main.js, i set the event listener:
Template.myTemplate.events({
'click button': function (e,t){ //do this on click };
})
But when i run the app - i get console error
Uncaught TypeError: Cannot read property 'events' of undefined

OK - after a couple of hours of research it seems that the Package.onUse function must list dependencies in the order that they should be loaded.
I thought the load order was only relevant to the 'packages' file in the main application, but it seems that you need to load the dependencies in order as well.
So this fixed it---
Package.onUse(function(api) {
api.versionsFrom('1.4.2.3');
api.addFiles('client/main.html', 'client');
// I moved the html file above the javascript - so the DOM loads first and then the template exists for the event listener to listen to.
api.use('templating', 'client');
api.addFiles('server/main.js', 'server');
api.addFiles('client/main.js', 'client');
api.mainModule('pkgName.js');
});

Related

React 6.4 - dispatch method inside async action function with createBrowserRouter

I've got this on my routes:
{
path: "/user/login",
element: <Login />,
action: loginAction,
},
So, I'm reaching the login action correctly when I submit the form in the login page.
The issue is, I'm trying to store some data using reducers but inside the loginAction is giving me an error when try to use the
async function action({ request }) {
const formData = await request.formData();
const dispatch = useDispatch(); <-- ERROR here
action as been imported as loginAction
import { action as loginAction } from "./pages/Login";
This is the error I'm getting:
Line 38:20: React Hook "useDispatch" is called in function "action" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks
Of course, if I change the "action" to "Action" it doesnt giving me any error (after fixing the import) but it just doesnt work.
Any clue how to use useDispatch inside a action function from React 6.4?
Thanks!
Use reducers in action function.
React Hook "useDispatch" is called in function "action" that is neither a React function component nor a custom React Hook function
as the error says, you cannot call a hook like this, inside a simple function ! you should call useDispatch:
const dispatch = useDispatch();
inside your function component and make sure that it is called at the top level.

Getting window.checkout.quoteData or store code are undefined error when cart item count updated using ajax

I have created a custom page with free text ordering functionality and called custom add to cart API to add items to the cart.
Once the item is added I need to update the cart item count with updated quantity. I tried to use
require([
'jquery',
'Magento_Checkout/js/action/get-totals'
], function ($, getTotalsAction) {
'use strict';
var deferred = $.Deferred();
getTotalsAction([], deferred);
});
But It is throwing error:
Uncaught TypeError: Cannot read property 'quoteData' of undefined at quote.js:34
And
url-builder.js:12 Uncaught TypeError: Cannot read property 'storeCode' of undefined at url-builder.js:12
Anything missing here?
I referred https://magento.stackexchange.com/questions/210517/error-javascript-define-magento2-window-checkout-quotedata-or-store-code-are-u which doesn't have any working solutions.
The issue is that quoteData lives in window.checkoutConfig - this data will only be set on the checkout pages, you won't have many of required js the modules loaded on a custom page that set this data correctly.
this may be a useful read: https://www.yireo.com/blog/2017-08-20-do-not-depend-on-window-checkoutconfig
I was able to achieve this for my scenario using the below code. It might help someone
require([
'Magento_Customer/js/customer-data'
], function (customerData) {
var sections = ['cart'];
customerData.invalidate(sections);
customerData.reload(sections, true);
});

VueJS Performance Questions

I'm working on a browser extension that uses Vue Cli with Vue Bootstrap. I've already optimized my Vue Bootstrap imports to only load the components and icons I use in the project. I also have lazy loaded route components, but I still see a long time to get to the created hook of my first component. Here's a code extract:
Main entry point
console.info("Loaded in " + (new Date().getTime() - global.start) + "ms")
require("#/App.js")
App.js
import Vue from "vue"
import * as Sentry from "#sentry/vue"
import { Integrations } from "#sentry/tracing"
import App from "#/App.vue"
import router from "#/common/router"
import store from "#/common/store"
import { get } from "#/common/api"
...
import {
ModalPlugin,
ButtonPlugin,
TabsPlugin,
DropdownPlugin,
AlertPlugin,
ToastPlugin,
FormInputPlugin,
FormRadioPlugin,
...
BIconArrowRightShort,
BIconArrowDownSquareFill,
} from "bootstrap-vue"
Vue.use(ModalPlugin)
Vue.use(ButtonPlugin)
Vue.use(TabsPlugin)
...
Vue.component("BIcon", BIcon)
Vue.component("BIconX", BIconX)
Vue.component("BIconArrowLeft", BIconArrowLeft)
Vue.component("BIconMailbox", BIconMailbox)
Vue.component("BIconFolderPlus", BIconFolderPlus)
Vue.component("BIconEnvelope", BIconEnvelope)
...
global.vm = new Vue({
router,
store,
render: h => h(App),
created() {
this.$router.push({ name: "Responses" })
...
})
}
And here's my component file that gets loaded first:
<template>
<div>
<div>
...
</div>
</div>
</template>
<script>
let now = new Date().getTime()
console.info("SFC file loaded in " + (now - global.start) + "ms")
import ... from "#/common/components/..."
export default {
...
mounted() {
let now = new Date().getTime()
...
</script>
<style lang="scss">
...
</style>
When I benchmark times, this is what I get:
SFC file loaded at 46ms (at the top of the script section)
Created Hook starts a 177ms
Mounted Hook starts at 308ms
I'm wondering what takes so long in the created hook (I don't do much, just checking the $route parameters). 150ms to just go through the created hook seems like a lot?
Here's the created hook:
console.info("Created Hook in " + (new Date().getTime() - global.start) + "ms")
if (this.$route.params.xx {
this.... = this.$store.state.xxxx.find(e => {
return e.uuid == .......
})
}
Performance loading the extension is important for the user experience, and it always feels a little sluggish when opening the extension popup.
Any idea on what could delay the loading like that?
Thanks!
The first thing that I notice is that you are doing a route.push on App created hook, that means that the router already solve the first route (probably '/') and after that you are adding another route (but not immediately) and then the router is solving that new route.
For a faster boot why don't you add a redirect to the route:
//...routes
{
path: '/',
redirect: {name: 'Responses'}
}
If you have the opportunity to change to Vue3 then maybe you could also perceive a performance boost since Vue2 has an always present GlobalAPI and Vue3 is doing a tree shaking and ignoring the unused stuff after building.
Note: Make sure you are testing it with a production environment, because if you are using the vue-cli to serve the content then the startup will include a lot of overhead
Thanks guys! Actually the default route is already redirecting to Responses, and removing the push doesn't change much.
Unfortunately I can't really migrate to Vue 3 as I rely on dependencies that do not fully support Vue 3 (Vue BS being an important one).
I'm guessing that's as much as I can do at this point. Just wondering if there's any way with Vue Cli to open the window browser extension popup immediately and load Vue afterwards (right now, it's waiting for the whole thing to be loaded and then opens the popup which gives a 300ms delay between the click and the window actually opening).
There is much more what happens between created and mounted hooks - it's not just the code in all created hooks what is running. Check the Vue component lifecycle
As you are using Vue SFC'c and probably Webpack, template compilation is out of the question but still, after created Vue is executing render functions of all components (producing VDOM) and creating real DOM elements based on VDOM (rendering whole app). So depending on number of components and elements, 150ms is not that bad...

Global dialog implementation with vue

I am need a reusable, global dialog/modal component in my vue application. I need to be able to call it from any component and update its header text, body text and callback function once the dismiss button is pressed on the modal. I have tried importing a custom made dialog component into each component where I plan to use it and I have tried creating a global dialog where the values would be set using a mutable values in a modals vuex modal. This latter did not work because vuex will not store functions as values. I am new to vue and am not too sure how to go about this and absolutely any advice on a good way to go about it would help tremendously.
I did something like that before. The main ingredient is the handling of events through the $root because you can't relay in this scenario on the normal component communication.
// In your Global Modal
<script>
export default {
name: 'GlobalModal',
mounted () {
this.$root.$on('callGlobalModal', () => {
this.dialog = true
})
},
data: () => ({
dialog: false,
}),
}
</script>
Then call it frome anywhere using this.$root.$emit('callGlobalModal')

Fails to call a plugin

im trying to develop a plugin that render a datagrid to html element. For example in:
<div id="datagrid">
<!-- Renderizado via Backbone.js -->
</div>
I have this plugin definition:
// csDatagrid.js
(function($) {
$.fn.csDatagrid = function(options) {
// Code ...
};
}, (jQuery));
And i call the function in this way:
// csAuditarSesiones.js
$("#datagrid").csDatagrid({
columns: cols,
url: $("#sfAction").val(),
filterBy: 'user_str'
});
Chrome says:
Uncaught TypeError: Object [object Object] has no method 'csDatagrid'
Load library queue (Chrome Developer Tools):
JQuery
csDatagrid (my plugin)
csAuditarSesiones (script with code for the current page, have the plugin call)
Thanks !
EDIT 1
Apparently the plugin not load, follow code always print "Not Loaded !":
if(jQuery().csDatagrid) {
console.log("Loaded !");
}else{
console.log("Not Loaded !");
}
The reason here is you are defining your plugin in the document ready event. Therefore the elements calling this plugin must call the plugin after the plugin has been loaded.
Try defining your new plugin outside of a load\ready event such as.
(function($) {
$.fn.csDatagrid = function(options) {
// Code ...
};
}, (jQuery));
$(document).ready(function(){
$("#datagrid").csDatagrid({
columns: cols,
url: $("#sfAction").val(),
filterBy: 'user_str'
});
});
Just for refrence (surely reading it now). http://learn.jquery.com/plugins/basic-plugin-creation/
EDIT:
Is it possible you have multiple jQuery versions being loaded? This can happen when there are conflicts. Also ensure that your plugin is loaded after the javascript file and before the document ready function.
There is one other (i have no idea why I believe it is when multiple versions are loaded) phenomenom that happens and you have to add the $ call back to your ready function. Such as.
$(document).ready(function($){
//TODO: Code here
});
There is error in your immediate invoked function's last line.
}, (jQuery)); should be :
})(jQuery);

Resources