I want to get route name from the list of items iterated using {{each}} helper.Some thing like example below.
<script type="text/x-handlebars" data-template-name="orders">
<h2> list all orders</h2>
{{#each content}}
{{#linkTo {{route name}} }}Some Text{{/linkTo}}
{{/each}}
</script>`enter code here`
You might wanna take a look at this. Have tried, but it would be awesome nested handlebars work.
https://github.com/mateusmaso/handlebars.nested
If the above doesn't work, you can try using a handlebar helper to set a variable within the controller. And use the variable value on the linkTo. It's kind of hacky, but this is what I did:
/**
* Used to set color of pie chart legend
*/
Ember.Handlebars.helper('setColor', function(controller, property, array, index) {
controller.set(property, array[index])
})
Related
Is there a way to get the "string version" of a handlebars partial to include it in the innerHTML of an HTML element?
For instance, imagine I have a ToDo list, and I want to add a task everytime I click the button "Add Task", like this:
todo_list.hbs
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
And that I have a handlebars partial in the file "task.hbs":
task.hbs
<h1 class="task-title">The task is: {{title}}</h1>
<button id="delete-task">Delete task</button>
<script>
const button_delete_task = document.getElementById('delete-task');
button_delete_task.addEventListener('click', deleteTask);
function deleteTask () {
// delete task code here
}
</script>
My question is: How could I create a Task partial everytime the button "Add Task" is clicked? Something like this:
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
const todo_list = document.getElementById('todo_list');
todo_list.innerHTML += {{> Task title="A new task"}};
// More code here...
}
</script>
I have also tried enclosing the partial with backticks (`{{> Task title="A new task"}}`), and quotes ("{{> Task title='A new task'}}") as well as read many posts on this subject, but all of them use handlebars.js, not express-handlebars.
I am using express.js for the backend, and therefore, express-handlebars as the view engine. In advance, thanks a lot for your help!
I managed to solve the issue!
It turns out that enclosing the partial with backticks works! The problem was that my partial had <script></script> tags.
Imagine my task.hbs looked like this:
<div>
<script></script>
</div>
then, the processed version of todo_list.hbs would look like this:
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
const todo_list = document.getElementById('todo_list');
todo_list.innerHTML += `<div>
<script></script>
</div>`;
// More code here...
}
</script>
This would be valid in a normal HTML file, but it looks like handlebars process the closing script tag that is inside the string (</script>) as a normal tag, and with it, closes the <script> tag of todo_list.hbs.
The solution I found was to not use <script> tags into my partial (not a beautiful solution, but works for me!) and instead, declare the javascript code in another file, and import it into todo_list.hbs using <script> tags with the src parameter like this:
todo_list.hbs
<div id="todo-list">
</div>
<button onclick="addTask">Add Task</button>
<script>
function addTask() {
const todo_list = document.getElementById('todo_list');
todo_list.innerHTML += `{{> Task title="New task!"}}`;
// More code here...
}
</script>
<!-- JAVASCRIPT CODE REQUIRED BY TASK PARTIAL -->
<script src="/foo/bar/partials/Task.js"></script>
Where Task.js is the file containing the javascript of the Task.hbs partial:
Task.js
const button_delete_task = document.getElementById('delete-task');
button_delete_task.addEventListener('click', deleteTask);
function deleteTask () {
// delete task code here
}
And with this changes, Task.hbs would look like this:
Task.hbs
<h1 class="task-title">The task is: {{title}}</h1>
<button id="delete-task">Delete task</button>
You are very close to getting this to work.
As you have noted, your Handlebars is executing on the server-side. In the case of your partial, you are trying to have it render within a script block. In order for the result to be valid JavaScript, you would need have quotes around the output of the partial so that it will be a valid JavaScript string. Therefore:
todo_list.innerHTML += "{{>Task title='A new task'}}";
Which, when rendered, would result in:
todo_list.innerHTML += "<h1>The task is: A new task</h1>";
It should be noted that quotes in your partial could be problematic. For example, if the <h1> in your partial had a class <h1 class="task">, the resultant JavaScript would now be invalid because the quote after the = would be interpreted as the closing quote of the JavaScript string. Therefore, you would need to be sure to either escape the quotes in your partial or ensure they are different from those used to wrap your partial call (a single-quote ('), in this case.
todo_list.innerHTML += "<h1 class=\"task\">The task is: A new task</h1>";
Additionally, you have an inconsistency with the id of your <div>. The tag has id="todo-list" (with a dash); but your JavaScript has document.getElementById('todo_list') (with an underscore). Those will need to be consistent.
Update
As #Sharif Velásquez Alzate noted in comments, the quotes will not work when the partial contains line-breaks because JavaScript strings cannot span multiple lines (unless each line ends with a \ to signify that the text continues to the next line. However, a template literal, using back-ticks, will support text with line-breaks.
Therefore, a better solution is:
todo_list.innerHTML += `{{>Task title='A new task'}}`;
I am using vuejs3 and I want to make a filter. When user click to the link I want to append the url and push it to browser address bar for now. Later I will do ajax request to update page with product list.
So far I am able to send parameters to URL, but only one item from one group.From first color group I want user to select only one but from second size group I want user to select multiple.
I want this type of URL: localhost:8080/product?color=red&size=medium&size=large
<template>
<div class="products">
<div class="multi_filters">
<h1>Multi Filter By Color</h1>
Red color
Blue color
</div>
<div class="single_filter">
<h1>Multi Size</h1>
Medium
Large
</div>
</div>
</template>
<script>
export default {
data() {
return {
filters:{},
selectedFilters:{}
}
},
methods:{
activateFilter(key,value){
this.selectedFilters = Object.assign({},this.selectedFilters,{[key]:value})
console.log(this.selectedFilters)
this.$router.replace({
query: {
...this.selectedFilters
}
})
}
}
}
</script>
You are expecting size to be an array, this post will helps.
Submitting multi-value form fields, i.e. submitting arrays through GET/POST vars, can be done several different ways, as a standard is not necessarily spelled out.
Three possible ways to send multi-value fields or arrays would be:
?cars[]=Saab&cars[]=Audi (Best way- PHP reads this into an array)
?cars=Saab&cars=Audi (Bad way- PHP will only register last value)
?cars=Saab,Audi (Haven't tried this)
I'm creating a list of (thumbnail) 2 images, and #click each image should be expanded to max-width, by adding said class ('max-w-full') to the classes. The class is added by setting the array entry with the same index nr. as the image (imgclicked[0], imgclicked[1]) in the list to 1.
data:() {
imgclicked=[0,0], //by default both are set to 'small'/not 'max-w-full'
},
the template part looks like this:
<template v-for="(image,index) in images>
<a #click="zoomImgClicked(index)">
<img :src="image.filename" :class={'max-w-full' : imgclicked[index]==1,'w-12' : imgclicked[index]==0}">
</a> // using this.imgclicked[index] gives me the error 'this.imgclicked[0] is undefined'
</template>
on click the method zoomImgClicked() ist launched:
zoomImgClicked: function(i){
if(this.imgclicked[i]==0){
this.imgclicked[i]=1
}else{
this.imgclicked[i]=0
}
},
But this is not working. If I open the vue console and change the values of imgclicked[0] manually from 0 to 1 it works (images ae being resized). Also I see the method doing it's work, when logging in the console, the values are changed from 0 to 1 and vice versa. But it's not reflected on the page #click.
Why is that?
Please read Change Detection Caveats in the Vue docs.
Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
You should use the $set method for this:
this.$set(this.imgclicked, i, 1)
try with array syntax:
<img
:src="image.filename"
:class="[
{
'max-w-full': imgclicked[index]==1,
'w-12': imgclicked[index]==0
},
'static class if need'
]"
>
I'm using the riot for the system. but I have a problem using the common tag in every place. because I have to copy the all common tag each page.
I added all tags like this. Does anyone have the solution for this ?
<st-service>
<st-alert></st-alert>
<st-header></st-header>
<st-body></st-body>
<st-footer></st-footer>
</st-service>
<st-profile>
<st-alert></st-alert>
<st-header></st-header>
<st-body></st-body>
<st-footer></st-footer>
</st-profile>
I found a solution, I'm using this method to handle these common tags. like this
<st-common>
<st-alert></st-alert>
<st-header></st-header>
<yeild></yeild>
<st-footer></st-footer>
</st-common>
service-page.tag // page
<st-service-page>
<st-common>
<st-service></st-service>
</st-common>
<st-service-page>
profile-page.tag // page
<st-profile-page>
<st-common>
<st-profile></st-profile>
</st-common>
<st-profile-page>
service-view.tag
<st-service>
// html / code body related to module
</st-service>
profile-view.tag
<st-profile>
// html / code body related to module
</st-profile>
If needed in details about this I can explain.
I'd have to know more about how you're routing to say for sure, but I think you should avoid using a different outer tag for each page. If your HTML looks something like this:
<body>
<st-app />
<script>
const pages = {
"/": "st-home",
"/about/": "st-about",
}
const content_tag = pages[window.location.pathname] || "st-notfound"
riot.mount("st-app", {
content_tag: content_tag
})
</script>
</body>
Then <st-app> would be defined something like:
<st-app>
<st-alert></st-alert>
<st-header></st-header>
<div data-is={this.opts.content_page}></div>
<st-footer></st-footer>
</st-app>
The important thing here being that you're controlling which tag should be used via the data-is attribute and the mounting options for <st-app>. In this example <st-home>, <st-about>, and <st-notfound> are riot components defined elsewhere.
<div>
{{#each value in controller}}
<div {{classNameBindings "col-lg-{{value}}"}}>{{value}}</div>
{{/each}}
</div>
Above is my partial view.
I want to generate classes like: col-lg-1, col-lg-2 etc
My controller is:
App.circleController = Ember.ArrayController.extend({
setupController: function(controller) {
controller.set('content', [1,2,3,4,5,6,7]);
}
});
why I get error: assertion failed: an Ember.CollectionView's content must implement Ember.Array. ?
I use a custom view to apply dynamically-named classes to items inside of an each helper. The class name is generated inside the view by a property than depends on a supplied index.
App.ItemView = Ember.View.extend({
classNameBindings: ['itemClass'],
index: null,
itemClass: function() {
return 'class-'+this.get('index');
}.property('index')
});
In the template, I supply the index through a {{view}} helper inside each iteration.
{{#each value in controller}}
{{#view App.ItemView indexBinding="value"}}
Item #{{value}}
{{/view}}
{{/each}}
For a closer look, check out this jsfiddle.