Where to Vue.use custom component on Quasar Framework? - facebook-oauth

I tried to use https://github.com/phanan/vue-facebook-signin-button component on Quasar Framework, I've installed it using npm:
npm i vue-facebook-signin-button --save
I've put on main.js:
import FBSignInButton from 'vue-facebook-signin-button'
Vue.use( FBSignInButton ) // use facebook signin button
Then I've add on main .vue file:
<template>
<q-layout>
<div class="layout-view">
<fb-signin-button
:params="fbSignInParams"
#success="onFbSignInSuccess"
#error="onFbSignInError">
Sign in with Facebook
</fb-signin-button>
</div>
</q-layout>
</template>
<script>
// Facebook
var FB;
window.fbAsyncInit = function() {
FB.init( {
appId: process.env.FB_APPID,
cookie: true, // enable cookies to allow the server to access the session
xfbml: true, // parse social plugins on this page
version: 'v2.8' // use graph api version 2.8
} )
}
(function( d, s, id ) {
var js, fjs = d.getElementsByTagName( s )[ 0 ];
if( d.getElementById( id ) ) return;
js = d.createElement( s );
js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore( js, fjs );
}( document, 'script', 'facebook-jssdk' ))
export default {
store,
data () {
return {
fbSignInParams: {
scope: '',
return_scopes: true
}
}
},
computed: {},
methods: {
onFbSignInSuccess ( response ) {
console.log( 'res', response )
FB.api( '/me', dude => {
console.log( 'dude', dude )
console.log( `Good to see you, ${dude.name}.` )
} )
},
onFbSignInError ( error ) {
console.log( 'OH NOES', error )
}
}
}
</script>
But it gives an runtime error:
(unknown) [Vue warn]: Unknown custom element: <fb-signin-button> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
---> <Index> at /home/asd/SPA/src/components/Index.vue
<App> at /home/asd/SPA/src/App.vue
<Root>
And another error on fbAsyncInit:
sdk.js:96 Uncaught TypeError: Cannot read property 'init' of undefined
at window.fbAsyncInit (eval at <anonymous> (0.6374f77….js:243), <anonymous>:10:5)
at v.__wrapper (sdk.js:96)
at sdk.js:140

FBSignInButton is a Vue component, so what you need to do is to import it and add it to your component like this:
import FBSignInButton from 'vue-facebook-signin-button'
export default {
...,
components: {
...,
FBSignInButton
},
...
}
You need to declare it as a component, otherwise Vue will not know about it.

This error occurs whenever there is an unidentified html tag which is not registered a vue component.
One solution to this problem is to create a vue wrapper of fb login button and implement the functionality.
Go through this link once.

Related

Using requireJS with svgPanZoom giving error: Uncaught TypeError: svgPanZoom is not a function

I have been using svg-pan-zoom library successfully with my javascript app but I now need to refactor it to use requireJS.
My util.js is:
define([
'baja!',
'jquery',
'/file/WebWidgets/js/libraries/svg-pan-zoom.js'
], function (
baja,
$,
svgPanZoom) {
'use strict';
const updateInitializeDiv = () => {
const svgDocument = $('#svgObjectElementFromBinding')[0].contentDocument;
const svgDocumentElement = svgDocument.documentElement;
console.log(svgDocumentElement);
console.log(svgDocumentElement.tagName);//svg
let panZoomSVG = svgPanZoom(svgDocumentElement, {
zoomEnabled: true,
controlIconsEnabled: true
});
}
const util = {};
util.updateInitializeDiv = updateInitializeDiv;
return util;
});
I am getting "Uncaught TypeError: svgPanZoom is not a function".
Can anyone suggest what I am doing wrong?
I had to reference the svg-pan-zoom library in the RequireJS config to get this to work.

How to mock Forge Viewer in React Unit Tests

