next-i18next i18n on array of objects - json

I have an array of objects called "Options", that I use as a prop to a dropdown/select Material-UI component. I want to use the next-i18next library on the labels. I already implemented with success through all the next app just like the documentation explains. I tried using the {t('key')} and it doesn't allow.
import { useTranslation } from 'next-i18next'
const UsersPage = () => {
const { t } = useTranslation('user');
const Options = [
{ value: 'fullName', label: 'Nome' },
{ value: 'cpf', label: 'CPF' },
{ value: 'id', label: 'PadrĂ£o' },
]
...rest of the component
}
export const getStaticProps = async ({ locale }) => ({
props: {
...await serverSideTranslations(locale, ['user', 'home']),
},
})
export default UsersPage;

The msefer answer is right:
`${t("key")}`
inside JSON or string building in props like
const since = `${t('since')}`;
const until = `${t('until')}`;
...
<ListItemText
primary={value.name}
secondary={since + value.beginDate + until + value.endDate}
/>

Related

Why useReducer does not update state in react-typescript

I have a problem with Typescript with React and useReducer hook. The state does not wanted to be updated and it is 100% typescript problem (I mean I need a typescript solution because in javascript it works perfect). So I want to make reducer as short as possible so I use "name" in HTML and get this name as a name of object in initialState. When I return ( title:{}, description: {}) It works but when I use [action.field] it does not work. action.field is the name of HTML element.
const initialStateReducer: inputsFormState = {
title: {
val: "",
isValid: false,
},
description: {
val: "",
isValid: false,
},
};
const RecipeForm = () => {
const inputReducer = (
state: inputsFormState,
action: inputsFormAction
): inputsFormState => {
console.log(action.type, action.content, action.field);
let isValid: boolean;
const { content } = action;
isValid = content.length > 0;
return {
[action.field]: {
val: content,
isValid: isValid,
},
...state,
};
};
const [formIsValid, setFormIsValid] = useState<boolean>(false);
const [inputsValues, dispatchReducer] = useReducer(
inputReducer,
initialStateReducer
);
const changeTextHandler = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
dispatchReducer({
type: ActionKind.changeVal,
field: e.target.name,
content: e.target.value,
});
};
return (
<React.Fragment>
<Input
name="title"
onChange={(e) => changeTextHandler(e)}
placeholder="Name for your recipe"
/>
<Textarea
name="description"
onChange={(e) => changeTextHandler(e)}
placeholder="Description"
cols={20}
rows={20}
resize="none"
/>
</React.Fragment>
);
};
Typescript is only a superset of JS that adds on Typing while writing code, it has no effect on the actually running of the JS (as it gets compiled into JS before running).
From looking at the above code I think the issue is your return in the reducer:
return {
[action.field]: {
val: content,
isValid: isValid,
},
...state,
};
Should be:
return {
...state,
[action.field]: {
val: content,
isValid: isValid,
}
};
As (and this may not be a great explanation) but right most arguments overwrite the preceding values, so you're effectively overwriting the new with the original.

Vue v-model on input as a dynamic ref

I created this <input> Vue component that handles a v-model property.
Adding the two-way data binding wasn't a problem, but I now want to add the current input value to the "state" so that I can clear it by just modifying a ref.
clearValue is called as expected but updating inputValue doesn't update the UI. What should I be doing differently ?
<template>
<div>
<input
:id="id"
type="text"
:value="inputValue"
#input="updateValue"
/>
<UiIcon
type="x"
#click.native="clearValue"
/>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from '#nuxtjs/composition-api';
import UiIcon from '~/components/ui/UiIcon.vue';
export default defineComponent({
name: 'UiInput',
components: { UiIcon },
props: {
id: {
type: String,
required: true,
},
value: {
type: String,
default: undefined,
},
},
setup(props, { emit }) {
const inputValue = ref(props.value);
const clearValue = () => {
inputValue.value = '';
};
const updateValue = (event: Event) => {
emit('input', (event.target as HTMLInputElement).value);
};
return {
updateValue,
clearValue,
inputValue,
};
},
});
Usage example
data() {
return { text: '' };
},
template: `
<div>
<UiInput :id="id" v-model="text" />
</div>
`,
I think you just need to emit 'input' event with empty value inside clearValue function
emit('input', '');

Antd customRender return raw html

