Spartacus : Access Occ url on the NgModule inside Spartacus.configuration.module.ts file - spartacus-storefront

I have experience on Jquery but Angular is very new to me and so is spartacus. I have been trying to configure our Spartacus to be used across multiple environments. The only solution that I could find was to use the meta tag on index.html and then grab the config based on url as mentioned by janwidmer and CarlEricLavoie. I did make a slight change from these 2 solutions though, and that was to use the environment.{env}.ts files instead of a new Backend service or keeping the config in a map in the same file.
The issue that I am facing is that the spartacus.cofniguration.module.ts file needs these values in #NgModule and I've tried everything that I could think of, but am unable to use the value of this.config.backend?.occ?.baseUrl inside the NgModule, where I need to give the value for base URL and Base site.
Below is what I am trying to do. i have written the logic that would expose the correct config object according to the OCC base url that I receive on the environment. Now over here, the console log inside the constructor prints a proper object with all the properties I need, but then the conf object right before the #NgModule, it prints undefined and the values inside the NgModule are undefined as well then.
I also tried to create a new class and import it here so I could maybe create an instance, but couldn't do that as well, as it would then need the variable/method to be static for me to be able to access it inside NgModule.
import { Injectable, NgModule } from '#angular/core';
import { FacetChangedEvent, FeaturesConfig, I18nConfig, OccConfig, provideConfig, SiteContextConfig } from "#spartacus/core";
import { environment } from '../../environments/environment';
import { defaultB2bCheckoutConfig, defaultB2bOccConfig } from "#spartacus/setup";
import { defaultCmsContentProviders, layoutConfig, mediaConfig } from "#spartacus/storefront";
import { translations } from 'src/assets/i18n-translations/translations';
import { translationChunksConfig } from 'src/assets/i18n-translations/translation-chunks-config';
import { CdcConfig, CdcRootModule, CDC_FEATURE } from '#spartacus/cdc/root';
import { EnvironmentConfigurationModule } from "../../environments/environment-configuration.module"
import { environment as envdev} from '../../environments/environment';
import { environment as envstag} from '../../environments/environment.stage';
import { environment as envprod} from '../../environments/environment.prod';
let conf : any;
console.log("before ng");
console.log(conf);
#NgModule({
declarations: [],
imports: [
],
providers: [provideConfig(layoutConfig), provideConfig(mediaConfig), ...defaultCmsContentProviders, provideConfig(<OccConfig><unknown>{
backend: {
occ: {
baseUrl: environment.baseUrl,
}
},
}), provideConfig(<SiteContextConfig>{
context: {
urlParameters: ['baseSite', 'language', 'currency'],
baseSite: [environment?.baseSite],
currency: ['USD', 'GBP',]
},
}),
provideConfig(<I18nConfig>{
i18n: {
backend:{
loadPath:'assets/i18n-translations/{{lng}}/{{ns}}.ts',
},
resources: translations,
chunks: translationChunksConfig
,fallbackLang: 'en'
},
}
), provideConfig(<FeaturesConfig>{
features: {
level: '4.2'
}
}),
provideConfig(defaultB2bOccConfig), provideConfig(defaultB2bCheckoutConfig),
]
})
export class SpartacusConfigurationModule {
urlValue : string | undefined;
env: any;
constructor(private config: OccConfig) {
this.urlValue = this.config.backend?.occ?.baseUrl;
console.log("baseurl : " + this.config.backend?.occ?.baseUrl);
if(this.urlValue?.includes('s1'))
{
this.env=envstag;
}
else if(this.urlValue?.includes('p1'))
{
this.env=envprod;
}
else{
this.env=envdev;
}
conf = this.env;
console.log("conf");
console.log(conf);
}
getConfig() {
return conf;
}
}
Apart from these solutions, we have also tried to use window.location and location.href to find the base url and work based on that. This works amazing on local, but as soon as you deploy it to the server, it says that the reference window not found/reference location not found. We tried to do this right before the NgModule, inside spartacus-configuration.module.ts
import { environment as envDev } from "../../environments/environment";
import { environment as envStage } from "../../environments/environment.stage";
import { environment as envProd } from "../../environments/environment.prod";
let loc=location.hostname;
let env;
if(loc.includes('s1'))
{
env=envStage;
}
else if(loc.includes('p1'))
{
env=envProd;
}
else{
env=envDev;
}
console.log("before ng===>>>",loc);

