Dynamic localization in vue-i18n - internationalization

I would like to update my localization messages in vue-i18n dynamically.
I am building a webshop, where every item has descriptions in more languages. So what I’d like to achieve is when I get the webshop items from the REST API I want to put their names, descriptions etc. to the messages object in vue-i18n so it can work with them. Does the vue-i18n API have something to handle that? Also I am getting the data from the server (I get a Promise), so how can I make sure it gets updated in the browser view, when I finally get the response, and add the data to the localization?

What I did was write a mixin, and use it everywhere I need dynamic localization:
export default {
methods: {
$t: function (translate) {
if (typeof translate === 'string') {
return this.$i18n.t(translate)
} else if (translate === void 0) {
return this.$i18n.t('loading')
}
return translate[this.$i18n.locale]
}
}
}
This way when my texts look like the following, I can call $t(text) (NOT $t('text') of course):
data: function () {
return {text: {en:'Book', de:'Buch', hu:'Könyv'}}
}
So in your components you have to import this and add it as a mixin:
import dynamicLocalization from '#/components/mixins/dynamic-localization'
export default {
...
mixins:[dynamicLocalization]
...
}

Related

How to build a Model Layer in Vue3 just like other MVC language?

my name is DP, I have 2 years Vue2 experience, but I am new to Vue3. I am learning Vue3 recently, as I found the "setup(Composition API)" just like the "Controller(in MVC)" that I did in other language, so I am trying to build my test Vue3 project in MVC way, but I go some problem can anyone help? thx!
MVC Plan
M - use class
V - use <template> ... </template>
C - use setup
My Problem
working: using loadTopic_inSetup().then() in setup is working, because topicList_inSetup is defined in setup() too.
not working: using loadTopic_inModel() in setup is not working, I guess some kind data keep problem, because in console I can see the data already got from API
as u can see, I am not expert for js/ts, I am a backend developer, so if you know how to do it, plz help thx very much.
BTW, VUE is greet, I love it.
My Code
//APIBased.ts
import { ajax } from "#/lib/eeAxios"
export class APIBased {
//load data with given url and params
loadData(apiPath: string, params?: object): Promise<any> {
apiPath = '/v1/'+apiPath
return ajax.get(apiPath, params)
}
}
//Topic.ts
import { APIBased } from "./APIBased";
import { ref } from 'vue'
export class Topic extends APIBased {
//try keep data in model
topicList: any = ref([]);
constructor() {
super()
}
//direct return ajax.get, let setup do the then+catch
loadTopic_inSetup() {
return super.loadData('topics', { t_type_id: 1 })
}
//run ajax get set return data to this.topicList, keep data in model
loadTopic_inModel() {
super.loadData('topics', { t_type_id: 1 }).then((re) => {
console.log(re.data)
this.topicList = re.data
})
}
}
//EETest.vue
<template>
<EELayoutMainLayout>
<template v-slot:mainContent>
<h1>{{ "Hello Vue3 !!" }}</h1>
<hr/>
{{to.topicList}} //not working... just empty array
<hr/>
{{topicList_inSetup}} //working... topic list return from API show here.
</template>
</EELayoutMainLayout>
</template>
<script lang="ts">
import { defineComponent, getCurrentInstance, ref } from 'vue'
import EELayoutMainLayout from '#/components/eeLayout/EELayoutMainLayout.vue'
import { Topic } from "#/models/Topic";
export default defineComponent({
name: 'EETest',
props: {
},
setup() {
let topicList_inSetup = ref([])
const to = new Topic()
//try keep data in setup, it's working
to.loadTopic_inSetup().then((re) => {
topicList_inSetup.value = re.data
console.log(re.data)
})
//try keep data in model, the function is run, api return get, but data not show, even add ref in model
to.loadTopic_inModel()
return {
topicList,
to,
}
},
components: {
EELayoutMainLayout,
},
})
</script>
A few digressions before solving the problem. Maybe you are a java developer. I personally think it is inappropriate to write the front end with Java ideas. The design of vue3's setup is more inclined to combined functional programming
To fully understand why you need some pre knowledge, Proxy and the get and set method of Object
They correspond to the two core apis in vue, reactive and ref,
The former can only be applied to objects( because proxy can only proxy objects),The latter can be applied to any type(primary for basic javascript types, get and set can apply for any type)
You can modify the code to meet your expectations
loadTopic_inModel() {
super.loadData('topics', { t_type_id: 1 }).then((re) => {
console.log(re.data)
this.topicList.value = re.data
})
}
You cannot modify a ref object directly, a test case to explain what is reactive
when ref function is called, a will be like be wrapped in a class has value properties, and has get and set method
the effect function will call the arrow function, and in this time, the get method of a will be called and it will track as a dependence of the effect function, when a changed, the set method of a will be called, and it will trigger the arrow function,
so when you direct modify the a, the setter method will never trigger, the view will not update
const a = ref(1)
let dummy
let calls = 0
effect(() => {
calls++
dummy = a.value
})
expect(calls).toBe(1)
expect(dummy).toBe(1)
a.value = 2
expect(calls).toBe(2)
expect(dummy).toBe(2)
// same value should not trigger
a.value = 2
expect(calls).toBe(2)

