Comment out a part of Vue template element - syntax

Sometimes it is needed to comment out some element attribute without having to remember it in order to restore it quickly after some tests.
Commenting out whole element is achievable with HTML commenting syntax
<div>
<!-- <h2>Hello</h2> -->
<span>hi</span>
</div>
However this won't work with a single attribute (causes rendering error)
<my-comp id="my_comp_1"
v-model="value"
<!-- :disabled="!isValid" -->
#click="handleClick">
</my-comp>
The best approach I could see and used before was to make a tag backup by copying whole element and settings v-if="false" for it (or comment out whole copied element) and continue to experiment with original one

I don't think you can put an HTML comment inside a component tag, for much the same reason you can't put comments inside an HTML element opening tag. It's not valid markup in either situation. I think the closest you could come would be to place the comment in the quotes:
:disabled="// !isValid"
Which would have the same effect as:
:disabled=""
Depending on whether your component can handle that value being missing, that might fit your needs.

Prefix the attribute value with data- or Wrap with data attribute.
<my-comp id="my_comp_1"
v-model="value"
data-:disabled="!isValid"
data-_click="handleClick"> # `#` could not be used
</my-comp>
or
<my-comp id="my_comp_1"
v-model="value"
data='
:disabled="!isValid"
#click="handleClick">
'>
</my-comp>
I'll with the attribute set to something like data-FIXME.

I got these solutions to work. I thought of solution 1.
Starting code:
<div
v-for="foo in foos"
:key="foo.id"
#click="foo.on = !foo.on /* JavaScript comment. */"
>
<label>
{{ foo.name }} {{foo.on}}
</label>
</div>
The Vue directive HTML attribute that needs to be disabled: #click="foo.on = !foo.on"
How the final div tag will run:
<div
v-for="foo in foos"
:key="foo.id"
>
Solutions 1 and 2 keep the disabled attribute inside its tag. I didn't find a good way to make a "raw string". To keep the attribute in the tag, the outer and inner quotes must be different.
sol. 1: I made a new v-bind attribute (:lang) to put the disabled attribute in.
:lang='en /* #click="foo.on = !foo.on" */'
Sol. 2: Pick a Vue directive. Put the attribute in.
v-for="foo in foos /* #click='foo.on = !foo.on' */"
Solutions 3 and 4 put the attribute outside the tag.
Sol. 3:
<div v-if="false">
#click="foo.on = !foo.on"
</div>
Sol. 4: <!-- #click="foo.on = !foo.on" -->

One way to remove/hide component attributes is to create a custom directive for it.
Let's say you create a directive called v-hide and put it in your component as:
<my-comp v-model="value" #click="handleClick" v-hide :disable='true'></my-comp>
And the output would be:
<my-comp v-model="value" #click="handleClick"></my-comp>
Here is a working example:
Vue.component ('my-component', {
template: `<p> A custom template </p>`
})
Vue.directive('hide', {
inserted: function (el) {
console.log('el before hide', el)
while(el.attributes.length > 0)
el.removeAttribute(el.attributes[0].name);
console.log('el after hide', el)
}
})
new Vue({
el: '#app',
data () {
return {
someValue: 'Hello'
}
}
})
<script src="https://unpkg.com/vue#2.5.3/dist/vue.js"></script>
<div id="app">
<my-component v-model='someValue' v-hide :disable='true'></my-component>
</div>

Related

SharepointFramework - how to set the actual webpart code as initial value in PropertyFieldCodeEditor