With environment imports, your standard build configuration will replace the environment.ts variables with the ones set by your server's environment (eg. process.env) before deployment. Therefore, you should only import from environment.ts in your code and let the server handle overriding the staging environment variables.
With location and window objects, they are not accessible on the server-side of the build because Angular Universal initially delivers a pre-rendered page readable by bots (usually html-only) for SEO purposes. The location and window objects do not exist in this instance. In Angular, window and location objects should be imported into your classes.
Use Window: https://stackoverflow.com/a/52620181/12566149
Use Location: https://stackoverflow.com/a/43093554/12566149

Related

Add script into HTML using vite and vue 3

I have one js file which needs to be put in the public directory and needs to add it in the final production build as a text/javascript.
I have checked the options in vite config but couldn't find anything useful. The files I add contain a global JSON object and can be accessed directly.
To achieve this, I tried this solution.
vite.config.ts
import { fileURLToPath, URL } from "url";
import path from 'path';
// import test from "./src/assets/test.js"
import test from "./public/test.js"
import { defineConfig , loadEnv} from "vite";
import vue from "#vitejs/plugin-vue";
import { loadingScript } from 'vite-plugin-loading-script'
export default defineConfig(({ command, mode }) => {
// Load env file based on `mode` in the current working directory.
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
const env = loadEnv(mode, process.cwd(), '')
return {
// vite config
define: {
__APP_ENV__: JSON.stringify(env.VITE_REDIRECT_URL),
__TEST__: test,
},
plugins: [vue()],
server: {
hmr: {
overlay: false,
},
},
resolve: {
alias: {
"#": fileURLToPath(new URL("./src", import.meta.url)),
},
},
build: {
// rollupOptions: {
// external: ['__APP_ENV__'],
// output: {
// globals: {
// __APP_ENV__: JSON.stringify(env.VITE_REDIRECT_URL),
// }
// }
// }
}
}
});
test.js
export default {
REDIRECT_URL: "https://example.com/",
API_URL: "https://example.com/",
};
with the above changes, I got the console.log('__TEST__', __TEST__) as expected JSON object but it doesn't work with the production build.
maybe you can try including the js file to the html in the public directory

vue compile shared computed functions into separate package

