Vue - Content of contenteditable is doubling without variable change - javascript

I have 4 contenteditables which I am copying to a variable.
If I write "1234" in Path2, then change to Title1 and then defocus Title1, then Path2 will change to "12341234", but the variable this.PC.Path2 will still be "1234".
This error occurs on only content editable - Path2.
I have changed the name so only the Function updateHtml can interact with it(on my site) - no effect.
<div class="PathContainer" v-on:click="focusPath2">
<div class="namespace edit path" contenteditable="true" #blur="updateHtml" name="Path">{{PC.Path}}</div>
<div class="path editPath2" contenteditable="true" #blur="updateHtml" name="Path2" ref="Path2" >{{PC.Path2}}</div>
</div>
Title1: <span class="edit" contenteditable="true" #blur="updateHtml" name="Title2">{{ PC.Title2 }}</span><br>
Title2: <span class="edit" contenteditable="true" #blur="updateHtml" name="Title1">{{ PC.Title1 }}</span>
updateHtml: function(e) {
var foo = e.target.getAttribute("name")
console.log("PATH2---", this.PC.Path2);
this.PC[foo] = e.target.innerText;
console.log("PATH2---", this.PC.Path2);
console.log("UPDATING this.PC." + foo, " to-->", this.PC[foo]);
},
The Complete Code with Backend is on: https://github.com/i5heu/wikinota/tree/V2
Only the Code of this Module: https://github.com/i5heu/wikinota-web/blob/04629560ec0d6383b4ad8f92cb2647a616c559ce/js/pedit.js
Here is the Chrome Performance Profile. (with Pictures of the weird stuff)
weird-bug.zip
I'm using Vue.js v2.5.16.

I have found a workaround for this problem.
I think the problem is in the DOM engine of Vue.
So you have to trigger the DOM engine again to overwrite the content of the content-editable.
This is achieved by 1 Line.
updateHtml: function(e) {
var foo = e.target.getAttribute("name");
console.log("PATH2---", this.PC.Path2);
this.PC[foo] = e.target.innerText;
e.target.innerText = this.PC[foo]; //<<<< This is the workaround
console.log("PATH2---", this.PC.Path2);
console.log("UPDATING this.PC." + foo, " to-->", this.PC[foo]);
}

Related

How to use template literals in a constructor in JavaScript

I'm building a To Do list app and have a question regarding OOP and JavaScript. I want to create a value in the Constructor that holds my taskBody which contains the HTML and template literal that will be assigned by either the input value or an eventual population from local storage. My goal is to re-use this HTML in two separate functions, but I'm stuck with the template literal.
class Task {
constructor() {
let taskValue //Initializing a variable
this.taskBody = `<div class="task">
<span>${taskValue}</span> //Template Literal
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>`;
}
addTask = () => {
//Prevent empty task
if (this.input.value == "") {
this.setPlaceholder("Please Enter A Task");
return;
}
this.taskValue = this.input.value; //Trying to reassign taskValue to the input value
this.results.innerHTML += this.taskBody; //Trying to grab the HTML from the constructor and populate with taskValue able
ls.setLS(this.taskValue); //setting the Local Storage the Task Value, which works
};
}
I expect if I type "Stack Overflow" in the to-do list, "Stack Overflow" populates in the HTML and the Local Storage, however, it only populates in the Local Storage. The todo item is either undefined, null, or empty.
I've tried using this.taskValue, let taskValue = "", and let taskValue = null, but I get the results described above. Where am I going wrong, and more specifically, how can I reuse the HTML in different functions?
Here's a CodePen where you can see the issue:
Codepen
When you first instantiate the Task, the value of the this.taskBody is set as below:
<div class="task">
<span>undefined</span>
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>
with undefined value, because at the moment of instantiation, the taskValue is undefined.
If your todo list items are added dynamically (which is the case), consider having a function which will enable dynamic replacement, like:
getTaskBody = item => `<div class="task">
<span>${item}</span>
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>`;
and use it later in line 123, instead of:
this.results.innerHTML += this.taskBody;
do:
this.results.innerHTML += getTaskBody(this.taskValue);

Adding and removing Meteor templates