Hello i am using this custom property pane control called PropertyFieldCodeEditor. what i want is to display the actual webpart code as the initial value of the code editor, then after i click save, the changes will be reflected on the webpart..
this is the code of PropertyFieldCodeEditor
PropertyFieldCodeEditor('htmlCode', {
label: 'Edit Code',
panelTitle: 'Edit Code',
initialValue: "",
onPropertyChange: this.onPropertyPaneFieldChanged,
properties: this.properties,
disabled: false,
key: 'codeEditorFieldId',
language: PropertyFieldCodeEditorLanguages.HTML
})
i tried to put this.domElement on initialvalue but it only accept string, also i cand find a way to convert this.domelement to string..
also what should i put inside
protected onPropertyPaneFieldChanged(path: string, newValue: string) {}
For initialValue, you should be able to use this.domElement.innerHTML or this.domElement.outerHTML. Both are strings representing the contents of domElement (note, domElement is really just an HTMLElement).
outerHTML will include everything, including one extra div layer on the outside:
<div style="width: 100%;">
<div class="helloWorld_d3285de8">
...
</div>
</div>
innerHTML is only the inside contents of that div:
<div class="helloWorld_d3285de8">
...
</div>
You'll probably want innerHTML, since that's what's initially used in the render method.
Once you set the initialValue, you would have accomplished copying your web part code into the PropertyFieldCodeEditor. Now you would need to get the PropertyFieldCodeEditor contents (which is stored in your property htmlCode) assigned back into this.domElement.innerHTML.
Unfortunately, in onPropertyPaneFieldChanged, this points to the PropertyFieldCodeEditor, not to the web part class anymore. You may or may not be able to do it here - I didn't look too deeply into it.
The easiest solution I figured was, in render, to assign this.domElement.innerHTML like so:
public render(): void {
this.domElement.innerHTML = this.properties.htmlCode || `
<div class="${styles.helloWorld}">
...
</div>`;
}
This way, the web part will initially render whatever comes after the ||. But as soon as you save a change to the PropertyFieldCodeEditor, it will start rendering the htmlCode instead. This only works because initially htmlCode is undefined. (Note it won't work exactly like this if you assign something truthy to it via your web part's preconfiguredEntries - you would have to write some further checks. The principle is the same, though.)

Is it possible to use vue-specific directives in localized text?