I need to generate html code in a customRender function of one column.
I cannot use scopedSlots as suggested here, since the html code is part of e generic component, and other components pass their columns array as a parameter.
BaseComponent.vue:
<template>
<a-table
:columns="attrs.columns"
:rowKey="record => record[attrs.recordId]"
:dataSource="filteredTableData"
>
</a-table>
</template>
<script>
export default {
props: {
attrs: {
type: Object,
required: true
}
:
</script>
ContactComponent.vue:
<template>
:
<base-component :attrs="attrs"/>
:
</template>
<script>
import BaseComponent from './BaseComponent';
export default {
components: {
BaseComponent
},
data() {
return {
attrs: {
columns: [
title: 'Type',
dataIndex: 'type',
customRender: (val, record) => {
return '<div class="myClass">' + val + </div>';
},
],
recordId: 'contactId'
}
}
}
:
</script>
The problem:
The following code:
customRender: (val, record) => {
return '<div class="myClass">' + val + '</div>';
},
renders this:
Is there a way to force raw html rendering directly from the customRender function?
You can transform your code:
customRender: (val, record) => {
return '<div class="myClass">' + val + '</div>';
},
In this way (if you have JSX support).
customRender: (data) => {
return <div class="myClass"> {data.text} </div>;
},
If you dont have JSX support, you can return a Vnode. Like specified here: https://vuejs.org/guide/extras/render-function.html#creating-vnodes (I didn't try this way).
Or you can try to add support for JSX: https://vuejs.org/guide/extras/render-function.html#jsx-tsx
(My reply is late but may help others.)
==========
Edit:
Here, another exemple, to show you where this piece of code should be (only work with JSX support):
data: function () {
return {
dataSource: [],
columns: [
{
title: 'Website',
dataIndex: "cat_website",
key: "cat_website",
customRender: (data) => {
return <a href={'http://' + data.text} target='_blank'>{data.text}</a>;
},
},
// other columns...
],
// ...
}
}

How to access objects inside an array of JSON object?

I'm trying to access object inside an array of a JSONObject and print its values.
I'm able to print the array as JSONObject using console.log. But i fail to access the values inside the array which are again JSONObject format. Following is my my JSONObject
{
"id": 4,
"meta": {
"type": "pagetype",
"title": "Home"
}
},
"title": "Expose data to frontend",
"subtitle": "We will be exposing the content to the frontend",
"content": [
{
"type": "full_richtext",
"value": "<p><b>Bold body</b></p>"
},
{
"type": "button",
"value": {
"button_text": "Google",
"button_url": "https://google.com"
}
}
]
}
I need to access the values inside the array "content" and print values for
"value" -- Bold body --
"button_text"
"button_url"
I have tried it as follows
class App extends React.Component {
constructor() {
super();
this.state = {
'items': []
}
}
componentDidMount() {
fetch('http://localhost:8000/api/v2/pages/4/')
.then(results => results.json())
.then(results => this.setState({ 'items': results }));
}
render() {
var contents_from_wagtail = this.state.items;
var streamfield_content_array = contents_from_wagtail.content;
console.log(streamfield_content_array); //prints array of objects
return (
<React.Fragment>
<p>{this.state.items.subtitle}</p>
<p>{this.state.items.title}</p>
/* print the values for
"value" -- Bold body --
"button_text"
"button_url"
*/
</React.Fragment>
);
}
}
export default App;
When showing an array of items the .map method can be used to create multiple elements:
class App extends React.Component {
constructor() {
super();
this.state = {
'items': {}
}
}
componentDidMount() {
fetch('http://localhost:8000/api/v2/pages/4/')
.then(results => results.json())
.then(results => this.setState({ 'items': results }));
}
render() {
var contents_from_wagtail = this.state.items;
var streamfield_content_array = contents_from_wagtail.content || [];
console.log(streamfield_content_array); //prints array of objects
return (
<React.Fragment>
<p>{this.state.items.subtitle}</p>
<p>{this.state.items.title}</p>
{streamfield_content_array.map((item, index) => {
return <div key={index}>type: {item.type} <p>{item.value}</p></div>
})}
</React.Fragment>
);
}
}
export default App;
More .map examples: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
To access it within the render you have to access it conditional because it is not set for the first render until the fetch call is Executed
That is why you have to provide some fallback until the array is loaded.just check if the item is undefined and return null for example.
Only if the array is filled render the desires output and it should be fine.
Hope this helps. Happy coding.
You could use a combination of .map() and .filter() to iterate over the items within the content array. It looks like you only want to display items that have a type of button. So try something like this:
class App extends React.Component {
constructor() {
super();
this.state = {
'items': []
}
}
componentDidMount() {
fetch('http://localhost:8000/api/v2/pages/4/')
.then(results => results.json())
.then(results => this.setState({ 'items': results }));
}
render() {
var contents_from_wagtail = this.state.items;
var streamfield_content_array = contents_from_wagtail.content;
var buttonContent = this.state.items ? this.state.items.content.filter((item) => item.type == "button") : null
return (
<React.Fragment>
<p>{this.state.items.subtitle}</p>
<p>{this.state.items.title}</p>
{ buttonContent && buttonContent.map(item => (
<div>
<p>{item.button_text}</p>
<p>{item.button_url}</p>
</div>
))}
</React.Fragment>
);
}
}
export default App;

Spread a destructuring to avoid a duplication

Is there a way to avoid the duplication of competenceList[competenceKey] (object) in lines :5 and :6, keeping the same object on return without using variable declaration?
const func = ({
entities: { competence: competenceList },
timesheet: { management: { competences: competenceKey } },
}) => ({
employeeKey: competenceList[competenceKey].employee,
payrollEnd: competenceList[competenceKey].competenceEnd,
});
Yes, it is technically possible by using a computed property name:
const func = ({
timesheet: { management: { competences } },
entities: { competence: { [competences]: competence },
}) => ({
employeeKey: competence.employee,
payrollEnd: competence.competenceEnd,
});
but I wouldn't recommend this as it's not really readable. Just write
function func({timesheet, entities}) {
const competence = entities.competence[timesheet.management.compentences];
return {
employeeKey: competence.employee,
payrollEnd: competence.competenceEnd,
};
}