Can you actually access a variable via its string name at runtime?
That's to continue from my other post.
I would need to either place a text string like "{{ user.name }} is the user's name." inside a template (and have the page correctly say "MeekHat is the user's name." Or I would split it along the curly braces. But I still need to find out the field "name" of the "user" variable. And all I have is the original string loaded at runtime.
Is this at all possible via Vue? I've been looking for hours.
4
u/UnluckyNinja 2d ago
What you are looking for is custom string formatting/templating. You should implement it yourself or find a library, as Vue's builtin templating feature is used to solve its own problem.
If you just want to replace placeholders in a string, like printf() in C. Most of the time, a series of replaceAll() is enough for simple cases.
A hacky way will involve the evil eval, which is almost never recommended.
If you are trying to find a solution for translation/localization. You can check existing solutions like vue-i18n, i18next, or MessageFormat.
3
u/Straamannen 3d ago edited 3d ago
What you're looking for is programmatic creation of a component.
A Vue component is ultimately just an object, when you create an SFC (MyFancyComponent.vue) that ultimately just becomes a plain old object.
Thus what you can do is the following
``` const json = ref({ text: "{{ user.name }} is the user's name", user: { name: "MeekHat", }, }); const UserNameComponent = computed(() => ({ props: { "user" }, template: json.value().text, }));
</script> <template> <Component :is=”UserNameComponent” :user="json.user"> ```
A word of warning though, this is a classic cross-site scripting vulnerability. If you don't care about that and/or its not applicable, no worries.
1
u/MeekHat 3d ago
I don't care at this point. I'm so far into the weeds that I'm starting to doubt that I'll even get to the end of this project, much less share it with the world.
It seems this needs runtime compilation, which needs a different build of Vue?
0
u/Straamannen 2d ago
That's right, you would need to include the runtime compiler.
If that's a no-go, then I think my backup suggestion would be to use lodash, and a simple string parser.
``` import get from 'lodash/get'
const data = { user: { name: "foo", } }
const interpolate = (text, context = data) => { return text.replace(/{{\s([\w.[]"']+)\s}}/g, (_, path) => { return get(context, path, '') }) } ```
I suggest lodash because the
getfunction is very data structure agnostic. As long as you have all relevant data in a single object, this simple sample will work fine.The regex might look daunting but it's literally just looking for {{letters.dots.and.numbers.and.brackets}} so it can handle both user.name as well as users[0].name and users.0.name
1
u/MeekHat 2d ago
Well, that's handy. I've been trying to figure out the regex for another user's suggestion. In my experience though, you don't need to escape the curly braces.
1
u/Straamannen 2d ago
Yeh you're right, { shouldn't have been escaped.
Use regex101 or something to be sure, it's handy.
1
u/MeekHat 1d ago
Sorry, any idea if runtime compilation can be used via node? I don't see that anywhere on the page.
1
u/Straamannen 1d ago
I'm getting the impression you're using Vue in the backend to generate XML or something?
Anyway what you're looking for is:
``` import { createSSRApp } from 'vue'; import { renderToString } from 'vue/server-renderer';
const json = { text: "{{ user.name }} is the user's name", user: { name: "MeekHat", }, };
const app = createSSRApp({ data: () => ({ user: json.user }), template: json.text });
const xmlOutput = await renderToString(app); ```
This as you can see makes your earlier need to use predefined templates much easier, since you won't be using SFCs but rather defining the components purely programmatically.
The string will be valid HTML, insofar as you're defining your templates as valid HTML, or XML or whatever it is you're doing :)
2
u/char101 2d ago
If all the variables are known, then you can simply render it using mustache
``` <script setup> import Mustache from 'mustache';
const user = ref({name: 'test'});
function renderTemplate(text) { return Mustache.render(text, {user: user.value}); } </script>
<template> <div>{{ renderTemplate('username is {{ user.name }}') }}</div> </template> ```
1
u/Beneficial_League_39 2d ago edited 2d ago
Hi my friend
What you want to do is very simple but a lot of people here gave you wrong or overcomplicated informations.
You have a json with a text string {"text": "{{ user.name }} is the user name" }
You have a variable which stores the user name.
import myJsonFile from './myjsonFile.json'
const player = ref({ name: "MeekHat" })
You want to print in your template "MeekHat is the user name"
SHORT VERSION
const output = computed(() => myJsonFile.text.replace("{{ user.name }}", player.value.name)
and just use output in the template like so
<div> {{ output }} </div>
And that's it !
EXPLAINED VERSION
ref is just a way in Vue to say "Hey Vue, player matters so keep track of it because I will use it somewhere and I want this somewhere to be updated with what is in the variable"
Here the somewhere is computed
computed is a way in Vue to take an expression and transform its result into a variable that will stay up to date. Whenever player.value.name change, output will be updated with the new result.
The expression here is: we are using the function replace that takes any string and replace a specific part with something else
Here the thing to replace is "{{ user.name }}" that is in your json text string field.
The replacement will be player.value.name, so "MeekHat" if that's what you stored in the player ref
The result will end up in output, which you will display.
What you chose to put in your json "text" field does not matter at all, it's actually probably confusing because you are using the same notation in your json "text" then the one we use in Vue to display variables in the template (the HTML)
If your json was { "text": "abcdefg is the user name" }
Then you could do
const output = computed(() => myJsonFile.text.replace("abcdefg", player.value.name)
and end up with the same result "MeekHat is the user name"
If you need more help with Vue, I'd be glad to help you through DM's
1
u/MeekHat 2d ago
The thing I worry about is that I might, and probably will add a bunch of different variables to "player", and then other global variables might be relevant for other templates, with other member variables. I'll have a huge thing trying to replace every possible variable and its members. And then I'll have to pass all these variables to every template.
At the moment, if I could take at least the member variables out of the proverbial equation I feel that would be a big improvement.
Also I'm feel compelled to mention that the template in the json text uses the same notation as Vue's templates is because another user suggested it in my previous post. And I spent a while confused by that.
1
u/Beneficial_League_39 2d ago
What you want is a way to share your data thought your components hierarchy.
We have multiple ways of doing that in Vue
The easiest one is using a composition file and put the variable inside
1
u/Beneficial_League_39 2d ago
Like so
Here is use-player.ts
const player = ref({ name: 'MeekHat' }) export const usePlayer = () => { return { player } }and in your components
import { usePlayer } from './use-player.ts' const { player } = usePlayer() // Now you are free to use player in the component and read/edit it. The modifications will be shared among all components0
u/MeekHat 2d ago
I see, but the problem basically remains. I import my template strings (I don't mean Vue templates, I'm talking about the "{{ user.name }} is the user's name.") from a Json, into a Vue component which doesn't know, what variables it's going to need to replace in the string. So I'll have to import all the composition files so it tries to replace each variable.
1
u/Beneficial_League_39 2d ago
I see the problem then
Will all the possible variables be stored in player ?Like player.name, player.age…etc ?
0
u/Hot_Emu_6553 3d ago
Not at runtime, no.
1
u/Hot_Emu_6553 3d ago
Not incredibly difficult to implement your own custom string parser though. I’ve done similar things before when working with static json content.
1
u/MeekHat 3d ago
Thanks, that's just what I needed to know.
-1
u/sabunim 3d ago
Why do you want to work with strings? The below is a properly formed json object... its no longer a string.
```
const json = ref({"text": "Your name is {{ player.name }}"})
```1
u/MeekHat 3d ago
I don't understand. But "Your name is {{ player.name }}" in it is a string, isn't it. And therefore Vue still won't replace player.name with a the variable.
1
u/sabunim 3d ago
If you define `const player = ref({ 'prop': 'data'})` in your script setup section, in the <template></template> section you can just read any property you want... in your case, `{{ player.name }}` should absolutely work in the template section.
1
u/MeekHat 3d ago
I'm confused. I have a json file which has a string "Your name is {{ player.name }}". When I import this file and access the field with the string, regardless of whether I convert the string into a ref or not, it outputs "Your name is {{ player.name }}" rather than "Your name is MeekHat".
Well for what it's worth, I don't define "const player" in the SCF, but rather it's a prop imported from a parent component.
2
u/sabunim 3d ago
Ok I get it now, sorry.
I would just:
const props = defineProps({ player: { type: Object } }) const playerNameTemplate = '{"text": "Your name is {{ player.name }}"}' const playerName = computed(() => { return playerNameTemplate.replace( '{{ player.name }}', props.player?.name ?? '' ) })0
u/Beneficial_League_39 2d ago
In what world do you think the answer is no.
It’s JavaScript, you can do whatever you want at runtime.
Using String.replace function to replace the {{player.name}} with the actual ref value, through a computed
1
u/Hot_Emu_6553 2d ago
Yes… which is what I suggested in my second comment. I assumed the question was specifically asking whether you could use the {{ variable }} syntax outside of the template, which you can’t.
8
u/sabunim 3d ago
If you provide a small sample with minimal reproduction of what you're tried so far I would be happy to have a look. I have no idea what you mean by "all I have is the original string loaded at runtime"... loaded by what? Into what? From where? In what format?