Using VueJS with a PHP Variable - vue.js

I'm attempting to bind a HTML element which contains a string echoed via PHP so that I can use it with VueJS. Essentially what I am going to be doing is switching between GBP and USD depending on some php/mysql database queries (USD is default value). Here is a simplified example of what I have tried so far.
<div id="app">
<?php $string = 'GBP'; ?>
<!-- Hide this from the front end but bind to Vue somehow -->
<span v-el:currency style="display: none;"><?php echo $string; ?></span>
<p>Payment currency: {{ currency }}</p>
</div>
Of course I could just echo the php variable again, but the main reason I want to bind it to a VueJS element is so I can use the value of this element in my JS to do something like this...
if (this.currency === 'GBP') {
return "Paying in GBP";
} else {
return "Paying in USD";
}
Worth noting that I already have a fair bit of VueJS working within this #app so it's nothing to do with the configuration of Vue being wrong, more a case of just not knowing the correct way to approach the issue.

I would not interleave PHP and javascript inside a component.
Why don't you create a new script at the end with the variables you need?
<!-- bottom of the body -->
<script>var currency = <?php echo $yourVar; ?></script>
And then it will be a global variable and you just take it from there.

Related

How to include Handlebars partial in a string? (add it to the innerHTML of a DOM Element)

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'}}`;

How to handle empty array data in Vue.js

A simple question in Vue.js. In this code, the second "project" don't have any "Property", obviously no "Number", this make the html shows blank, how to handle this when there's no data in the array? I've tried v-if but can't make it work. Thanks
<div v-for="propiedad in info" class="propiedad">
<div class="propiedad">
{{ project.Property[0].Number }}
</div>
</div>
I take the question "this make the HTML shows blank" as "There is some errors in the console and the entire page fails."
You can do it by
project && project.Property[0] && project.Property[0].Number
Or if you are working with babel with plugin babel-preset-env, you can take advantage of the optional chaining feature
project?.Property[0]?.Number
First, this looks off because of the loop that you are not using. (Is there a mistake or why bother showing it?)
A v-if should work, but it often gets verbose.
<div class="propiedad" v-if="project.Property && project.Property.length > 0 && project.Property[0].Number>
That should cover all the bases.
Therefore it's often cleaner to massage the data beforehand. Maybe make all projects have defaults.project.Property, so you don't have to check if it exists.
Or just make computed properties so your template can look nice
computed: {
hasProperty(){
return this.project.Property
&& this.project.Property.length > 0
&& this.project.Property[0].Number
}
}
<div class="propiedad" v-if="hasProperty"> ... </div>
Or if this is indeed related to the loop . Make a computed array that filters the array.
computed: {
// Here I make the wild assumption that info are actually projects
// (just to make the loop relevant)
filtered(){
// Skip all the projects without "Properties".
return this.info.filter( i => i.Property.length > 0 )
}
}
<div v-for="propiedad in filtered"> ....

Vue v-model input change mobile chrome not work

If i open https://v2.vuejs.org/v2/guide/forms.html#Text and edit text - no effect on typing text in mobile chrome. #keyup #input #keypress - v-model does not change when I'm typing
<input v-model="message" #keyup="log" placeholder="Edit">
<p>Edited: {{ message }}</p>
How can i fix it? I need get input value on typing (#keyup #input)
Update: After a lot of discussion, I've come to understand that this is a feature, not a bug. v-model is more complicated than you might at first think, and a mobile 'keyboard' is more complicated than a keyboard. This behaviour can surprise, but it's not wrong. Code your #input separately if you want something else.
Houston we might have a problem. Vue does not seem to be doing what it says on the tin. V-model is supposed to update on input, but if we decompose the v-model and code the #input explicitly, it works fine on mobile. (both inputs behave normally in chrome desktop)
For display on mobiles, the issue can be seen at...
https://jsbin.com/juzakis/1
See this github issue.
function doIt(){
var vm = new Vue({
el : '#vueRoot',
data : {message : '',message1 : ''}
})
}
doIt();
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id='vueRoot'>
<h1>v-model</h1>
<div>
<input type='text'
v-model='message'
>
{{message}}
</div>
<h1>Decomposed</h1>
<div>
<input type='text'
:value='message1'
#input='evt=>message1=evt.target.value'
>
{{message1}}
</div>
</div>
I tried all solutions I could find on the internet, nothing worked for me. in the end i came up with this, finally works on android!
Trick is to use compositionupdate event:
<input type="text" ... v-model="myinputbox" #compositionupdate="compositionUpdate($event)">
......
......
methods: {
compositionUpdate: function(event)
{
this.myinputbox = event.data;
},
}
Ok, I dont know if there is another solution for this issue, but it can be solved with a simple directive:
Vue.directive('$model', {
bind: function (el, binding, vnode) {
el.oninput = () => (vnode.context[binding.expression] = el.value)
}
})
using it just like
<input v-$model="{toBind}">
There is an issue on the oficial repo, and they say this is the normal behavior (because the composition mode), but I still need the functionality
EDIT: A simpler solution for me was to just use #input.native. Also, the this event has (now?) a isComposing attribute which we can use to either take $event.data into account, or $event.target.value
In my case, the only scheme that worked was handling #keydown to save the value before the user action, and handling #keyup to process the event if the value had changed. NOTE: the disadvantage of this is that any non-keyboard input (like copy/paste with a mouse) will not work.
<md-input
v-else
:value="myValue"
ref="input"
#keydown="keyDownValue = $event.target.value"
#keyup="handleKeyUp($event)"
#blur="handleBlur()"
/>
With handleKeyUp in my case being:
handleKeyUp(evt){
if(evt.target.value !== this.keyDownValue){
this.$emit('edited', evt);
}
}
My use case was the following:
I requested a search endpoint in the backend to get suggestions as the user typed. Solutions like handling #compositionupdate lead to sending several several requests to the backend (I also needed #input for non-mobile devices). I reduced the number of requests sent by correctly handling #compositionStarted, but there was still cases where 2 requests were sent for just 1 character typed (when composition was left then, e.g. with space character, then re-entered, e.g. with backspace character).

Ember {{#linkTo}} helper's with {{each}}

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])
})

dijit.byId returns undefined

I have a hidden form and I am trying to put it into a variable via dijit.byId
Unfortunately it always returns undefined.
Am I missing something? dojo is flummoxing me at every corner - any help much appreciated.
js:
dojo.require("dijit.form.Form");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.ValidationTextBox");
dojo.addOnLoad(function() {
var regForm = dijit.byId("hiddenRegister");
//regForm is undefined
});
html:
<div id="hiddenRegister" dojoType="dijit.form.Form" jsId="hiddenRegister" encType="multipart/form-data" action="" method=""></div>
id and jsId should not be the same
and if you are using jsId, then there is no need for dijit.byId. The widget is already assigned to a variable using the jsId as the variable name.