Repeating Dataset from API response in Wix Code

How can I repeat the display of response from API in Wix?
I have the following code in my backend module that queries the API for data like so:
export function getTopCoins() {
const url = 'https://api.coinmarketcap.com/v1/ticker/?limit=10';
return fetch(url, {method: 'get'})
.then(response => response.json());
}
and then in the frontend, I want to render it in the browser like so:
export function page1_viewportEnter(event, $w) {
getTopCoins().then(response => {
response.forEach(($w, itemData, index) => {
$w('#coinList').text = itemData.name;
});
});
}
So basically I have a Paragraph element with the ID of name coinList. So as you can see I want to display a list of names of the top 10 coins from coin market cap. How can I achieve this?
A working version of this was one that displayed the name of the first item on the list, this is the code:
export function page1_viewportEnter(event, $w) {
getTopCoins().then(response => {
$w('#coinList').text = "Name: " + response[0].name + "\n";
});
}
You can use the (pretty new) feature of Wix called Repeaters. Its in a Beta phase at the moment. You may find it under Add Panel --> Lists & Grids...
Then, in Wix Code IDE, you may write something like that (assuming you connected all the proper data binding with the connect panel configuration between you dataset and the repeater component:
export function page1_viewportEnter(event, $w) {
getTopCoins().then(response => {
$w('#repeater1').data = response;
});
}
You may refer to the Repeater API here.
One more thing, if I'm allowed to suggest: if you don't have any backend logic when calling getTopCoins from your backend code, you may use Wix Fetch API in you frontend code directly and save a network hop (faster and better performance)
Enjoy!

How can i override placeOrder() action in Magento 2

I'm newbie in Magento. My shop should work with a web service. I have to check availability of products from web service before magento creates a new order. And after creating order successful i have to send the orderId back to web service. All this actions should be execute when a customer confirm a button "place order".
In a picture you see an "Place Order". I not sure how Magento does create a new order. I assume that an action placeOrder() will be call. My aim is to put a method checkAvailability() before this action and and method sendOrderId() after this action. checkAvailability() and SendOrderId() are the methods from webservice.
Has somebody an idea, how and where can i do that?
Sorry about bad english. Thank you
If you need to overwrite a function instead a class method (I used to overwrite Magento_Checkout/js/action/place-order).
requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/action/place-order': {
'My_Module/js/action/place-order': true
}
}
}
};
place-order.js
define(['mage/utils/wrapper'], function (wrapper) {
'use strict';
return function (placeOrderAction) {
return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, redirectOnSuccess) {
// my own code here
return originalAction(paymentData, redirectOnSuccess);
});
};
});
For your requirement, you need to used this event.
Used this event observer to check checkAvailability()
checkout_onepage_controller_success_action
Used this event observer to used SendOrderId()
sales_order_place_after
I had a similar case. I needed to override placeOrder action that was announced in third part module (Amasty_Checkout).
So, my solution was to create mixin in my theme.
1) Announce the mixin in theme with myTheme/Amasty_Checkout/requirejs-config.js:
var config = {
config: {
mixins: {
'Amasty_Checkout/js/view/onepage': {
'Amasty_Checkout/js/view/onepage-extend': true
}
}
}
};
2) Add mixin myTheme/Amasty_Checkout/web/js/view/onepage-extend.js with code:
define(
[
'jquery',
'uiComponent',
'ko',
'uiRegistry',
'Magento_Checkout/js/model/quote',
'Amasty_Checkout/js/action/set-shipping-information',
'Amasty_Checkout/js/model/agreement-validator',
'Amasty_Checkout/js/model/agreement-validator-old',
'Magento_Checkout/js/model/payment/additional-validators',
'Amasty_Checkout/js/model/amalert',
'mage/translate'
],
function (
$,
Component,
ko,
registry,
quote,
setShippingInformationAction,
checkoutValidator,
checkoutValidatorOld,
additionalValidators,
alert,
$t
) {
'use strict';
var mixin = {
placeOrder: function () {
// Here you put your extended code
}
};
return function (target) { // target == Result that Magento_Ui/.../default returns.
return target.extend(mixin); // new result that all other modules receive
};
});
Note that in my case I copied all content in define[...] section from original module script ('Amasty_Checkout/js/view/onepage') that I needed to override.
Here is the resource that helped me with my solution https://github.com/magento/magento2/issues/1864#issuecomment-141112927
I hope this will help someone save time.