we're currently trying to unit / integration test our react application, which uses the forge viewer cdn script.
the to be tested code assumes that Autodesk is available on the window object, which is the case in the application (via script tags), but not in the context of testing. this leads to errors like these:
Test suite failed to run
ReferenceError: Autodesk is not defined
> 1 | export class ExtendedGuiViewer3D extends Autodesk.Viewing.GuiViewer3D {
according to the license comments, the forge viewer script only allows using it through the Autodesk servers, so I cant just download it, and require the file locally.
has anyone successfully tested components that use the forge viewer scripts?
Intro
Disclaimer, I've only recently been experimenting with "Reactifying" the Autodesk Forge Viewer!
I currently believe the 'correct' way to consume the forge viewer css / js is to pull code from the Autodesk hosted cdn. The types are still available on npm though.
For example, the endpoints for v7.52.0:
js https://developer.api.autodesk.com/modelderivative/v2/viewers/7.52.0/viewer3D.min.js
css https://developer.api.autodesk.com/modelderivative/v2/viewers/7.52.0/style.min.css
Steps
1. Add type information from npm
Firstly, if you are using typescript, you can still install the viewer types from npm with:
yarn add -D #types/forge-viewer (check/add specific version to match the version of the script from the cdn - you can verify in your package.json)
2. Sample ViewingContext.tsx
In your React code you may wish to create a React Context to manage the the script downloading for you. This example is based on next.js:
import React, { PropsWithChildren, useEffect, useState } from "react";
import Script from "next/script";
export const ViewingContext = React.createContext({
initialized: false,
});
export interface ViewingContextProps {
options: Autodesk.Viewing.InitializerOptions;
}
// Place a single global ViewingContextProvider component around the common root of all your Autodesk.Viewing (LMV) components.
// https://forge.autodesk.com/en/docs/viewer/v7/developers_guide/overview/
export const ViewingContextProvider = ({
options,
children,
}: PropsWithChildren<ViewingContextProps>): JSX.Element => {
const [scriptLoaded, setScriptLoaded] = useState(
typeof window !== "undefined" &&
window.Autodesk?.Viewing?.Initializer !== undefined
);
const [initialized, setInitialized] = useState(false);
useEffect(() => {
if (scriptLoaded && !initialized) {
Autodesk.Viewing.Initializer(options, () => setInitialized(true));
}
}, [options, initialized, scriptLoaded]);
return (
<ViewingContext.Provider value={{ initialized }}>
<link
rel="stylesheet"
href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.52.0/style.min.css"
type="text/css"
/>
<Script
async
src="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.52.0/viewer3D.min.js"
onLoad={(): void => setScriptLoaded(true)}
/>
{children}
</ViewingContext.Provider>
);
};
3. Sample Viewer.tsx Component
Only mount this component as a child of the ViewingContext. You can also modify/replace this component with the ExtendedGuiViewer3D you mentioned.
import React, { useContext, useEffect, useRef } from "react";
import { ViewingContext } from "./ViewingContext";
export interface ViewerProps {
config?: Autodesk.Viewing.Viewer3DConfig;
onLoaded?: (viewer: Autodesk.Viewing.GuiViewer3D) => void;
onError?: (code: number) => void;
}
// Thin wrapper around https://forge.autodesk.com/en/docs/viewer/v7/developers_guide/overview/
// Add your own imperative hook code after GuiViewer object is loaded with the onLoaded callback.
// Place inside a relative layout div.
export const Viewer = ({
config,
onLoaded,
onError,
}: ViewerProps): JSX.Element => {
const { initialized: viewingContextInitialized } = useContext(ViewingContext);
const viewerDivRef = useRef<HTMLDivElement>(null);
const viewer = useRef<Autodesk.Viewing.GuiViewer3D>();
// Viewer imperative loading code
useEffect(() => {
if (viewingContextInitialized && !viewer.current && viewerDivRef.current) {
viewer.current = new Autodesk.Viewing.GuiViewer3D(
viewerDivRef.current,
config
);
const startedCode = viewer.current.start();
if (startedCode > 0) {
onError && onError(startedCode);
return;
}
if (onLoaded) onLoaded(viewer.current);
}
}, [config, onLoaded, onError, viewingContextInitialized]);
// Viewer destructor
useEffect(() => {
return (): void => {
if (viewer.current) {
viewer.current.finish();
}
};
}, []);
return (
<div
style={{
position: "absolute",
width: "100%",
height: "100%",
overflow: "hidden",
}}
>
<div
style={{
margin: 0,
width: "100%",
height: "100%",
}}
ref={viewerDivRef}
/>
</div>
);
};
Hope this answers your question!
so after months of fighting, these are the two options I've come up with so far.
option 1: dirty mock everything
there's a few #ts-ignore, because I dont want to mock out the whole package. i'll only mock the parts my application uses.
you could to type assertion like global.THREE = {...} as unknown as typeof THREE too. whatever floats your boat.
// setupTests.ts
// NOP_VIEWER global is not part of the #types declaration, so we need to tell typescript that there will be a global
declare global {
namespace NodeJS {
interface Global {
NOP_VIEWER: ExtendedGuiViewer3DTypes;
}
}
}
global.Autodesk = {
// #ts-ignore
Viewing: {
GuiViewer3D: jest.fn(),
Extension: jest.fn(),
ToolInterface: jest.fn(),
},
};
// #ts-ignore
global.THREE = {
Color: jest.fn(),
Vector4: jest.fn(),
};
global.NOP_VIEWER = {
disableSelection: jest.fn(),
resize: jest.fn(),
// #ts-ignore
model: {
getExternalIdMapping: (successCallback: any, _: any) => {
successCallback({ 'test-guid': 1 });
},
},
clearThemingColors: jest.fn(),
setThemingColor: jest.fn(),
isLoadDone: () => true,
isolate: jest.fn(),
};
option 2: download and require
As Autodesk Developer Adam Nagy pointed out, you probably wont get sent to jail, if you download the script file and require it locally for your tests only. (note that this is just a "probably")
keep in mind that even if you require the file, you still have to mock NOP_VIEWER as this global is only available after initializing the viewer (which you dont want to do in your tests)
// setupTests.ts
// replace the mocks of `Autodesk` and `THREE` with this require.
require('./vendors/Autodesk/viewer3D.min');
in my tests i can then use the jest spies on NOP_VIEWER
expect(NOP_VIEWER.clearThemingColors).toHaveBeenCalled();

Is there a way to lint .html files in Angular using a custom EsLint plugin?

I would like to create a custom linting rule for HTML files inside and Angular2+ project that warns me when there is a clickable element without an ID set. By clickable element I mean and other custom Angular components which will have a click event.
The project is currently using EsLint and I am trying to create a custom plugin which would do the job.
Right now I tried to run this rule from a plugin that enforces to have a class called "btn" at least. But linting does trigger any warning, more than that, it seems like it does not lint .html files at all.
module.exports = {
meta: {
docs: {
description: '',
category: 'Possible Errors',
recommended: true,
},
fixable: null,
},
create: function (context) {
return {
JSXOpeningElement: (node) => {
const nodeType = node.name.name;
if (nodeType !== 'button') {
return;
}
const legalClassNameAttributes = node.attributes.filter((attr) => {
const isClassName = attr.type === 'JSXAttribute' && attr.name.name === 'className';
return isClassName && (attr.value.type !== 'Literal' ||
attr.value.value.includes('btn'));
});
if (!legalClassNameAttributes.length) {
context.report({
message:
'Use correct class',
node,
});
}
},
};
},
};

Accessing Vuex Store Before Page Load NuxtJS

Context: I am trying to get Google Maps place data via the place_id on the beforeEnter() route guard. Essentially, I want the data to load when someone enters the url exactly www.example.com/place/{place_id}. Currently, everything works directly when I use my autocomplete input and then enter the route but it does not work when I directly access the url from a fresh tab. I've been able to solve this using the beforeEnter() route guard in traditional Vue, but cannot solve for this using Nuxt. Please help!
Question: How can I access the Vuex Store before a page loads in Nuxt?
Error: Any solution I try (see below) I either end up with a blank page or the page will not load (I think it is stuck in a loop and cannot resolve the Promise).
Attempted Solutions:
Using Middleware like below:
middleware({ store, params }) {
return store.dispatch('myModule/fetchLocation', params.id)
}
Using asyncData like below:
data(){
return{
filteredLocation: {}
}
}
// snip
async asyncData({ store, params }) {
const { data } = await store.dispatch('myModule/fetchLocation', params.id)
return filteredLocation = data
}
I tried looking into fetch, but apparently you no longer have access to context
Example Code:
In one of my store modules:
/* global google */
import Vue from 'vue'
import * as VueGoogleMaps from '~/node_modules/vue2-google-maps/src/main'
Vue.use(VueGoogleMaps, {
load: {
key: process.env.VUE_APP_GMAP_KEY,
libraries: 'geometry,drawing,places'
}
})
export const state = () => ({
selectedLocation: {}
})
export const actions = {
fetchLocation({ commit }, params) {
return new Promise((resolve) => {
Vue.$gmapApiPromiseLazy().then(() => {
const request = {
placeId: params,
fields: [
'name',
'rating',
'formatted_phone_number',
'geometry',
'place_id',
'website',
'review',
'user_ratings_total',
'photo',
'vicinity',
'price_level'
]
}
const service = new google.maps.places.PlacesService(
document.createElement('div')
)
service.getDetails(request, function(place, status) {
if (status === 'OK') {
commit('SET_PLACE', place)
resolve()
}
})
})
})
}
}
export const mutations = {
SET_PLACE: (state, selection) => {
state.selectedInstructor = selection
}
}
EDIT: I already have it in a plugin named google-maps.js and in my nuxt.config.js file I have:
plugins: [
{ src: '~/plugins/google-maps.js' }
]
//
//
build: {
transpile: [/^vue2-google-maps.js($|\/)/],
extend(config, ctx) {}
}
Using Middleware is how we can access Vuex before page loads. try putting the configuration part in a custom Nuxt plugin.
Create a file in Plugins folder (you can name it global.js).
Put this
import Vue from 'vue'
import * as VueGoogleMaps from '~/node_modules/vue2-google-maps/src/main'
Vue.use(VueGoogleMaps, {
load: {
key: process.env.VUE_APP_GMAP_KEY,
libraries: 'geometry,drawing,places'
}
})
in global.js.
Then add the plugin in nuxt.config.js like this.
plugins: [
'~/plugins/global.js'
]
Also, make sure you're using underscore before 'page_id' name in your folder structure.

How do I load a WASM module in a Vue component without initializing the module every time?

I have created a Rust library of type cdylib using
cargo web build --target=wasm32-unknown-unknown
I use a modified version of the "rust-wasm-loader" NPM package to build and load the WASM file. rust-wasm-loader uses this as a way to use the Rust code:
const wasm = require('./main.rs')
wasm.initialize().then(module => {
// Use your module here
const doub = module.cwrap('doub', 'number', ['number'])
console.log(doub(21))
})
I do not want to initialize the module every time I want to use the code. How do I load the module and use it like a library?
Since the loading of WebAssembly is asynchronous and may actually take some time for large modules, you need to handle the state when the module is not loaded, and then let the rest of the application know when the WebAssembly module is loaded.
You do not say how you are handling state in your Vue application, but if you are e.g. using Vuex you can do something like this:
const doubPlugin = store => {
wasm.initialize().then(module => {
const doub = module.cwrap('doub', 'number', ['number'])
store.subscribe((mutation, state) => {
if (mutation.type === 'DOUB_REQUEST') {
store.commit('DOUB_RESULT', doub(mutation.payload))
}
})
store.commit('DOUB_READY')
})
}
const store = new Vuex.Store({
state,
mutations,
plugins: [doubPlugin]
})
I've done a similar thing in an Elm/WebAssembly application (relevant JavaScript), so if you want to see how this can be applied in practice you can check that out.
Making a wrapper JS module that performs initialization and re-exports the promise seems like the most straightforward approach.
// main.js
module.exports = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
Then anything can do
require("./main.js").then(api => {
console.log(api.doub(21));
});
and will always get the same module. Or alternatively you could invert the async part and do
// main.js
const api = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
exports.doub = async function (val) {
return (await api).doub(val);
};
Then users of your module could do
const api = require("./main.js");
api.doub(21).then(result => {
console.log(result);
});
I created a class to wrap the WebAssembly loading and created a cwrap for every function:
class mkLib {
ready = false
_mod = require("./main.rs").initialize().then(module => {
this._mod = module
this.doub = module.cwrap('doub', 'number', ['number'])
this.ready = true
})
}
export default mkLib
In the Vue component's data there is a variable for the new class and in watch I wait for a change in the ready property:
data () {
return {
mod: new mkLib,
ready: false
}
},
watch: {
'mod.ready': function () {
this.ready = true
// now this.mod.FUNC(PARAMS) can be used
console.log(this.mod.doub(20))
}
}