Render an Object as a String in Final Template but Pass the Full Object to Tags #756
Replies: 4 comments
-
A drop with a import { Liquid, Tag, Drop, evalToken, Tokenizer } from "liquidjs";
class MyDrop extends Drop {
constructor(obj) {
super();
this.value = obj.Value;
this.type = obj.Type;
this.name = undefined;
}
lookupName() {
if (this.name === undefined) {
// TODO: database lookup here
let data = { firstName: "foo", lastName: "bar" };
this.name = `${data.firstName} ${data.lastName}`;
}
return this.name;
}
valueOf() {
return this.lookupName();
}
}
class MyTag extends Tag {
constructor(token, remainTokens, liquid) {
super(token, remainTokens, liquid);
const tokenizer = new Tokenizer(token.args);
// In this example, arguments could be a mix of literals and variables.
this.arguments = [];
while (!tokenizer.end()) {
this.arguments.push(tokenizer.readValue());
}
}
*render(ctx, emitter) {
for (const arg of this.arguments) {
let obj = yield evalToken(arg, ctx);
if (obj instanceof MyDrop) {
// Access to drop properties here.
// Outputting as a JSON string for demonstration purposes.
emitter.write(JSON.stringify({ value: obj.value, type: obj.type }));
}
}
}
}
const liquid = new Liquid();
liquid.registerTag("mytag", MyTag);
const exampleTemplate = `\
Hello, {{ someDrop }}!
{% mytag someDrop %}`;
const someObject = {
Value: "12345",
Type: "user",
};
// Wrap plain object in a drop
const someDrop = new MyDrop(someObject);
liquid.parseAndRender(exampleTemplate, { someDrop }).then(console.log); Output
And captured values are always rendered strings.. // continued from above
const captureExampleTemplate = `\
{% capture foo %}
Hello, {{ someDrop }}!
{% mytag someDrop %}
{% endcapture %}
{{foo}}`;
liquid.parseAndRender(captureExampleTemplate, { someDrop }).then(console.log); Output
|
Beta Was this translation helpful? Give feedback.
-
I confused valueOf and toLiquid when testing this in my code before posting the issue. Now it works as expected. {% assign my_variable = some_function param1 param2 %} Instead I'm thinking about doing something like: {% some_function param1 param2 set:my_variable %} And internally either set the variable or return the value depending on wether "set" is there or not. Is there a better way of doing this? (Can I even do that?) |
Beta Was this translation helpful? Give feedback.
-
That seems quite reasonable to me. The standard class MyTag extends Tag {
constructor(token, remainTokens, liquid) {
super(token, remainTokens, liquid);
const tokenizer = new Tokenizer(token.args);
// NOTE: param1 and param2 could be undefined here
this.param1 = tokenizer.readValue();
tokenizer.skipBlank();
this.param2 = tokenizer.readValue();
tokenizer.skipBlank();
const optionName = tokenizer.readNonEmptyIdentifier()?.content;
tokenizer.assert(
optionName === undefined || optionName === "set",
`expected option 'set', found '${optionName}'`
);
if (optionName !== undefined) {
tokenizer.skipBlank();
tokenizer.assert(tokenizer.peek() === ":");
++tokenizer.p; // Move past colon
tokenizer.skipBlank();
this.option = tokenizer.readNonEmptyIdentifier();
tokenizer.assert(
this.option !== undefined,
"expected option 'set' to be passed an identifier"
);
} else {
this.option = undefined;
}
}
*render(ctx, emitter) {
const obj1 = yield evalToken(this.param1, ctx);
const obj2 = yield evalToken(this.param2, ctx);
// TODO: something with obj1 and obj2
if (this.option !== undefined) {
const variableName = yield evalToken(this.option, ctx);
ctx.environments[variableName] = "some value";
}
}
} |
Beta Was this translation helpful? Give feedback.
-
That is perfect! Thank you both for the help, this also lets me see how things are done in a more standard way. I will close the issue now |
Beta Was this translation helpful? Give feedback.
-
I’m currently using LiquidJS and I’m trying to implement the following behavior:
Example Scenario:
Let’s say I have an object structured like this:
{{ object }}
), I want it to trigger a lookup in the database (in this case, using the Value field to find the firstName and lastName of the user) and return a string with the user's name.Specific Questions:
Is it possible to achieve this behaviour using Drops and toLiquid() and/or valueOf() in LiquidJS?
{{ object.name }}
it worked. But I want to have this behaviour without having to add ".name" and simply have{{ object }}
.{% capture %}
tag? If I use the{% capture %}
tag to store this object in a variable, will the captured variable hold the entire object (with its Value and Type fields), or will it contain the rendered string (the user's name after the database lookup)?Would userVar hold the object or the rendered string (e.g., "John Doe")?
Beta Was this translation helpful? Give feedback.
All reactions