How to use $axios Nuxt module inside of setup() from composition API? - javascript

The docs say to use this.$axios.$get() inside of methods/mounted/etc, but that throws TypeError: _this is undefined when called inside of setup(). Is $axios compatible with the composition API?
To clarify, I'm specifically talking about the axios nuxt plugin, not just using axios generically. https://axios.nuxtjs.org/
So for instance, something like this throws the error above
export default {
setup: () => {
const data = this.$axios.$get("/my-url");
}
}

import { useContext } from '#nuxtjs/composition-api';
setup() {
const { $axios } = useContext();
}

Alright, so with the usual configuration of a Nuxt plugin aka
plugins/vue-composition.js
import Vue from 'vue'
import VueCompositionApi from '#vue/composition-api'
Vue.use(VueCompositionApi)
nuxt.config.js
plugins: ['~/plugins/vue-composition']
You can then proceed with a test page and run this kind of code to have a successful axios get
<script>
import axios from 'axios'
import { onMounted } from '#vue/composition-api'
export default {
name: 'App',
setup() {
onMounted(async () => {
const res = await axios.get('https://jsonplaceholder.typicode.com/posts/1')
console.log(res)
})
},
}
</script>
I'm not sure about how to import axios globally in this case but since it's composition API, you do not use the options API keys (mounted etc...).
Thanks to this post for the insight on how to use Vue3: https://stackoverflow.com/a/65015450/8816585

Related

Vue3 - Setting up Pinia gives me an error of no active Pinia

I have this Vue app I would like to convert the store to a Pinia store because the normal reactive store just doesn't work and does not keep the values I set.
So I installed pinia and I have the following main.js file:
import { createApp, h } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
import { store } from "./store";
import axios from "axios";
axios.defaults.baseURL = "http://127.0.0.1:8000";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
axios.defaults.xsrfCookieName = "csrftoken";
const pinia = createPinia();
const app = createApp(App).use(router, axios, store, pinia).mount("#app");
I have created a store inside a stores folder and here is that store:
import { defineStore } from "pinia";
export const useSomeAppStore = defineStore("someapp", () => {
const userID = ref(0);
const loggedIn = ref(false);
});
On the log in page I would like to use this store so I can save the user id in the store.
<script setup>
document.title = "Login | addapost";
import axios from "axios";
import { ref, watch, onBeforeMount, onMounted } from "vue";
import { useSomeAppStore } from "#/stores/someapp";
import { useRouter, useRoute } from "vue-router";
const store = useAddapostStore();
</script>
If I refresh the log in page I see the following error in the console:
Uncaught (in promise) Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
Why is this error happening and what am I doing wrong here please?
The problem is with axios I don't have to use(axios) in the main.js file. Wonder why some people add it to the main.js file and it works but for me it doesn't.
But now I have another problem. I got pinia working and it says the store is installed and I see the user id when I am logged in. As soon as I refresh the data is lost and the user id becomes 0. Why is the store not keeping my data?
You can’t pass multiple plugins to a single app.use() method.
Instead, you have to chain them - one plugin each:
app.use(…).use(…)

Why I could connect API via axios?

I created react app with Typescript and I made a request via axios.
Here is my original code.
// /src/axios.js
import axios from 'axios';
const instance = axios.create({
baseURL:"https://api.baseurl.org",
});
export default instance;
// /src/component.js
import React from "react";
import axios from "./axios";
...
const Component = ({ fetchUrl }) => {
async function fetchData() {
const request = await axios.get(fetchUrl);
}
...
};
...
I could get responses correctly, but don't know why I could make a request.
In axios.js file, I export instance, not axios.
In component.js file, I import axios.
I think I should import instance in component.js, that is, modify the file like this :
// /src/component.js modified
import React from "react";
import instance from "./axios";
...
const Component = ({ fetchUrl }) => {
async function fetchData() {
const request = await instance.get(fetchUrl);
}
...
};
...
I could get the same result correctly.
Two ways of using axios instance made correct results.
Why I could connect API with the original code?
In axios.js file, I export instance, not axios. In component.js file, I import axios.
You're using a default export, not a named export. The name you assign to it is completely up to the module doing the importing.
Consider:
const foo = 123;
export default foo;
export const bar = 456;
To import that you say:
import whatever_you_want_to_call_it, { bar as anything_you_like } from './export.mjs';
bar is a named export so you have to specify that it is bar you want to import (but giving it a different name is optional). The default export has to be given a name, but which name is entirely up to you.

Vue composition API use VueAxios?