I just started using vue-i18n and tried to use the v-on-directive (shorthand: #) in my language specific text.
What i tried to do:
// locale definition
let locale = {
en: {
withEventListener: 'Some HTML with <a #click="onClickHandler">event handling</a>'
}
}
and the vue template:
<!-- vue template be like -->
<p v-html="$t('withEventListener')" />
This doesn't throw an error but unfortunately it does not get evaluated by vue-js either. This would result to Plain-HTML like:
<p>
Some HTML with <a #click="onClickHandler">event handling</a>
</p>
So my question is if there is a way to make Vue 'evaluate' the text and thus 'translate' the directives within the text.
You can use Vue.compile to do something like this if you are including the standalone build script. I'm not familiar with vue-i18n, but this might put you on the right path.
Note that I had withEventListener to wrap it in a div because of the rules around templates.
let locale = {
en: {
withEventListener: '<div>Some HTML with <a #click="onClickHandler">event handling</a></div>'
}
}
const res = Vue.compile(Vue.t("withEventListener"));
Vue.component("internationalized", {
methods:{
onClickHandler(){
alert("clicked")
}
},
render: res.render,
staticRenderFns: res.staticRenderFns
})
new Vue({
el:"#app"
})
With the template
<div id="app">
<internationalized></internationalized>
</div>
Working example.

using foundation 5 joyride with tabs

Is there a way to switch tabs with foundation 5 Joyride?
I have foundation tabs on the page and want Joyride to point elements on different tabs.
Like mentioned in the comment from Steven, you could use the callback either in the pre or post step callback function you activate the tab you need.
post_step_callback : function (){}, // A method to call after each step
pre_step_callback : function (){} // A method to call before each step
Hope this helps...
Here's what worked for me. I looked around and couldn't find anything useful, so wrote this. The hardest part was figuring out how to get the id of the target anchor. This was found buried in the 'this' object available to the callback.
$(this.$target)[0].id;
The 'content' class is used by foundation to identify the content to display when a tab is clicked. So traversing up the .parents tree finding the enclosing elements gives you the content tab(s) holding your link. And then of course you have to add an id to the <a> element of the tab you want to click. If you name it the same as your content div, with '-a' appended, you should be good to go.
html:
<dl class="tabs radius" data-tab id="my_tabs">
<dd class="active">Tab 1</dd>
<dd class="active">Tab 2</dd>
</dl>
<div class="tabs-content">
<div class="content" id="tab1">
<article id="joyride_stop1">...</article>
</div>
<div class="content" id="tab2">
<article id="joyride_stop2">...</article>
</div>
</div>
js:
$(document).ready(function() {
$(document).foundation('joyride', 'start', {
pre_step_callback: function(i, tip) {
var target = $(this.$target)[0].id;
if($('#' + target).parents('.content').length > 0) {
$('#' + target).parents('.content').each(function() {
var id = $(this).attr('id');
if($('#' + id).is(':visible') == false) {
$('#' + id + '-a').click();
}
});
}
}
});
});
This will work on any page, whether it contains tabs or not, so it can be universally included across a site.

How to change the default delimiter of Handlebars.js?

I need to use handlebars.js and I also use Blade template engine from Laravel (PHP Framework). The tags {{}} conflict with blade's placeholders that are exactly the same.
How can I change those {{var}} to something like <% var %> ?
Although it may be true that you can't configure Handlebars' expression delimiters, that's not the only way to resolve the conflict between Handlebars and Blade. Blade provides syntax that allows you to avoid the conflict by designating what should be passed on to Handlebars. (This is fitting, since Blade created the conflict to begin with, and it's necessary, since Blade is processing the template before Handlebars ever sees it.) Just prepend # before the curly braces that you want Blade to ignore and pass as-is to Handlebars. Here's a very short snippet of a much larger example:
...
<link
rel="stylesheet"
type="text/css"
href="{{ asset("css/bootstrap.theme.3.0.0.css") }}"
/>
<title>Laravel 4 Chat</title>
</head>
<body>
<script type="text/x-handlebars">
#{{outlet}}
</script>
...
{{outlet}} will be passed to Handlebars, but {{ asset("css/bootstrap.theme.3.0.0.css") }} will be handled by Blade.
I created handlebars-delimiters on GitHub / npm to make it easy to use custom delims with Handlebars.
var Handlebars = require('handlebars');
var useDelims = require('handlebars-delimiters');
var a = Handlebars.compile('{{ name }}<%= name %>')({name: 'Jon'});
console.log(a);
//=> 'Jon<%= name %>'
// Pass your instance of Handlebars and define custom delimiters
useDelims(Handlebars, ['<%=', '%>']);
var b = Handlebars.compile('{{ name }}<%= name %>')({name: 'Jon'});
console.log(b);
//=> '{{ name }}Jon'
The idea for the compile function came from https://stackoverflow.com/a/19181804/1267639
Suggestions for improvement or pull requests are welcome!
Instead of changing the delimiters you can create files with your handlebars templates in without the .blade extension. Just include these files in your blade template. E.g
Blade Template File - template.blade.php
#extends('master.template')
#section('content')
#include('handlebars-templates/some-template-name')
#stop
some-template-name File - some-template-name.php
<script type="text/x-handlebars" data-template-name="content">
<div>
<label>Name:</label>
{{input type="text" value=name placeholder="Enter your name"}}
</div>
<div class="text">
<h1>My name is {{name}} and I want to learn Ember!</h1>
</div>
</script>
I've made this function.
Hope it can be useful for someone..
/**
* change the delimiter tags of Handlebars
* #author Francesco Delacqua
* #param string start a single character for the starting delimiter tag
* #param string end a single character for the ending delimiter tag
*/
Handlebars.setDelimiter = function(start,end){
//save a reference to the original compile function
if(!Handlebars.original_compile) Handlebars.original_compile = Handlebars.compile;
Handlebars.compile = function(source){
var s = "\\"+start,
e = "\\"+end,
RE = new RegExp('('+s+'{2,3})(.*?)('+e+'{2,3})','ig');
replacedSource = source.replace(RE,function(match, startTags, text, endTags, offset, string){
var startRE = new RegExp(s,'ig'), endRE = new RegExp(e,'ig');
startTags = startTags.replace(startRE,'\{');
endTags = endTags.replace(endRE,'\}');
return startTags+text+endTags;
});
return Handlebars.original_compile(replacedSource);
};
};
//EXAMPLE to change the delimiters to [:
Handlebars.setDelimiter('[',']');
This is not possible with "standard" Handlebars. https://github.com/wycats/handlebars.js/issues/227
"If you need to display a string that is wrapped in curly braces, you may escape the Blade behavior by prefixing your text with an # symbol"
#{{varname}}
Hope it helps!
I used and updated the source code of user1875109 and now it works with Handlebars v3.0.3:
/**
* change the delimiter tags of Handlebars
* #author Francesco Delacqua
* #param string start a single character for the starting delimiter tag
* #param string end a single character for the ending delimiter tag
*/
Handlebars.setDelimiter = function(start,end){
//save a reference to the original compile function
if(!Handlebars.original_compile) Handlebars.original_compile = Handlebars.compile;
Handlebars.compile = function(source){
var s = "\\"+start,
e = "\\"+end,
RE = new RegExp('('+s+')(.*?)('+e+')','ig');
replacedSource = source.replace(RE,function(match, startTags, text, endTags, offset, string){
var startRE = new RegExp(s,'ig'), endRE = new RegExp(e,'ig');
startTags = startTags.replace(startRE,'\{');
endTags = endTags.replace(endRE,'\}');
return startTags+text+endTags;
});
return Handlebars.original_compile(replacedSource);
};
};
//EXAMPLE to change the delimiters to [:
Handlebars.setDelimiter('[',']');
There is an option to tell template engine for not parsing certain part of code and treat it as plain text.
Find the following ways to do it, hope it helps somebody.
In blade template engine (laravel) you can use #verbatim Directive. So that you dont have to add # to every variable.
Example :
#verbatim
<div class="container">
Hello, {{ name }}.
</div>
#endverbatim
Similarly for twig template engine (symfony) you can block the whole code by using
{% verbatim %}
<div>
My name is {{name}}. I am a {{occupation}}.
</div>
{% endverbatim %}

