vuejs - "editing" html inside variable - vue.js

I've got a variable which has html inside. This is for my wysiwyg editor.
data () {
return {
HTMLtext: "<p style="text-align: center;"><b>Hospital</b></p><p>
</p><p style="text-align: center;"> <b>Physician</b></p>
<p></p><p style="text-align: center;"> City <b>New York</b>",
Place: "London"
}
}
Now, I want to change this HTMLtext variable in some places. For example I've got variable called place:
Place: "London"
and I want to replace <b>New York</b> with Place variable into <b>London</b>
What should I do?
What is the best approach?
Is it generally safe?
Is this a good practice?

If you make HTMLtext a computed, it can compose the text based on the variables. Template strings are a nice way to interpolate variables into strings.
Here's an example of what I'm suggesting. I don't know if that will work in your circumstance because I don't know where HTMLtext comes from or how you know that New York is in it to be replaced.
new Vue({
el: '#app',
data: {
place: 'New York'
},
computed: {
htmlText() {
return `<p style="text-align: center;"><b>Hospital</b></p><p>
</p><p style="text-align: center;"> <b>Physician</b></p>
<p></p><p style="text-align: center;"> City <b>${this.place}</b>`;
}
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
Place: <input v-model="place">
<div v-html="htmlText"></div>
</div>

Related

How to set focus on input field in Vuejs when field loads conditionally with v-if

My form has a first name and last name that initially appear as one compact string, but split into two input fields when the user clicks on the string. I need these two input fields to disappear and revert back to the compact string format when the user clicks elsewhere on the page (anywhere other than inside those two input fields). In order to achieve this, I use a v-on directive with the blur attribute. This works well, but only if the user first clicks into one of the input fields to trigger focus. This is because blur only triggers on an input field that is already in focus. Is there either a way for me to automatically put the first input field in focus when the input element conditionally renders? Is there a better approach?
Cheers.
Here is my HTML:
<a v-if="!eitherNameSelected" #click="firstNameSelected">
{{person.firstname}} {{person.lastname}}</a>
<div class="row" v-else-if="eitherNameSelected" v-enter="focusOnFirstChild($event)">
<div class="col-md-3">
<input
#focus="firstnameselected=true"
#blur="firstnameselected=false"
type="text"
class="form-control"
name="example-text-input-invalid is-invalid"
id="firstname"
placeholder="First Name"
v-model="person.firstname"
>
<div class="invalid-feedback">
Invalid feedback
</div>
</div>
<div class="col-md-3">
<input
#focus="lastnameselected=true"
#blur="lastnameselected=false"
type="text"
class="form-control"
name="example-text-input-invalid is-invalid"
placeholder="Last Name"
v-model="person.lastname"
>
<div class="invalid-feedback">
Invalid feedback
</div>
And in my Javascript ...
data() {
return {
listid: 0,
listname: "",
personid: 0,
person: {},
nameselected: false,
activetab1: "main",
activetab2: "notes",
firstnameselected: false,
lastnameselected: false,
....
methods: {
firstNameSelected() {
var elem = vm.$el.getElementById('firstname');
elem.focus();
this.firstnameselected="true";
}
},
computed: {
eitherNameSelected() {
return (this.firstnameselected || this.lastnameselected);
}
},
It may not be the best solution but you could use a #click($event) in order to know where you clicked to hide or not the inputs.
This post about checking event targets can be a good start I guess.
I also coded a minimal example to help you get through your issue. I hope it will help you.
new Vue({
el: "#app",
data: {
person: {
firstname: 'PersonFirstname',
lastname: 'PersonLastname'
},
showCompactString: true
},
methods: {
onCompactStringClicked() {
this.showCompactString = false
},
// Hide the inputs if a click is triggered outside of them
onAppClicked(event) {
// Do nothing if compact string is being shown
if (this.showCompactString)
return
const fistnameInputClickedOn = event.target.matches('#firstname')
const lastnameInputClickedOn = event.target.matches('#lastname')
const anyInputClickedOn = fistnameInputClickedOn || lastnameInputClickedOn
if (!anyInputClickedOn)
this.showCompactString = true
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" #click="onAppClicked($event)">
<a v-if="showCompactString" #click.stop="onCompactStringClicked">{{person.firstname}} {{person.lastname}}</a>
<div class="row" v-else>
<div>
<input type="text" id="firstname" placeholder="First Name" v-model="person.firstname">
</div>
<div>
<input type="text" id="lastname" placeholder="Last Name" v-model="person.lastname">
</div>
</div>
</div>
Don't hesitate to leave a comment if you have trouble. I'll answer you back as fast as I can.
put an #click=someFunction (or computed) on the root element (i.e. the whole body of HTML) and then have that change the text.

v-if inside v-for is giving me Property or method "*" is not defined on the instance

I want to be able to compare two dates in vue and add a class to the div if the date is less then the today date. This is my code:
<tr v-for="car in cars">
<div v-if="Date.now() <= new Date(car.ContractDate)">if true, add class to this div</div>
</tr>
But I am getting this error instead:
[Vue warn]: Property or method "car" is not defined on the instance
but referenced during render. Make sure that this property is
reactive, either in the data option, or for class-based components, by
initializing the property.
When i try to execute <td>{{ car.ContractDate }}</td> inside my v-for i don't have any issues.
I created a rough example based on the information you gave:
new Vue({
el: '#app',
data() {
return {
cars: [{
name: 'Car X',
contractDate: '9/17/2016, 1:21:34 PM'
},
{
name: 'Car Y',
contractDate: '9/17/2020, 1:21:34 PM'
}]
}
},
methods: {
compareDate(d) {
return (new Date(d)).getTime() > (new Date()).getTime();
}
}
});
.active {
color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" v-cloak>
<table>
<tbody>
<tr v-for="car in cars">
<td>{{car.name}}</td>
<td>
<div :class="{ active: compareDate(car.contractDate) }">
{{car.contractDate}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
Notes
Instead of doing the comparison in the actual template, I elected to do it in a method - it felt cleaner
I'm using object syntax for the class bind, again, purely preference, you could use a ternary if you so chose to.
Hope this helps!
Based on your code : https://pastebin.com/MmBcp0Bx
you should remove this line:
<div v-if="Date.now() <= new Date(car.ContractDate)">if true, add class to this div</div>

use vue and v-model with a css specific checkbox

I am migrating some jQuery code to Vue. We have a part that is like the following:
<td>
<p>
<input class="select-one checkbox-default-input" type="checkbox" :value="placement.id" v-model="checkedPlacements">
<label for="select-clicks" class="checkbox-default" #click="setValue"></label>
</p>
</td>
ie I am using a label to adjust the UI of an input similar to this: Pure CSS Checkbox Image replacement
how would I associate my v-model with the correct input value?
<td>
<p>
<input id="select-clicks" class="select-one checkbox-default-input" type="checkbox" :checked="placement.id" #input="toggleCheckbox">
<label for="select-clicks" class="checkbox-default"></label>
</p>
</td>
I have added an id attribute to the input tag which corresponds to the label for attribute.
:checked="placement.id" - Here placement.id should correspond to a boolean value stored on your vue component's data property.
#input="toggleCheckbox" - this should be a simple method on your vue component that toggles the value. Something like this:
toggleCheckbox() {
this.placement.id = !this.placement.id;
},
You can then apply conditional classes to the element using the :class bindings. You can read more about them here: https://v2.vuejs.org/v2/guide/class-and-style.html
As #GustavMahler pointed out, Vue Guide: Form Input binding shows how to reach the goal.
#GustavMahler already provided the solution by v-bind and v-on (actually v-model is one syntax sugar which does similar things. one simple explanation at here),
Below is the steps uses v-model:
add attr=id for the checkbox, so label know which checkbox it links.
v-model to one data property which is one Array. (if default value is one Boolean, the value will be one Boolean value (true/false), it ignores the value it binds (please check the third checkbox).)
click the checkbox(label), it will toggle the values to the array.
Below is one demo (the CSS part is copied from the link in the question).
new Vue({
el: '#app',
data() {
return {
placement: {id: 'value1'},
checkedPlacements: [], // default is one Array
checkedPlacement: false // default is one Boolean
}
},
watch: {
checkedPlacements: function (newVal) {
console.log('changed', newVal)
},
checkedPlacement: function (newVal) {
console.log('changed', newVal)
}
}
})
input[type=checkbox] {
display:none;
}
input[type=checkbox] + label
{
background: #999;
height: 16px;
width: 16px;
display:inline-block;
padding: 0 0 0 0px;
}
input[type=checkbox]:checked + label
{
background: #0080FF;
height: 16px;
width: 16px;
display:inline-block;
padding: 0 0 0 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<p>
<input class="select-one checkbox-default-input" type="checkbox" :value="placement.id" v-model="checkedPlacements" id="select-clicks1">
<label for="select-clicks1" class="checkbox-default"></label>
<input class="select-one checkbox-default-input" type="checkbox" :value="'value2'" v-model="checkedPlacements" id="select-clicks2">
<label for="select-clicks2" class="checkbox-default"></label>
</p>
<p>
<input class="select-one checkbox-default-input" type="checkbox" :value="placement.id" v-model="checkedPlacement" id="select-clicks3">
<label for="select-clicks3" class="checkbox-default"></label>
</p>
</div>

Passing a variable to a Vue component from the parent page that called it

I have a simple table that displays all of my data:
main file.php
<table class="table table-bordered table-hover" id="all-jobs">
<thead>
<tr>
<th>{{ __('Job Name') }}</th>
<th>{{ __('Job Description') }}</th>
<th>{{ __('Job Status') }}</th>
<th>{{ __('Job Applications') }}</th>
<th>{{ __('Manage') }}</th>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="non_searchable"></td>
<td class="non_searchable"></td>
</tr>
</thead>
</table>
<div id="app">
<div id="editJob" class="modal fade in" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<edit-job id=""></edit-job>
</div>
</div>
</div>
</div>
Now, I have a edit button that I am trying to open an edit modal for that specific row:
<a href='' data-id='{$job->id}' class='btn btn-xs btn-danger' data-toggle='modal' data-target='#editJob'><i class='fa fa-close'></i></a>";
The href is is location in one of the of my data table, I am trying to pass that to my .vue file so I can use it for my get and post requests:
myfile.vue
<template>
<div>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Edit Job</h4>
</div>
<div class="modal-body">
<form method="post" #submit.prevent="signIn" #keydown="errors.clear($event.target.name)">
<!-- Removed code, it's just inputs !-->
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info btn-fill btn-wd" v-on:click="addJob">Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</template>
<script>
export default
{
props: ['id'],
data: function ()
{
return {
countries: [],
name: '',
summary: '',
salarytype: '',
salaryfrom: '',
salaryto: '',
location: '',
contactemail: '',
contactphone: '',
errors: new Errors()
}
},
methods:
{
addJob: function()
{
axios.post('/jobs/edit', this.$data)
.then(response => {
if(response.data.status === true){
$('#editJob').modal('hide');
getJobTable();
}
else{
formError = response.data.message;
}
})
.catch(error => this.errors.record(error.data))
}
},
mounted: function()
{
console.log($(this).data('id'));
axios.get('/jobs/my-job/')
.then(response => {
this.name = response.data.name
this.summary = response.data.summary
this.salarytype = response.data.salary_type
this.salaryfrom = response.data.salary_from
this.salaryto = response.data.salary_to
this.location = response.data.location
this.contactemail = response.data.contact
this.contactphone = response.data.phone
})
axios.get('/countries')
.then(response => {
this.countries = response.data;
})
}
}
</script>
How can I past my href id to my to use for my request? Thanks
MY structure:
Created-jobs.blade.php
https://pastebin.com/TPBnC1qP
Edit-Job.vue
https://pastebin.com/30UWR5Nn
app.js
https://pastebin.com/1yxZWvVC
The table just populates the data, and adds the dropdown like so:
<ul class='icons-list'>
<li class='dropdown'>
<a href='#' class='dropdown-toggle' data-toggle='dropdown' aria-expanded='false'>
<i class='icon-menu9'></i>
</a>
<ul class='dropdown-menu dropdown-menu-right'>
<li>
<a data-id='{$job->id}' onclick='getID({$job->id})' data-toggle='modal' data-target='#editJob'>
<i class='icon-file-pdf'></i> Edit Job
</a>
</li>
<li>
<a href='javascript:void();' data-id='{$job->id}' onclick='deleteJob({$job->id})'>
<i class='icon-cross'></i> Delete Job
</a>
</li>
</ul>
</li>
</ul>
You don't give a lot of information about the structure of your application, but it looks like you are using at least one single file component to display the data inside your modal which is being entirely displayed via Bootstrap. It also looks like the table with the id values you want to pass to Vue is outside of the Vue itself.
That being the case, the way you can pass the data you want to the single file component is to capture the Vue in a variable and then set the id whenever the link in your table is clicked.
Let's suppose your main.js or app.js looks like this:
import Vue from 'vue'
import EditJob from './EditJob.vue'
Vue.component('edit-job', EditJob)
const app = new Vue({
el: '#app',
data:{
id: null
}
})
// Add a click handler for the links with the `data-id` property.
// This is using jQuery (because you are using Bootstrap) but you
// can do this any way you want.
$("[data-id]").on("click", function(){
// Set the Vue's data to the data-id property in the link.
app.id = this.dataset.id
})
Notice how the code captures the result of new Vue(...) in the variable app. Then I've added the data property, id to the Vue and a click handler for all of your links that sets app.id to this.dataset.id whenever a link is clicked. In this way, every time a link is clicked, the data property in the Vue will be set to the id of the clicked link.
Then, all you need to do is bind the id property to your component.
<edit-job :id="id"></edit-job>
and your EditJob component will always get the updated id.
Here is a working example.
Edit
In the code you added to your example, you are defining all of your jQuery script in Created-jobs.blade.php. That being the case, the function you wrote, getID doesn't have access to the app variable you defined in your webpack bundle because of normal javascript scoping rules. To make app accessible to your jQuery code, add it to the window.
window.app = new Vue({
el: '#app',
data:{
id: null
}
});
Secondly, though you defined the getID function, nothing calls it. It needs to be called when the links are clicked. Add this somewhere to your jQuery code in Created-jobs.blade.php (ideally in a document ready function).
$("[data-id]").on("click", getID)

What's proper method to highlight one element?

I want to show red on r.data2 by vue.js, but it can not work proper, any hint to debug it?
The data 'rows' is updated by a timer per second. The 'Status', 'data1', 'data2' are generated on server side.
vm = new Vue({
el: '#app',
data: {
rows: [{Status:1, data1:"d1", data2:"d2"}]
}
})
...
<tr v-for="r in rows">
<td><button class="st-{{r.Status}}">r.data1</button></td>
<td valign="top">
<div v-if="r.Status == 4">
<font color="red">{{r.data2}}</font>
</div>
<div v-else>
{{r.data2}}
</div>
</td>
</tr>
Did you try class and style bindings as given in Vue docs?
Ref: https://vuejs.org/guide/class-and-style.html#Object-Syntax-1
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
and your data:
data: {
activeColor: 'red',
fontSize: 30
}
That example is a copy-paste from the docs link above. Check it out!
Edited answer after comment #1:
The best way is to use array syntax for class bindings, as follows:
<div v-bind:class="['some-static-class-for-your-data', { 'danger-class' : data2 == 4 }]">
my data2 value = {{data2}}
</div>
And set a CSS like:
.danger-class {
color: red;
}
Now whenever your data2 has the value of 4, the 'danger-class' will be set on your div. Your CSS ensures that it is displayed in red, or whatever color you choose.
If you have other classes for your data, you can put it as shown in the above example with some-static-class
It is a good practice to set meaningful class names instead of hard-coding styles. So I would not recommend you to go for style bindings, though you can do it if you want. Check out the docs link for class and style bindings.