Aurelia 2 custom elements (already sharing a view via #) doing almost the same thing, how to refactor?

Here is my problem:
Aurelia app:
a few custom elements (already sharing a view via #UseView) doing almost the same thing (specific func shoud be defined by every element itself), how to manage shared code (inkl #bindable)?
How to refactor this:
https://gist.run/?id=897298ab1dad92fadca77f64653cf32c
The "shared" code you refer to in your question is lifecycle-related stuff in your custom elements, which isn't really suited for sharing. You would need to do inheritance, and with custom elements that's setting yourself up for a lot of headaches.
Rather than sharing code, why not focus on the things which are variable and try to make them configurable? By looking at your gist, that seems by far the most straightforward solution here.
Say you have a custom element that calls a function when a property changes. This function needs to be different for some instances of the element. You could accomplish that with a bindable function, and use the .call behavior, like so:
some-element.js
import { bindable } from 'aurelia-framework';
export class SomeElement {
#bindable value;
#bindable processValue;
valueChanged(newValue, oldValue) {
if (this.processValue) {
this.processValue({ val: newValue });
}
}
}
consumer.html
<some-element value.bind="myValue" process-value.call="myFunc(val)"></some-element>
<some-element value.bind="anotherValue" process-value.call="anotherFunc(val)"></some-element>
consumer.js
myFunc(val) {
console.log("val: " + val);
}
anotherFunc(val) {
console.log("val: " + val);
}

Is there a way to add a Jasmine matcher to the whole environment

There are plenty of documents that show how to add a matcher to a Jasmine spec (here, for example).
Has anyone found a way to add matchers to the whole environment; I'm wanting to create a set of useful matchers to be called by any and all tests, without copypasta all over my specs.
Currently working to reverse engineer the source, but would prefer a tried and true method, if one exists.
Sure, you just call beforeEach() without any spec scoping at all, and add matchers there.
This would globally add a toBeOfType matcher.
beforeEach(function() {
var matchers = {
toBeOfType: function(typeString) {
return typeof this.actual == typeString;
}
};
this.addMatchers(matchers);
});
describe('Thing', function() {
// matchers available here.
});
I've made a file named spec_helper.js full of things like custom matchers that I just need to load onto the page before I run the rest of the spec suite.
Here's one for jasmine 2.0+:
beforeEach(function(){
jasmine.addMatchers({
toEqualData: function() {
return {
compare: function(actual, expected) {
return { pass: angular.equals(actual, expected) };
}
};
}
});
});
Note that this uses angular's angular.equals.
Edit: I didn't know it was an internal implementation that may be subjected to change. Use at your own risk.
jasmine.Expectation.addCoreMatchers(matchers)
Based on previous answers, I created the following setup for angular-cli. I also need an external module in my matcher (in this case moment.js)
Note In this example I added an equalityTester, but it should work with a customer matcher
Create a file src/spec_helper.ts with the following contents:
// Import module
import { Moment } from 'moment';
export function initSpecHelper() {
beforeEach(() => {
// Add your matcher
jasmine.addCustomEqualityTester((a: Moment, b: Moment) => {
if (typeof a.isSame === 'function') {
return a.isSame(b);
}
});
});
}
Then, in src/test.ts import the initSpecHelper() function add execute it. I placed it before Angular's TestBed init, wich seems to work just fine.
import { initSpecHelper } from './spec_helper';
//...
// Prevent Karma from running prematurely.
__karma__.loaded = function () {};
// Init our own spec helper
initSpecHelper();
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
//...

Resources