ReferenceError: mapkit is not defined when referred from an internal page - mapkit

I am using mapkit, which is displayed on /map. If I load /map directly, mapkit loads the map as intended. However, if I load any other page on my site and then access /map, I get the following error: ReferenceError: mapkit is not defined.
I tried playing around with onMount, afterMount, and reactive statements, but nothing seems to work. For context, I have every page set to pretender.
Here is my map.svelte
<script>
import { onMount } from 'svelte';
onMount(() => {
mapkit.init({
authorizationCallback: function (done) {
fetch('/api/gettoken')
.then((response) => response.json())
.then(done);
}
});
let map = new mapkit.Map('map');
});
</script>
<svelte:head>
<script src="https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js"></script>
</svelte:head>
<div id="map" />

The load order is not guaranteed to be correct like this. You could use a dynamic import instead:
onMount(async () => {
await import('https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js');
// mapkit should be defined from here
});

Related

How to display object in a variable in Vue 3 script setup

I can't display object in Vue 3 script setup. I used ref, reactive and standard variables but all scenarios is unsuccessful.
I want to reflect the response from the getDetail request to the screen. getDetail is fetching this data asynchronously. I run into a problem in every scenario.
ref() usage
<script setup>
let movie = ref([])
const getMovieData = async ()=> {
try {
const data = await getDetail('movie', route.params.id)
movie.value.push(data)
}
catch (e){
console.log(e)
}
}
getMovieData()
</script>
<template>
<h1>{{movie[0].original_title}}</h1>
</template>
I am able to show data in this usage but I am getting these errors
reactive() usage
<script setup>
let movie = reactive({
data:null
})
const getMovieData = async ()=>{
try {
const data = await getDetail('movie', route.params.id)
movie.data = data
}catch (e){
console.log(e)
}
}
getMovieData()
</script>
<template>
<h1>{{movie.data.original_title}}</h1>
</template>
In this usage I can show data but I get similar errors
Standart variable usage
<script setup>
let movie
const getMovieData = async ()=>{
try {
const data = await getDetail('movie', route.params.id)
movie =data
}catch (e){
console.log(e)
}
}
getMovieData()
</script>
<template>
<h1>{{movie.id}}</h1>
</template>
In this usage, the data does not appear on the screen, because the movie object is formed asynchronously.
How do I manage to display this object on the screen in the most correct way without getting an error?
Write a v-if <h1 v-if="movie && movie.length">{{movie[0].id}</h1> So it only renders when data is available.
Template code runs immediately on component creation. In each case, before movie has been asynchronously assigned, your template code is trying to access some non-existent property. optional chaining is arguably the easiest way to prevent these types of errors:
movie[0]?.original_title
Another option is to provide a default value that matches your template usage so it doesn't error out:
let movie = ref([
{
original_title: ''
}
])

How do I use Vue.use with dynamic imports?

Currently I have
import { SchedulePlugin } from "#syncfusion/ej2-vue-schedule";
Vue.use(SchedulePlugin);
I would like to change this to a dynamic import.
I've changed the import to:
const { SchedulePlugin } = () => import("#syncfusion/ej2-vue-schedule");
but have been unable to find the syntax for the corresponding changes I need to make to Vue.use.
What is the correct syntax to use?
The dynamic import syntax you have is for specifying async components, where Vue internally resolves them. For Vue plugins, you have to resolve the module yourself before passing it on to Vue.use(). The import() method returns a Promise, so you can await the result of the module loading in the context of an async function:
const loadPlugins = async () => {
const { SchedulePlugin } = await import("#syncfusion/ej2-vue-schedule")
Vue.use(SchedulePlugin)
}
loadPlugins()
Note the plugins should be loaded before the app to ensure the plugin's effects are available to the app:
loadPlugins().then(() => {
new Vue({
render: (h) => h(App)
}).$mount("#app")
})
demo

Vue js, cannot read property $el

I have the problem with correctly understood the flow elements, method calling in vue js. It is the standard idea - fetching some data from rest api, and render it on the browser.
The getting method I wrote into mounted(). Also I added there calling renderHomePageMethod(). This method was written in methods:
mounted() {
axios.get("http://localhost:3000/api/test").then(response => {
this.testData= response.data
this.renderHomePageMethod();
});
}
In renderHomePageMethod() I used this.refs$ and $el. And probably there is the problem, everything is working fine, but in the browser I got warning about:
Uncaught (in promise) TypeError: Cannot read property '$el' of undefined
Probably I should calling
this.renderHomePageMethod()
in another place. But where?
It seems like your referenced component is not rendered before the main component renders, so it gives a reference error.
A hackish way would be something like this:
mounted() {
axios.get("http://localhost:3000/api/test").then(response => {
this.testData= response.data
setTimeout(() => {
this.renderHomePageMethod();
}, 1000); // or any other minimum delay before the subcomponent is rendered
});
}
or the better and harder way, create an event-bus.js file which contains:
import Vue from 'vue';
export const EventBus = new Vue();
in your main and sub components:
import { EventBus } from "./event-bus.js";
in your sub component, this will send the notification to the main component when it's ready to roll:
mounted(){
EventBus.$emit("subcomponent:is-mounted");
}
in your main component:
data(){
return {
testData: null
}
},
mounted(){
axios.get("http://localhost:3000/api/test").then(response => {
this.testData= response.data
});
EventBus.$on("subcomponent:is-mounted", () =>{
this.renderHomePageMethod();
});
},
beforeDestroy(){
EventBus.$off("subcomponent:is-mounted");
// don't forget to remove the listeners because of duplicate listeners may occur
// if your component refreshes (remounts)
}

How to unit test Vue.js components that use nuxt-i18n

If I try to run the thing below (with yarn run jest), I get TypeError: _vm.$t is not a function, because SearchField is using a translation ("$t('search')").
import { mount } from "#vue/test-utils";
import SearchField from "#/components/ui/SearchField";
describe("SearchField", () => {
const wrapper = mount(SearchField);
it("renders correctly", () => {
expect(wrapper.element).toMatchSnapshot();
});
});
If I add the following three lines at the beginning, I get TypeError: Cannot read property '_t' of undefined instead.
import Vue from "vue";
import VueI18n from "vue-i18n";
Vue.use(VueI18n);
nuxt-i18n is an external library, not your own code, so the test good practices ask us to just mock the translation library and its needed functions ($t in this case).
The following code should solve your problem:
describe("SearchField", () => {
const wrapper = mount(SearchField);
it("renders correctly", () => {
mocks: {
$t: (msg) => msg
}
expect(wrapper.element).toMatchSnapshot();
});
});
More information on this approach can be found here.

Shadow DOM and testing it via Jasmine

I have a webcomponent that creates a shadow DOM and adds some html to its shadowRoot.
class SomeThing extends HTMLElement {
attachedCallback () {
this.el = this.createShadowRoot();
this.render();
}
render () {
this.el.innerHTML = '<h1>Hello</h1>';
}
}
export default SomeThing;
And I am compiling it with the help of webpack and its babel-core and babel-preset-es2015 plugins.
Also I am using Karma and Jasmine to write my Unit Test. This is what it looks like.
describe('some-thing', function () {
var someElement;
beforeEach(function () {
someElement = document.createElement('some-thing');
});
it('created element should match string representation', function () {
var expectedEl = '<some-thing></some-thing>';
var wrapper = document.createElement('div');
wrapper.appendChild(someElement);
expect(wrapper.innerHTML).toBe(expectedEl);
});
it('created element should have shadow root', function () {
var wrapper = document.createElement('div');
wrapper.appendChild(someElement);
expect(wrapper.querySelector('some-thing').shadowRoot).not.toBeNull();
})
});
I want to see if there is something in the shadowRoot of my element, and want to write test cases for the HTML and events created inside the shadowRoot. But the second test is failing. It is not able to add shadowRoot to the some-element DOM.
If anyone can help me out, that would be helpful.
I am also uploading the full test working project on Github. You can access it via this link https://github.com/prateekjadhwani/unit-tests-for-shadow-dom-webcomponents
Thanks in advance
I had a similar problem testing a web component but in my case I am using lit-element from polymer/lit-element. Lit-element provides life cycle hooks, template rendering using lit-html library (documentation).
So this is my problem and how I solved. I noticed that the component was added and the class executed constructor and I had access to public methods using:
const element = document.querySelector('my-component-name')
element.METHOD();
element.VARIABLE
But it never reached the hook firstUpdated, so I thought the problem was the speed the test executes vs the speed component is created. So I used the promised provided by lit-element API (updateComplete):
Note: I use mocha/chai instead of Jasmine
class MyComponent extends LitElement {
render() {
return html`<h1>Hello</h1>`
}
}
customElements.define('my-component', TodoApp);
let element;
describe('main', () => {
beforeEach(() => {
element = document.createElement("my-component");
document.querySelector('body').appendChild(element);
});
describe('test', () => {
it('Checks that header tag was added to shadowRoot', (done) => {
(async () => {
const res = await element.updateComplete;
const header = element.shadowRoot.querySelector('h1');
assert.notEqual(header, null);
done();
})();
});
});
});
So, my advice is create a promise and resolve it when the render function is executed, use the promise to sync the creation of the component with tests.
I am using this repository to test concepts
https://github.com/correju/polymer-playground