For a Meteor application I'd like the users to be able to add divs to a column by clicking on a button, with each of these having a button to remove the div. Here's an extract of the code:
<template name="MainTemplate">
....
<a class="btn btn-sm" class="add-dynamic"></a>
<div id="add-stuff-here"></div>
....
</template>
<template name="DynamicTemplate">
<div id="dynamic-{{uniqid}}">
<a class="btn btn-sm delete-dynamic" name="{{uniqid}}"></a>
</div>
</template>
...and in the javascript file:
Template.MainTemplate.events({
'click .add-dynamic'(event) {
const random = String.fromCharCode(65 + Math.floor(Math.random() * 26));
const uniqid = random + Date.now();
Blaze.renderWithData(Template.DynamicTemplate, {uniqid: uniqid}, $("#add-stuff-here")[0])
},
})
Template.DynamicTemplate.events({
'click .delete-dynamic'(event){
const target = event.target || event.srcElement;
const id = target.id;
console.log("Button ID: " + id); // This is null
new = $('#dynamic-' + id);
new.parentNode.removeChild(new); // Therefore, this fails
}
});
Adding the templates works as expected but deleting them fails as the id of the clicked button appears to be nil.
In any case I'm probably going about this in completely the wrong way (I haven't used Meteor for a long time and didn't know it very well anyway), and so would appreciate any suggestions as to how this might be accomplished.
ETA: The possible answer suggested in the comments includes:
UI.remove(self.view);
...which "expects a template rendered with Blaze.render". I'm not able to determine how to pass the identity of the template to be removed to the function which is run on the button click - does anyone have any suggestions?
In the end I worked around it by doing this:
<template name="DynamicTemplate">
<input type="text" class="dynamic-input" placeholder="Some text" />
</template>
Template.DynamicTemplate.events({
'click .delete-dynamic'(event){
inputs = $(".dynamic-input").length;
if ( inputs > 1 ) {
const element = $(".dynamic-input")[dynamic - 1];
element.parentNode.removeChild(element);
}
}
});
That seems to do more-or-less what I was after and avoids the problem of the null ID which I mentioned in the original post.

How do i get a value from Kendo UI template

I have this template:
var template = kendo.template("<div class='relatedItemRow'>"
+ "<span id='relatedItemName'>Related Item #: Number #</span>"
+ "<div class='relatedItemRowInfo'><span >#: Name #</span>"
+ "<a data-relatedItemID='#: Value #' class='removeRelatedItem'>"
+ "<img src= '#: Img #'/</a></div><br class='clear'/></div>");
var data = {
Name: "" + checkbox.getAttribute('flatName'),
Number: $('#relatedItemsList').children().length + 1,
Img: '/Content/images/x_remove.png',
Value: checkbox.value
};
var result = template(data);
$("#relatedItemsList").append(result);
I am able to access the data-relatedItemID by using:
$('#relatedItemsList').children().eq(i).children().last().attr('data-relatedItemID')
But how do I get to the Number field in data? I want to change that one dynamically.
I have tried:
$('#relatedItemsList').children().eq(i).children().attr('Number') == 5
but it does not work. Any idea how to do that?
I know that there is an answer for this question even accepted but I'd like to suggest a different approach that I think it is much simpler and more Kendo UI oriented and is using Kendo UI ObservableObject. This allows you to update an HTML that is bound to the ObservableObject without having to crawl the HTML.
This approach is as follow:
Wrap your data definition with a Kendo Observable Array initialization.
Redefine your template and start using using data-binding.
Append this new template to your HTML.
Bind data to the HTML.
Now you can get current value using data.get("Number") or set a new value using data.set("Number", 5) and the HTML get magically updated.
The code is:
Template definition
<script type="text/kendo-template" id="template">
<div class='relatedItemRow'>
<span id='relatedItemName'>Related Item <span data-bind="text : Number"></span></span>
<div class='relatedItemRowInfo'>
<span data-bind="html : Name"></span>
<a class='removeRelatedItem' data-bind="attr: { data-relatedItemID : Value }">
<img data-bind="attr : { src : Img }"/>
</a>
</div>
<br class='clear'/>
</div>
</script>
data definition as:
var data = new kendo.data.ObservableObject({
Name: "" + checkbox.getAttribute('flatName'),
Number: $('#relatedItemsList').children().length + 1,
Img: '/Content/images/x_remove.png',
Value: checkbox.value
});
and the initialization of the HTML is:
$("#relatedItemsList").append($("#template").html());
Getting current value of Number is:
var old = data.get("Number");
Setting is:
data.set("Number", 5);
Running example in JSFiddle here : http://jsfiddle.net/OnaBai/76GWW/
The variable "result" and thus the data you're trying to change aren't a Kendo templates, they're just html created by the template, and the number is just text in the html. To modify the number without rebuilding the whole string, you need to change the template so you can select the number by itself with jQuery by putting it in it's own element, and adding something to identify it.
var template = kendo.template("<div class='relatedItemRow'><span id='relatedItemName'>Related Item <span class='relatedNumberValue'>#: Number #</span></span><div class='relatedItemRowInfo'><span >#: Name #</span><a data-relatedItemID='#: Value #' class='removeRelatedItem'><img src= '#: Img #'/</a></div><br class='clear'/></div>");
//other code the same
And once you can select it, then you can change it like this.
$('#relatedItemsList').children().eq(i).find('.relatedNumberValue').text(5);
And retrieve it like this.
var foo = $('#relatedItemsList').children().eq(i).find('.relatedNumberValue').text();