How can i share common vue/nuxt specific code between different packages?
I do not want to use a monorepo however I have shared code that I want to separate into its own package. The shared code(new package), is written using #nuxtjs/composition-api and is just shared computed and methods used over and over in different components/templates.
I do not want the package to be setup as a plugin. Instead something to directly import to utilize tree shaking(just like the composition-api).
I am familiar with rollupjs to create the importable modules.
//New package
//index.js
export { default as isTrue } from './src/isTrue'
...
//src/isTrue
import { computed } from '#nuxtjs/composition-api'
export default (p) => {
return computed(() => p === 'true') //Im not 100% is this will break reactivity?!?!
}
I havent had any issues compiling this into .ssr, .esm, .min formats via rollupjs
The issue I come across is when i import the new package into a working file.
import { isTrue } from 'new-package'
export default{
name: 'testComp',
setup(props){
return {
isActive: isTrue(props.active)
}
}
will yield:
[vue-composition-api] must call Vue.use(VueCompositionAPI) before using any function.
i understand the #nuxtjs/composition-api is a wrapper of the VueCompositionAPI.
i dont really want to install the new package as a plugin therefore I have omitted the install on the new package(install ex: https://github.com/wuruoyun/vue-component-lib-starter/blob/master/src/install.js)
Used the options API
//library.js
export default function(){ // access to this -> arrow function doesnt have this
return this.disabled == true // when applied using the options api this will be the vue context aka property disabled
}
library.js is compiled using rollupjs and can be imported
//component.vue
import { isDisabled } from 'library'
export default {
//Composition API:
setup(props){
return {
//stuff
}
},
//Options API:
computed:{
isDisabled,
}
}

Can't import .obj files into my Nuxt components

in my Nuxt project I have a background scene made with Three.js.
Now I want to load an .obj into this scene. So the model has to loaded via the component.
my index.vue component:
export default {
name: 'scene',
data () {
return {
}
},
mounted () {
if(!this.scene) this.scene = new Scene({
$canvas: this.$refs.canvas,
});
}
}
In my .js file(Inside the components, where the .vue is as well):
import * as THREE from "three";
import Common from "../Common";
import { OBJLoader2 } from '~/node_modules/three/examples/jsm/loaders/OBJLoader2.js';
const Model = require('#/assets/models/background_1.obj')
export default class Model_1{
constructor(){
this.init();
}
init(){
var loader = new OBJLoader2();
console.log(Model)
loader.load(Model, (root) => {
Common.scene.add(root);
});
}
}
The Nuxt config:
export default {
mode: 'universal',
build: {
vendor: ['hammerjs'],
extend (config, ctx) {
config.module.rules.push(
{
test: /\.(obj|gltf)$/i,
loader: 'file-loader',
}
);
}
}
}
Following error appears:
Cannot find module '#/assets/models/background_1.obj'
I though that the .vue component will look into the assets folder, gets the obj and just reflects the url. But it's looking for a module, which I don't really understand 🤷‍♂️
The .obj file is located in the assets/models folder.
Ok, although every doc said that if the file is in static/file you'll have to use the src static/file. Now that did not work for me, but just using file as source worked. It's working, but can someone please clarify this since I've been on this problem since 2 days and many migraines 😐
Transfer the file to static folder example static/images/background.png
Then omit the #/assets/
var textureURL = "images/background.png";
var texture = textureLoader.load(textureURL);

Nuxtjs using Vuex-module-decorator doesn't wordk

I want to use my vuex modules as classes to make my code more clean and readable. I used the section (Accessing modules with NuxtJS) at the bottom of this document: https://github.com/championswimmer/vuex-module-decorators/blob/master/README.md
I've searched for the solution for almost 3 days and tried out this link:
vuex not loading module decorated with vuex-module-decorators
but, it didn't work.
Also, I used getModule directly in the component like the solution in this issue page: https://github.com/championswimmer/vuex-module-decorators/issues/80
import CounterModule from '../store/modules/test_module';
import { getModule } from 'vuex-module-decorators';
let counterModule: CounterModule;
Then
created() {
counterModule = getModule(CounterModule, this.$store);
}
Then, accessing method elsewhere
computed: {
counter() {
return counterModule.getCount
}
}
it didn't work for me!
This is my Module in store folder in Nuxtjs project:
import { ICurrentUser } from '~/models/ICurrentUser'
import { Module, VuexModule, Mutation, MutationAction } from 'vuex-module-decorators'
#Module({ stateFactory: true, namespaced: true, name: 'CurrentUserStore' })
export default class CurrentUser extends VuexModule {
user: ICurrentUser = {
DisplayName: null,
UserId: null,
};
#Mutation
setUser(userInfo: ICurrentUser) {
this.user = userInfo;
}
get userInfo() {
return this.user;
}
}
In index.ts file in sore folder:
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import CurrentUser from './currentUser'
let currentUserStore: CurrentUser
const initializer = (store: Store<any>): void => {
debugger
currentUserStore = getModule(CurrentUser, store)
}
export const plugins = [initializer]
export {
currentUserStore,
}
I think the problem stems from this line:
currentUserStore = getModule(CurrentUser, store)
currentUserStore is created as object but properties and methods are not recognizable.
when I want to use getters or mutation I get error. For instance, "unknown mutation type" for using mutation
Probably several months late but I struggled with a similar issue, and eventually found the solution in https://github.com/championswimmer/vuex-module-decorators/issues/179
It talks about multiple requirements (which are summarised elsewhere)
The one that relates to this issue is that the file name of the module has to match the name you specify in the #Module definition.
In your case, if you rename your file from currentUser to CurrentUserStore' or change the name of the module toCurrentUser`, it should fix the issue.

How to use Toasted inside an export default {}

I'm trying to use the package Toasted but I'm having a hard time understading how to use it.
I have a package called TreatErrors.js and I call this package to handle all errors from my application based on HTTP code returned by API a restfull API.
TreatErrors.js
import toasted from 'vue-toasted';
export default {
treatDefaultError(err){
let statusCode = err.response.status;
let data = err.response.data;
for(let field in data.errors){
if (data.errors.hasOwnProperty(field)) {
data.errors[field].forEach(message => {
toasted.show(message);
})
}
}
if(statusCode === 401){
toastr.error('Your token has expired. Please logout and login again to retrieve a new token');
}
return null;
},
}
and I'm tryin to call Toasted from within this package but I'm getting vue_toasted__WEBPACK_IMPORTED_MODULE_2___default.a.show is not a function. Any idea how I can use this Toasted inside of my own defined package?
The vue-toasted plugin must be registered with Vue first:
import Toasted from 'vue-toasted';
Vue.use(Toasted); // <-- register plugin
Then, your module could use it via Vue.toasted.show(...):
// TreatErrors.js
export default {
treatDefaultError(err) {
Vue.toasted.show(err.message);
}
}
And your Vue components could also use it via this.$toasted.show(...):
// Foo.vue
export default {
methods: {
showError(err) {
this.$toasted.show(err.message);
}
}
}