I am in main.js importing vue-axios
main.js
import { createApp } from 'vue';
import axios from 'axios';
import VueAxios from 'vue-axios';
import App from './App.vue';
const app = createApp(App);
app.use(VueAxios, axios);
app.mount('#app');
and App.vue
export default {
name: 'App',
setup() {
axios.get('xxxx').then((res) => {console.log(res)}); // is not working
}
};
but it seems Vue composition API setup can't get axios? So it must be used in App.vue import axios from 'axios'?
import axios from 'axios';
export default {
name: 'App',
setup() {
axios.get('xxxx').then((res) => {console.log(res)});
}
};
My environment is vite
generally I'd do something like this ( except I modularize all the api calling into a "service" rather than inlining it into the view code.)
import axios from 'axios';
import {onMounted} from 'vue'
export default {
name: 'App',
setup() {
onMounted(async () => {
let res = await axios.get('xxxx')
console.log(res)
});
}
};
This wouldn't work in any version of Vue without an import:
axios.get(...)
Even in Vue 2, you have to use one of these if you use vue-axios:
this.axios.get(...)
this.$http.get(...)
Vue.axios.get(...)
The composition API has no this access in the component, so vue-axios doesn't help much.

How can I import firebase app as browser module import?

I am trying to import firebase app (8.0.2) on the client side without bundler.
I downladed the firebase-app.js from the CDN and host it on my server.
The goal is to have the firebase app module imported in one of my modules like this:
import firebase from '/vendor/firebase-app.js';
However, when trying to access firebase, I am getting an error about the default export not being defined. In firefox, this is:
Uncaught SyntaxError: import not found: default
Where am I wrong? Does the module import only work with a bundler?
I believe I have some fundamental misunderstanding, but I cant figure it out.
Thanks for the help!
As Doug comments, that syntax is intended for use with bundlers.
If you want to import the SDK inside a module script then you will need to use a CDN, like this:
useEffect(() => {
let isSubscribed = true
const initFirebase = async () => {
try {
const [{
initializeApp
},
{
getFirestore,
collection,
addDoc
}
] = await Promise
.all([
import('https://www.gstatic.com/firebasejs/9.6.10/firebase-app.js'),
import('https://www.gstatic.com/firebasejs/9.6.10/firebase-firestore.js')
])
const fireApp = initializeApp(firebaseConfig)
const appDB = getFirestore(fireApp)
if (isSubscribed) {
setFirebase(fireApp)
setDB(appDB)
setResult('success')
}
} catch (e) {
console.log(e);
setResult('error')
}
}
if (firebaseConfig) {
initFirebase()
.catch(e => {
console.log(e);
})
}
return () => is Subscribed = false
}, [firebaseConfig])
That's from a POC I'm working on, single HTML file, Preact + Firebase, no bundlers needed.
It is still a long way to be something and need validations, but it answers your question.
Here is the repo.
In Firebase "^9.9.4" use :
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {}
export const app = initializeApp(firebaseConfig);
export const db = getFirestore(app)

How to integrate azure ad into a react web app that consumes a REST API in azure too