this in event handling functions of Meteor: How does this get bound to model object?

The following code is taken from a tutorial in tutsplus.
if (Meteor.isClient) {
var Products = new Array(
{ Name: "Screw Driver",
Price: "1.50",
InStock: true},
{ Name: "Hammer",
Price: "2.50",
InStock: false}
);
Template.Products.ProductArr = function () {
return Products;
};
Template.Products.events = {
"click .Product": function () {
if (this.InStock)
confirm("Would you like to buy a " + this.Name + " for " + this.Price + "$");
else
alert("That item is not in stock");
}
};
}
Here is the template:
<template name="Products">
{{#each ProductArr}}
<div class="Product">
<h2>{{Name}}</h2>
<p>Price: ${{Price}}</p>
{{#if this.InStock}}
<p>This is in stock</p>
{{else}}
<p>This is sold out</p>
{{/if}}
</div>
{{/each}}
</template>
I wonder how this get bound to the model object product? This looks like magic to me.
The expression "click .Product" specifies that the click event on HTML elements having class Product should trigger the specified function. I understand it. But I don't understand why this is bound to an element of the Products array.
This is how Handlebars (which Meteor builds on) works. What you're seeing in the template isn't pure JS, but syntax specific to Handlebars.
Inside the each block helper, the context is to set each element of the array you're iterating over. So if you use InStock, it will look for it on the element of the current iteration.
The this keyword is used for disambiguation. This comes in handy if you have a general helper registered with the name InStock, like this, for example:
Template.Products.InStock = function (){
//...
};
But you want to make sure you're referring to the property of the element from the array, so you can use this to access its context explicitly.

Javascript Need to pull a value from a function within an array

Here is my code:
<script>
window.addEvent('domready', function(){
new Request.Stocks({
stocks: ['SXCL'],
onComplete: function(yahoo){
var result = '';
Array.each(Array.from(yahoo.query.results.quote), function(quote){
result += '<div class="company-ticks"></div>
<span class="company">Steel Excel ({Name})</span>
<span class="sub-info"> - OTC Markets<span> </div> <div><span class="value">
{LastTradePriceOnly}</span><span class="changeup">
<img src="change-up.gif" class="change-img" />{Change}
({ChangeinPercent})</span></div></div>'.substitute(quote);
}, this);
$('stocks').set('html', result);
},
onRequest: function(script){
$('stocks').set('text', 'Loading...');
}
}).send();
// Request.Stocks.element.js
});
</script>
You see where I have the variable {Change]. I need to determine if this variable is a positive or negative value. If it is positive then it should display the class as "changeup" and the image as change-up.gif. If the value is negative then the class displayed should be "changedown" and the image would be change-down.gif
The images are a green arrow up and a red arrow down. The classes make the color altrenate between red and green.
Because this is within an array thats being called using a function I'm not sure how to go about this. I assume I would have to split up my "result" into 3 sections. The section before, the section that sets the class and the image, and then the rest of the result.
Any help would be appreciated.
This uses Javascript with mooTools. It's pulling a stock quote from yahoo.
I made the assumption that the Change variable was a property of the quote object. Otherwise that's an easy fix in the code bellow.
Array.each(Array.from(yahoo.query.results.quote), function (quote) {
quote.changeImage = (quote.Change > 0) ? 'change-up.gif' : 'change-down.gif';
result += '<div class="company-ticks"></div>
<span class="company">Steel Excel ({Name})</span>
<span class="sub-info"> - OTC Markets<span> </div> <div><span class="value">
{LastTradePriceOnly}</span><span class="changeup">
<img src="{changeImage}" class="change-img" />{Change}
({ChangeinPercent})</span></div></div>'.substitute(quote);
}, this);
Please note, producing HTML in the was you are doing is a bit risky and hard to maintain.

Categories

Resources