In my angular5 application, i am trying to make everything localized and translated. I am following steps mentioned in this link: https://angular.io/guide/i18n
Question:
How to mark text with i18n tag for text in TS files?
For example:
The text in the name field of array needs to be translated:
pageList = [
{ id: 1, path: '/dashboard', name: 'Dashboard' },
{ id: 2, path: '/list', name: 'List' }
]
This array is used in the dropdown:
<button class="dropdown-item" *ngFor="let page of pageList" (click)="changePage(page.path)">{{ page.name }}</button>
How to mark text displayed in an expression to be translated?
For example:
<h1 lass="page-title">{{ pageTitle }}</h1>
Thanks!
Related
I'm new to vueJS. I am creating a news system to train myself. I'm a little problem. Here is the link that allows me to go to the detail of an article:
<router-link :to="{ name: 'Blog Details', params: { id: 1 }}"><img v-bind:src="postThumbnail" v-bind:alt="title"></router-link>
My component on which this link is located, has several props including the article id (actu_id).
In the link to the article, I would like the id located in the params not to be hard "1", but actu_id.
I do not know how to do.
When building things like this, first think how the data gets to your page. It's probably most efficient to use loops here, assuming you'll have multiple blog posts. Save them in the data in an array:
blogs: [
{
name: "Post 1",
id: 1,
thumbnail: "agsrgsghr.jpg",
},
{
name: "Post 2",
id: 2,
thumbnail: "agsrgsghr2.jpg",
},
],
And you can use template literals to set the router link parameters
<div v-for="blog in blogs" :key="blog.id">
<router-link :to="{ name: `${blog.name}`, params: { id: `${blog.id}` } }"
><img :src="blog.thumbnail" :alt="blog.name"
/></router-link>
</div>
Say I have a custom component that uses Vuetify's v-data-table within.
Within this component, there's multiple other custom components such as loaders and specific column-based components for displaying data in a certain way.
I found myself using the same code for filtering, retrieving data, loaders etc. across the project - so not very DRY.
The things that vary are:
API request url to retrieve data from (which I can pass to this generic component)
headers for v-data-table (which I pass to this generic component)
specific item slot templates!
(One file using this same code would need a column modification like the below, requiring different components sometimes too):
<template v-slot:[`item.FullName`]="{ item }">
<router-link class="black--text text-decoration-none" :to="'/users/' + item.Id">
<Avatar :string="item.FullName" />
</router-link>
</template>
Where another would have for example:
<template v-slot:[`item.serial`]="{ item }">
<copy-label :text="item.serial" />
</template>
There are many more unique "column templates" that I use obviously, this is just an example.
modifying items passed to v-data-table in a computed property (to add "actions" or run cleanups and/or modify content before displaying it - not related to actual HTML output, but value itself)
computed: {
items () {
if (!this.data || !this.data.Values) {
return []
}
return this.data.Values.map((item) => {
return {
device: this.$getItemName(item),
serial: item.SerialNumber,
hwVersion: this.$getItemHwVersion(item),
swVersion: this.$getItemSwVersion(item),
actions: [
{ to: '/devices/' + item.Id, text: this.$t('common.open') },
{ to: '/devices/' + item.Id + '/replace', text: this.$t('common.replace') }
],
...item
}
})
}
there are some unique methods that I can use on certain template slot item modifications, such as dateMoreThan24HoursAgo() below:
<template v-slot:[`item.LastLogin`]="{ item }">
<span v-if="dateMoreThan24HoursAgo(item.LastLogin)">{{ item.LastLogin | formatDate }}</span>
<span v-else>
{{ item.LastLogin | formatDateAgo }}
</span>
</template>
I can always make this global or provide them as a prop so this point should not be a big issue.
So my questions are:
What is the best way to use one component with v-data-table within but dynamically pass template slots and also allow item modification prior to passing the array to the v-data-table (as per point 3 and 4 above)
is there a better way to approach this since this seems too complex (should I just keep separate specific files)? It does not feel very DRY, that's why I'm not very fond of the current solution.
Basically I would be happy to have something like:
data: () => {
return {
apiPath: 'devices',
headers: [
{ text: 'Device', align: 'start', value: 'device', sortable: false, class: 'text-none' },
{ text: 'Serial Number', sortable: false, value: 'serial', class: 'text-none' },
{ text: 'Status', value: 'Status', class: 'text-none' },
{ text: 'Calibration', value: 'NextCalibrationDate', class: 'text-none' },
{ text: '', sortable: false, align: 'right', value: 'actions' }
],
itemsModify: (items) => {
return items.map((item) => {
return {
device: this.$getItemName(item),
serial: item.SerialNumber,
actions: [
{ to: '/devices/' + item.Id, text: this.$t('common.open') },
{ to: '/devices/' + item.Id + '/replace', text: this.$t('common.replace') }
],
...item
}
})
},
columnTemplatesPath: '/path/to/vue/file/with/templates'
}
}
And then I'd just call my dynamic component like so:
<GenericTable
:api-path="apiPath"
:headers="headers"
:items-modify="itemsModify"
:column-templates-path="columnTemplatesPath"
/>
Relevant but not exactly a solution to my question:
Is it possible to use dynamic scoped slots to override column values inside <v-data-table>?
Dynamically building a table using vuetifyJS data table
I am trying to create a dropdown (v-select/q-select (using quasar)), which allows me to select from an array in my vuex-storage and then eventually save the selected item (content of it) in a variable. Currently I have no problem to access the vuex-storage, but face the problem, that the v-select expects a string and not an object.
My code looks like the following.
// vuex storage:
const state = {
savedsystems:
[
id: "1",
system: {...}
],
[
id: "2",
system: {...}
]
// example of the vuex storage out of my viewdevtools
systemsconstant: Object
savedsystems:Array[2]
0:Object
id:"first"
system:Object
7a73d702-fc28-4d15-a54c-2bb950f7a51c:Object
name:"3"
status:"defined"
88519419-8a81-48f1-a5e6-5da77291b848:Object
name:"5"
status:"not defined"
1:Object
id:"second"
system:Object
7a73d702-fc28-4d15-a54c-2bb950f7a51c:Object
name:"3"
status:"not defined"
88519419-8a81-48f1-a5e6-5da77291b848:Object
name:"9"
status:"defined"
}
// dropdown:
<q-select
outlined
dense
emit-value
:value="currentsystem"
:options="savedsystems"
label="selectsystem" />
// computed to get systems from vuex:
computed: {
savedsystems() {
return this.$store.getters['systemsconstant/getsavedsystems']
}
},
I used the following example https://codepen.io/sagalbot/pen/aJQJyp as inspiration and tried a couple of different setups stringifying resulting in nothing really.
If one would try to apply my case to a similar problem (v-select displays object Object), the mentioned formatlabel would be an object instead of a string.
Question:
How can I modify the (with a getter) imported array of objects "savedsystems", so it can be used both as label to select it and furthermore then to connect it properly to the values, so I can save the selected as a variable.
Or can I change something in my v-select, e.g. varying what comes behind :options/options?
I'd appreciate any help!
You should use the property option-label
<div id="q-app">
<div class="q-pa-md" style="max-width: 300px">
<div class="q-gutter-md">
<q-badge color="secondary" multi-line>
Model: "{{ model }}"
</q-badge>
<q-select filled v-model="model" :options="options" label="Standard" option-label="description"></q-select>
{{ model }}
</div>
</div>
</div>
JS:
new Vue({
el: '#q-app',
data () {
return {
model: null,
options: [
{
label: 'Google',
value: 'Google',
description: 'Search engine',
category: '1'
},
{
label: 'Facebook',
value: 'Facebook',
description: 'Social media',
category: '1'
},
{
label: 'Twitter',
value: 'Twitter',
description: 'Quick updates',
category: '2'
},
]
}
}
})
https://codepen.io/reijnemans/pen/bGpqJYx?editors=1010
I've created a custom radio button component in Vue. The component is set up to take these props: label (string), value (string), checked (boolean), and a vModelValue which serves as the v-model value that will be used on the component. I set the internal reference to the v-model value to be prop: vModelValue and event: 'change'.
I have a computed value called local_vModelValue which gets the vModelValue prop sent down in the v-model on the component and sets it to the v-model internally.
This works correctly as is, except for one problem. Accessibility isn't working correctly. When I use voice over controls, and I have two distinct radio groups made up of three buttons each, it will identify the selected button as 1 of 6 even though it should be 1 of 3. It sees all 6 buttons on the page and acts as if there is one group.
To fix this, I want to put a name attribute in my component in the underlying logic, and I set up a computed property to check if there is a vModelValue. If there is, it sets the name to that vModelValue (or it SHOULD do so). I don't want to have to send a name down as a prop at this point. I want it to just use the vModelValue as the name. (Later I will check if there is a name attribute prop on the component and then it will use that as the name but for now I'm just trying to get it work with the vModelValue as the name.)
The problem is it just won't set the name to that vModelValue coming in.
Here is the component:
CustomRadioButtons.vue
<template>
<div>
<label
tabindex="0"
>
<input
type="radio"
:checked="checked"
v-model="local_vModelValue"
:value="value"
:name="getNameValue"
#change="onChange"
>
<span>
{{ label }}
</span>
</label>
</div>
</template>
<script>
export default {
name: 'CustomRadioButtons',
model: {
prop: 'vModelValue',
event: 'change'
},
methods: {
onChange(event) {
this.$emit('change', event.target.value, this.label, this.name, this.vModelValue)
},
},
props: {
vModelValue: {
type: String,
default: ''
},
label: String,
value: String,
name: {
type: String,
default: ''
},
checked: {
type: Boolean,
default: false
}
},
computed: {
local_vModelValue: {
get(){
return this.vModelValue;
},
set(value) {
this.$emit('change', value)
}
},
getNameValue() {
return this.vModelValue.length > 0 ? this.vModelValue : this.name
}
},
watch: {
vModelValue:{
immediate: true,
handler(){
console.log(this.vModelValue, this.checked, this.name)
}
}
},
}
</script>
App.vue
<template>
<div id="app">
<h3>Custom Radio Buttons 1</h3>
<div v-for="(button, i) in buttons" :key="'buttons'+i">
<CustomRadioButtons :label="button.label" :value="button.value" :checked="true" v-model="cat"></CustomRadioButtons>
</div>
<h3>Custom Radio Buttons 2</h3>
<div v-for="(button, i) in otherButtons" :key="'otherbuttons'+i">
<CustomRadioButtons :label="button.label" :value="button.value" :checked="true" v-model="dog"></CustomRadioButtons>
</div>
</div>
</template>
<script>
import CustomRadioButtons from "#/components/CustomRadioButtons"
export default {
name: 'App',
components: {
CustomRadioButtons
},
data(){
return {
cat: 'cat',
dog: 'dog',
buttons: [{label: 'label 1', value: 'value 1', name: 'name1'}, {label: 'label 2', value: 'value 2', name: 'name1'}, {label: 'label 3', value: 'value 3', name: 'name1'}],
otherButtons: [{label: 'test 1', value: 'value 1', name: 'name2'}, {label: 'test 2', value: 'value 2', name: 'name2'}, {label: 'test 3', value: 'value 3', name: 'name2'}],
}
},
props: ['value'],
}
</script>
Using the computed value of getNameValue causes the whole thing to work very strangely and I never see the name get updated to the vModelValue.
I have an answer now to the name attribute issue. The name attribute WAS getting updated with the vModelValue. However, since I was trying to log this.name in the console to see that value change, I couldn't actually see that anything was being changed. That's because this.name refers to the 'name' prop I set up in my component. But I wasn't passing a name prop down, so the name prop continued to default to an empty string.
If I take the name prop definition out, the name shows up as undefined in the console. If I move 'name' to the data object and make it a data property, it still shows up as an empty string in the console. I need to figure out why that is the case. But at least the name attribute in the DOM updates as it should.
One issue that comes up is that the buttons don't work right if the two radio groups have identical values for any of the corresponding buttons. Since I'm setting the name to the vModelValue, and the vModelValue is the value of which ever button is currently selected, if the two separate radio groups have a matching value for their selected buttons, the name becomes identical and then the groups are seen as one group. This is a problem!
I have to create a dynamic form in vue2. I want to save the values of the dynamic fields in an named object so that I can pass them along on submit.
The following code is working fine except I get an error in the console when I change the input value the first time (value will be propagated correctly though):
[TypeError: Cannot read property '_withTask' of undefined]
Here is how I define the props:
props: {
fields: {
type: Object,
default: {startWord: 'abc'}
},
},
And this is how I populate the model from the input field:
v-model="fields[field.id]"
Here is the entire code:
<template>
<div>
<!-- Render dynamic form -->
<div v-for="(field, key) in actionStore.currentAction.manifest.input.fields">
<!-- Text -->
<template v-if="field.type == 'string'">
<label>
<span>{{key}} {{field.label}}</span>
<input type="text" v-bind:placeholder="field.placeholder"
v-model="fields[field.id]"/>
</label>
</template>
<!-- Footer -->
<footer class="buttons">
<button uxp-variant="cta" v-on:click="done">Done</button>
</footer>
</div>
</template>
<script>
const Vue = require("vue").default;
const {Bus, Notifications} = require('../../Bus.js');
module.exports = {
props: {
fields: {
type: Object,
default: {startWord: 'abc'}
},
},
computed: {
actionStore() {
return this.$store.state.action;
},
},
methods: {
done() {
console.log('fields', this.fields);
Bus.$emit(Notifications.ACTION_INPUT_DONE, {input: this.fields});
}
},
}
</script>
So again, everything is working just fine (showing initial value in input, propagating the new values to the model etc.). But I get this '_withTask' error when I first enter a new character (literally only on the first keystroke). After that initial error it doesn't pop up again.
-- Appendix --
This is what the manifest/fields look like:
manifest.input = {
fields: [
{ id: 'startWord', type: 'string', label: 'Start word', placeholder: 'Enter start word here...' },
{ id: 'startWordDummy', type: 'string', label: 'Start word dummy', placeholder: 'Enter start word here...' },
{ id: 'wordCount', type: 'integer', label: 'Word count' },
{ id: 'clean', type: 'checkbox', label: 'Clean up before' },
]
}
-- Update --
I just discovered that if I set the dynamic field values initially with static values I don't get the error for those fields set this way:
created() {
this.fields.startWord = 'abc1';
},
But this is not an option since it will be a dynamic list of fields. So what is the best way to handle scenarios like this?
From documentation: Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive.
As I understand it's bad idea create keys of your object by v-model.
What would I do in HTML:
<input type="text"
:placeholder="field.placeholder"
#input="inputHandler(event, field.id)" />
Then in JS:
methods: {
// ...
inputHandler({ target }, field) {
this.fields[field] = target.value;
}
},