I have one web app which is React, and I already configured Azure AD Authentication for the web app itself. Its 100% Client site app, no server side components.
I used this component:
https://github.com/salvoravida/react-adal
My code is as follows:
adalconfig.js
import { AuthenticationContext, adalFetch, withAdalLogin } from 'react-adal';
export const adalConfig = {
tenant: 'mytenantguid',
clientId: 'myappguid',
endpoints: {
api: '14d71d65-f596-4eae-be30-27f079bf8d4b',
},
cacheLocation: 'localStorage',
};
export const authContext = new AuthenticationContext(adalConfig);
export const adalApiFetch = (fetch, url, options) =>
adalFetch(authContext, adalConfig.endpoints.api, fetch, url, options);
export const withAdalLoginApi = withAdalLogin(authContext, adalConfig.endpoints.api);
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import DashApp from './dashApp';
import registerServiceWorker from './registerServiceWorker';
import 'antd/dist/antd.css';
import { runWithAdal } from 'react-adal';
import { authContext } from './adalConfig';
const DO_NOT_LOGIN = false;
runWithAdal(authContext, () => {
ReactDOM.render(<DashApp />, document.getElementById('root'));
// Hot Module Replacement API
if (module.hot) {
module.hot.accept('./dashApp.js', () => {
const NextApp = require('./dashApp').default;
ReactDOM.render(<NextApp />, document.getElementById('root'));
});
}
},DO_NOT_LOGIN);
registerServiceWorker();
dashapp.js
import React from "react";
import { Provider } from "react-redux";
import { store, history } from "./redux/store";
import PublicRoutes from "./router";
import { ThemeProvider } from "styled-components";
import { LocaleProvider } from "antd";
import { IntlProvider } from "react-intl";
import themes from "./settings/themes";
import AppLocale from "./languageProvider";
import config, {
getCurrentLanguage
} from "./containers/LanguageSwitcher/config";
import { themeConfig } from "./settings";
import DashAppHolder from "./dashAppStyle";
import Boot from "./redux/boot";
const currentAppLocale =
AppLocale[getCurrentLanguage(config.defaultLanguage || "english").locale];
const DashApp = () => (
<LocaleProvider locale={currentAppLocale.antd}>
<IntlProvider
locale={currentAppLocale.locale}
messages={currentAppLocale.messages}
>
<ThemeProvider theme={themes[themeConfig.theme]}>
<DashAppHolder>
<Provider store={store}>
<PublicRoutes history={history} />
</Provider>
</DashAppHolder>
</ThemeProvider>
</IntlProvider>
</LocaleProvider>
);
Boot()
.then(() => DashApp())
.catch(error => console.error(error));
export default DashApp;
export { AppLocale };
Until that point everything works fine, when the user is not authenticated its redirected to login.live.com for authentication and then its redirected back.
However I also created another azure webapp for hosting a REST API, that REST API is already configured in Azure AD, so that users that try to use the rest will need to be authenticated.
Now the question is: How do I setup my client side APP to consume REST API which is protected by Azure AD.?
I found this and looks what I am looking for, but I am not sure how to integrate this into my existing code above
https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/481
Update:
For potential readers
This answer plus the instructions on this url to configure App registrations helped me to solve the problem: https://blog.ithinksharepoint.com/2016/05/16/dev-diary-s01e06-azure-mvc-web-api-angular-and-adal-js-and-401s/
The key here is adalApiFetch, defined in adalConfig.js. As you can see, it's a simple wrapper around adalFetch. This method (defined in react-adal) receives an ADAL instance (authContext), a resource identifier (resourceGuiId), a method (fetch), a URL (url) and an object (options). The method does the following:
Use the ADAL instance (authContext) to obtain an access token for the resource identified by resourceGuiId.
Add this access token to the headers field of the options object (or create one if it wasn't provided).
Call the given "fetch" method passing in url and the options object as parameters.
The adalApiFetch method (which you have defined in adalConfig.js) simply calls adalFetch with the resource identified in adalConfig.endpoints.api.
Ok, so how do you use all of this to make a REST request, and consume the response in your React app? Let's use an example. In the following example, we will be using the Microsoft Graph API as the Azure AD-protected REST API. We will be identifying it by it's friendly identifier URI ("https://graph.microsoft.com"), but just keep in mind that that could just as well be the Guid app ID.
adalConfig.js defines the ADAL configuration, and exports a couple helper methods:
import { AuthenticationContext, adalFetch, withAdalLogin } from 'react-adal';
export const adalConfig = {
tenant: '{tenant-id-or-domain-name}',
clientId: '{app-id-of-native-client-app}',
endpoints: {
api: 'https://graph.microsoft.com' // <-- The Azure AD-protected API
},
cacheLocation: 'localStorage',
};
export const authContext = new AuthenticationContext(adalConfig);
export const adalApiFetch = (fetch, url, options) =>
adalFetch(authContext, adalConfig.endpoints.api, fetch, url, options);
export const withAdalLoginApi = withAdalLogin(authContext, adalConfig.endpoints.api);
index.js wraps indexApp.js with the runWithAdal method from react-adal, which ensures the user is signed with Azure AD before loading indexApp.js:
import { runWithAdal } from 'react-adal';
import { authContext } from './adalConfig';
const DO_NOT_LOGIN = false;
runWithAdal(authContext, () => {
// eslint-disable-next-line
require('./indexApp.js');
},DO_NOT_LOGIN);
indexApp.js simply loads and renders an instance of App, nothing fancy here:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
App.js is a simple component where the magic happens:
We define a state value. In this case, it's called apiResponse since we're just displaying the raw API response, but of course you could name this state whatever you wanted (or have multiple state values).
During componentDidMount (which is run after the element is available in the DOM), we make a call to the adalApiFetch. We pass in fetch (from the Fetch API as the fetch parameter, and the endpoint for the REST request we want to make (the /me endpoint in Microsoft Graph, in this case):
In the render method, we simply display this state value in a <pre> element.
import React, { Component } from 'react';
import { adalApiFetch } from './adalConfig';
class App extends Component {
state = {
apiResponse: ''
};
componentDidMount() {
// We're using Fetch as the method to be called, and the /me endpoint
// from Microsoft Graph as the REST API request to make.
adalApiFetch(fetch, 'https://graph.microsoft.com/v1.0/me', {})
.then((response) => {
// This is where you deal with your API response. In this case, we
// interpret the response as JSON, and then call `setState` with the
// pretty-printed JSON-stringified object.
response.json()
.then((responseJson) => {
this.setState({ apiResponse: JSON.stringify(responseJson, null, 2) })
});
})
.catch((error) => {
// Don't forget to handle errors!
console.error(error);
})
}
render() {
return (
<div>
<p>API response:</p>
<pre>{ this.state.apiResponse }</pre>
</div>
);
}
}
export default App;
I still had the issue with the config given above. I added on more config to the above and it worked. Hope it helps.
import { AuthenticationContext, adalFetch, withAdalLogin } from 'react-adal';
export const adalConfig = {
tenant: '{tenant-id-or-domain-name}',
clientId: '{app-id-of-native-client-app}',
endpoints: {
api: 'https://graph.microsoft.com'
},
cacheLocation: 'localStorage',
extraQueryParameter: 'prompt=admin_consent'
};
export const authContext = new AuthenticationContext(adalConfig);
Phillipe's response put me down the right path, but I was still running into an issue with my token not being accepted.
aadsTS700051: response_type 'token' is not enabled for the application.
To resolve I needed to go into my app's registration > manifest & set oauth2AllowImplicitFlow to true:
"oauth2AllowImplicitFlow": true,
Log out of your Azure account, sign back in & you should receive your user's details.

Categories