I have a chat on my website that reads from a JSON file and grabs each message and then displays it using Vue.js. However, my problem is that when a user posts a link, it is not contained in an anchor tag <a href=""/>. Therefore it is not clickable.
I saw this post, and I think something like this would work, however, I am not allowed to add any more dependencies to the site. Would there be a way for me to do something similar to this without adding more dependencies?
Code for displaying the message.
<p v-for="msg in messages">
<em class="plebe">
<b> [ {{msg.platform.toUpperCase()}} ]
<span style="color: red" v-if="msg.isadmin">{{msg.user.toUpperCase()}}</span>
<span style="color: #afd6f8" v-else="">{{msg.user.toUpperCase()}}</span>
</b>
</em>:
{{msg.message}}
</p>
In a situation like this, its preferred to write a custom functional component.
The reason for this is the fact that we are required to emit a complex html structure, but we have to make sure to properly protect against xss attacks (so v-html + http regex is out of the picture)
We are also going to use render functions, because render functions have the advantage to allow for javascript that generates the html, having more freedom.
<!-- chatLine.vue -->
<script>
export default {
functional: true,
render: function (createElement, context) {
// ...
},
props: {
line: {
type: String,
required: true,
},
},
};
</script>
<style>
</style>
We now need to think about how to parse the actual chat message, for this purpose, I'm going to use a regex that splits on any length of whitespace (requiring our chat urls to be surrounded with spaces, or that they are at the start or end of line).
I'm now going to make the code in the following way:
Make a list for child componenets
Use a regex to find url's inside the target string
For every url found, do:
If the match isn't at the start, place the text leading from the previous match/start inside the children
place the url inside the list of children as an <a> tag, with the proper href attribute
At the end, if we still have characters left, at them to the list of children too
return our list wrapped inside a P element
Vue.component('chat-line', {
functional: true,
// To compensate for the lack of an instance,
// we are now provided a 2nd context argument.
// https://vuejs.org/v2/guide/render-function.html#createElement-Arguments
render: function (createElement, context) {
const children = [];
let lastMatchEnd = 0;
// Todo, maybe use a better url regex, this one is made up from my head
const urlRegex = /https?:\/\/([a-zA-Z0-9.-]+(?:\/[a-zA-Z0-9.%:_()+=-]*)*(?:\?[a-zA-Z0-9.%:_+&/()=-]*)?(?:#[a-zA-Z0-9.%:()_+=-]*)?)/g;
const line = context.props.line;
let match;
while(match = urlRegex.exec(line)) {
if(match.index - lastMatchEnd > 0) {
children.push(line.substring(lastMatchEnd, match.index));
}
children.push(createElement('a', {
attrs:{
href: match[0],
}
}, match[1])); // Using capture group 1 instead of 0 to demonstrate that we can alter the text
lastMatchEnd = urlRegex.lastIndex;
}
if(lastMatchEnd < line.length) {
// line.length - lastMatchEnd
children.push(line.substring(lastMatchEnd, line.length));
}
return createElement('p', {class: 'chat-line'}, children)
},
// Props are optional
props: {
line: {
required: true,
type: String,
},
},
});
var app = new Vue({
el: '#app',
data: {
message: 'Hello <script>, visit me at http://stackoverflow.com! Also see http://example.com/?celebrate=true'
},
});
.chat-line {
/* Support enters in our demo, propably not needed in production */
white-space: pre;
}
<script src="https://unpkg.com/vue#2.0.1/dist/vue.js"></script>
<div id="app">
<p>Message:</p>
<textarea v-model="message" style="display: block; min-width: 100%;"></textarea>
<p>Output:</p>
<chat-line :line="message"></chat-line>
</div>
You can watch or write computed method for the variable having url and manupulate it to html content and then use v-html to show html content on the page
v-html
Related
Why is the #page rule not working in browsers? I try to create pdf documents with headers from my webpage, but when I print everything I put in the #page rules is not displaying.
Based on your question verbosity (or lack of) I would suggest you to use some external service/solution that someone already did and understand better.
I personally use html2pdf (https://html2pdf.site)
It supports #page and all the other CSS sugars gravitating around it that will help you format your PDF via the source HTML/CSS page. I would say it does surprisingly good job (and fast).
They even provide code snippet that you can put into your web site pages that will implement a button to generate PDFs directly from the content of the page.
Good luck.
Well... It can't be more simple. Just add these snippets (provided within their templates) in your HTML. Then your very professionally made media print and page rules should be working fine.
IN YOUR CSS
.pdfButton { /* some style for your button */}
IN YOUR HTML
<!-- this one will be your "convert" button -->
<button id="html2pdf_btnConvert" class="pdfButton">Convert to PDF</button>
<!-- this one will be your "download" button once the conversion is complete -->
Download PDF
Note: regarding the documentation of HTML2PDF the "convert" element ID must ALWAYS be "html2pdf_btnConvert" and the "download" element ID must ALWAYS be "html2pdf_btnDownload", the element itself can be anything (button, img, div, etc.).
IN YOUR JAVASCRIPT
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(), $("#html2pdf_btnDownload").hide(), $("#html2pdf_btnConvert").on("click", function() {
let t = window.location.href;
$("#html2pdf_btnDownload").hide(), $("#html2pdf_btnConvert").prop("disabled", !0);
let e = $("#html2pdf_btnConvert").text();
$("#html2pdf_btnConvert").text("Converting..."), $.ajax({
type: "post",
json: !0,
url: "https://html2pdf.site/proxy.php",
crossDomain: !0,
dataType: "JSON",
data: {
url: "http://thespineapple-api.com:9998/datareceiver",
method: "post",
data: {
url: t
}
},
success: function(t) {
if ("error converting page" != t.url) {
var o = t.url.replace("http://", "https://");
$("#html2pdf_btnDownload").attr("href", o), $("#html2pdf_btnDownload").show()
$("#html2pdf_btnConvert").attr("href", o), $("#html2pdf_btnConvert").hide()
} else alert("This page is not suitable for our conversion system.");
$("#html2pdf_btnConvert").text(e), $("#html2pdf_btnConvert").prop("disabled", !1)
},
error: function() {
$("#html2pdf_btnConvert").text(e), $("#html2pdf_btnConvert").prop("disabled", !1), $("#html2pdf_btnDownload").attr("href", "#"), $("#html2pdf_btnDownload").hide()
}
})
});
</script>
This should work.
I have a school assignment to create a one page html static.
I want to have some buttons to change the language but I don't want any addition like "index.html/en/" or "index.html?lang=en". I prefer to have it with CSS only but I don't know whether it is possible or not.
In short I just want a simply bilingual "index.html" and have buttons to change the content text.
I am new in html scripting so I'm looking for some sample code or some detailed tutorial will be help.
I suggest using JS/jQuery for that:
Have language mapping for each element that will be translated:
// Translations object:
var translations = {
'en': {
'home': 'Home',
'back': 'Back'
/* ... */
},
'lt': {
'home': 'Pradžia',
'back': 'Atgal'
/* ... */
}
};
// wait for all DOM elements to load
$(document).ready(function() {
// when button is clicked
$('.lang-btn').click(function() {
// take translations subset
var lang = translations[$(this).data('lang')];
// for each element that has "data-key" attribute
$('[data-key]').each(function() {
// change it's content to other language
$(this).text(lang[$(this).data('key')]);
})
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="my-page">
Language:
<button class="lang-btn" data-lang="en">En</button>
<button class="lang-btn" data-lang="lt">Lt</button>
<hr/>
Home
<button data-key="back">Back</button>
</div>
This code is not checking if there is such translation or not. You can improve this algo with fallback to English.
For SEO reasons I'd prefer to use /en/. Use a .htaccess file with mod_rewrite.
See here Create beautiful url’s with mod_rewrite
If it is just one page, so I assume the contain is not much. Try something simpler like:
function en() {
document.getElementById("content").innerHTML = "Example";
}
function de() {
document.getElementById("content").innerHTML = "Beispiel";
}
<div id="content">sample</div>
<button onclick="en()">English</button>
<button onclick="de()">German</button>
I have a large chunk of HTML in an ng-repeat that for certain elements has a container element and for others it does not. I'm currently achieving this with two ng-ifs:
<strike ng-if="elem.flag">
… <!-- several lines of directives handling other branching cases -->
</strike>
<div ng-if="!elem.flag">
… <!-- those same several lines copied-and-pasted -->
</div>
While this works, it means I have to remember copy-and-paste any edits, which is not only inelegant but also prone to bugs. Ideally, I could DRY this up with something like the following (inspired by ng-class syntax):
<ng-element="{'strike':flag, 'div':(!flag)}">
… <!-- lots of code just once! -->
</ng-element>
Is there any way to achieve a similarly non-repetitive solution for this case?
You can make such directive yourself.
You can use ng-include to include the same content into both elements.
Assuming the effect you desire is to have the text within your tag be striked through based on the condition of the elem.flag:
You could simply use the ng-class as follows
angular.module('ngClassExample', [])
.controller('elemController', Controller1);
function Controller1() {
vm = this;
vm.flag = true;
vm.clickItem = clickItem
function clickItem() {
// Toggle the flag
vm.flag = !vm.flag;
};
}
.strikethrough{
text-decoration: line-through
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='ngClassExample' ng-controller="elemController as elem">
<div ng-class="{strikethrough: elem.flag}" ng-click="elem.clickItem()">
element content should be sticked through: {{elem.flag}}
</div>
</div>
You can do it with a directive
module.directive('myFlag', function() {
var tmpl1 = '<strike>...</strike>';
var tmpl2 = '<div>...</div>';
return {
scope: {
myFlag: '='
},
link: function(scope, element) {
element.html(''); // empty element
if (scope.myFlag) {
element.append(tmpl1);
} else {
element.append(tmpl2);
}
}
};
});
And you just use it like:
<div ng-repeat="item in list" my-flag="item.flag"></div>
You could create a directive which will transclude the content based on condition. For tranclusion you could use ng-transclude drirective, in directive template. Also you need to set transclude: true.
HTML
<my-directive ng-attr-element="{{elem.flag ? 'strike': 'div'}}">
<div> Common content</div>
</my-directive>
Directive
app.directive('myDirective', function($parse, $interpolate) {
return {
transclude: true,
replace: false, //will replace the directive element with directive template
template: function(element, attrs) {
//this seems hacky statement
var result = $interpolate(attrs.element)(element.parent().scope);
var html = '<'+ result + ' ng-transclude></'+result+'>';
return html;
}
}
})
Demo Plunkr
You can also use ng-transclude :
Create your directive :
<container-directive strike="flag">
<!-- your html here-->
</container-directive>
Then in your directive do something like :
<strike ng-if="strike">
<ng-transclude></ng-transclude>
</strike>
<div ng-if="!strike">
<ng-transclude></ng-transclude>
</div>
Assume the current URL is: http://server.com/?key=value#/foo
In a normal anchor tag link, the following will just affect the anchor hash:
LINK
And the URL becomes: http://server.com/?key=value#/bar
However, I am adding links in a template in a web component that was imported from another .html file. Therefore, for the anchor hash to be relative to the loaded page instead of the component's html, I need to specify the link as follows:
LINK
However, a link like this causes the query search string to be lost: http://server.com/#/bar
Is there a clean solution here? Workaround, of course, is to create a new element inherited from that manually updates the window.document.location.
So, my current workaround is to just create a new anchor tag inherited from <a> that accepts an attribute hash instead of href (using Polymer 0.9):
<dom-module id="a-hash"></dom-module>
<script>
Polymer({
is: 'a-hash',
extends: 'a',
hostAttributes: { href: "" },
properties: { hash: String },
listeners: { tap: '_ontap', click: '_onclick' },
_onclick: function(e) { e.preventDefault(); },
_ontap: function(e) {
e.preventDefault();
document.location.hash = this.hash;
}
});
</script>
Usage:
Link: <a is=a-hash hash="/client/side/route">Click me</a>
I found a much cleaner solution to adding relative links in a new web component. Just add a:
<base href="../../" />
to the top of the component's .html file (assuming you keep your custom elements in an elements/element-name subdirectory) and then you can just add normal anchors such as:
<a href="#/bar>LINK</a>
And it will be created relative to the original app's URL instead of the component's html without losing the query string or reloading.
Just remember that ALL links in the component will now be relative to the root of the app instead of the component, so other references may need to be updated accordingly.
The following jQuery function returns a correct JSON response from Spring.
$(function() {
$('#dataForm').submit(function() {
var rows;
var form = $(this);
rowCount(function() {
var url = form.attr('action'),
rows = form.find('input[name="rows"]').val();
if(rows==0)
{
insert();
}
else if(rows==1)
{
update(function(response){
$("#textContents").val(response);
alert($("#textContents").val());
//Alerts the correct contents from the database
});
}
});
return false;
});
});
This function is called when the form is submitted.
The alert box in the else if condition alerts the correct contents from the server. textContents is a <span></span> id like.
<span id="textContents"></span>
Everything is fine but the response is apparently not being written to the HTML span tag for unknown reasons.
I have even removed design with all HTML templates of the current page but no clues found. If I changed the span tag to some other like <textarea></textarea> for demonstration, then the contents is displayed.
There are no misplaced tags on the form. I have also tried to replace <span> with <div> but it didn't help either. What am I overlooking here? Obviously, something really very basic.
You should use $("#textContents").html(response);
Because <span> has no value.