Separating template logic from Backbone.View

I just started learning Backbone.js, and have been working on (what else) a simple to-do application. In this app, I want to display my to-do items inside of <ul id="unfinished-taks"></ul> with each task as a <li> element. So far, so simple.
According to the tutorials I have read, I should create a View with the following:
// todo.js
window.TodoView = Backbone.View.extend({
tagName: 'li',
className: 'task',
// etc...
});
This works fine, but it seems like bad practice to define the HTML markup structure of my to-do item inside of my Javascript code. I'd much rather define the markup entirely in a template:
// todo.js
window.TodoView = Backbone.View.extend({
template: _.template($("#template-task").html()),
// etc...
});
<!-- todo.html -->
<script type="text/template" id="template-task">
<li class="task <%= done ? 'done' : 'notdone' %>"><%= text %></li>
</script>
However, if I do it that way Backbone.js defaults to using tagName: 'div' and wraps all my to-do items in useless <div> tags. Is there a way to have the HTMl markup entirely contained within my template without adding unsemantic <div> tags around every view element?
If you are only planning to render the view once, you can set the el property of the view manually in .initialize():
// todo.js
window.TodoView = Backbone.View.extend({
template: _.template($("#template-task").html()),
initialize: function() {
this.el = $(this.template(this.model.toJSON())).get(0);
},
// etc
});
There are some caveats here, though:
Backbone expects the el property to be a single element. I'm not sure what will happen if your template has multiple elements at the root, but it probably won't be what you expect.
Re-rendering is difficult here, because re-rendering the template gives you a whole new DOM element, and you can't use $(this.el).html() to update the existing element. So you have to somehow stick the new element into the spot of the old element, which isn't easy, and probably involves logic you don't want in .render().
These aren't necessarily show-stoppers if your .render() function doesn't need to use the template again (e.g. maybe you change the class and the text manually, with jQuery), or if you don't need to re-render. But it's going to be a pain if you're expecting to use Backbone's standard "re-render the template" approach for updating the view when the model changes.

Resources