Evaluate HTML injected with v-html - javascript

How would one go about injecting raw HTML with Vue when that HTML contains Vue markup that needs to be evaluated?
For example, this could be used to render HTML when it comes from a variable:
<p v-html="markup"></p>
{
computed: {
markup() {
return 'Hello <em>World</em>!';
},
},
}
This will work nicely, but what if the markup computed property also included a custom Vue component?
return 'Hello <em>World</em>! My name is <my-name></my-name>!';
In this case <my-name></my-name> will not be evaluated unfortunately and just rendered as-is.
How do I go about having it "Vue-fied" and also how does scope work in that case?

I read the docs thoroughly, turns out it's not really possible "out-of-the-box" as it's somewhat of an anti-pattern.
The problem I am trying to fix however demands it as I want to be able to inject custom components in an arbitrary places in translated text.
I ended up using Custom Directives.
The only limitation here is that if you want to pass a property, say name from the context of where you're including the directive, you must reference it via $parent.
return 'Hello <em>World</em>! My name is <my-name :name="$parent.name"></my-name>!';
Usage will be:
<p v-markup="markup"></p>
When rendered, the above will be identical to doing this:
<p>
Hello <em>World</em>! My name is <my-name :name="name"></my-name>!
</p>
Checkout this Gist

Related

How can I append a {{link-to}} helper to my template dynamically in ember.js? (Functionality similiar to $compile in angular)

I am using ember 2.0 with ember-cli. All the other answers that come close to addressing this all use deprecated methods.
Currently I have some code that looks like this inside a component called object-form that I use in several places:
saveObject(newObject) {
newObject.save().then((object) => {
this.$('.success-message')
.html('Object "' + object.get('name') +
'" successfully added.')
}
}
This works fine, but now I'd have to also have something like:
$('.success-message').append('{{link-to object.name "object.show" object}}')
However, as expected, the text is added exactly as written instead of being added as an actual {{link-to}} helper.
In angular, which I'm more familiar with, you used something called the $compile service to dynamically add directives, the equivalent of ember components.
If link-to existed in angular, for example, the pseudocode would look like something like this:
var directive = $compile('<link-to ng-href="/object/{{object.id}} ng-model=object">{{object.name}}</link-to>')($scope);
$('.success-message').append(directive);
So is there an equivalent method of parsing the handlebars inside a string you want to append?
Instead of dynamically appending the {{link-to}} it makes much more sense to just put it under an if statement, which behind the scenes is really doing the same thing I intended.

Where to put a particular template helper

I have a pageTitle template variable that is in the header element () of each section of my site, and I would like it to be dynamic, rather than hard-coded, so site maintenance is easier.
Using Meteor (I'm currently reading Discover Meteor), would it be best to put it in a Session variable, and then return the value from a Template helper (i.e. as in the example in the Session chapter of Discover Meteor), or is there an Atmosphere package that would handle this better?
Also, if I do put it in a Template helper, I'm a little fuzzy about where is the appropriate file in which to put the helper. That is, if I have a .js file for (almost) every template (because I'm following along with the book), would I really need to put the same helper code that returns the pageTitle variable from the Session in each template's .js file?
It seems inefficient to have so many redundancies, but if I had a single instance of that Template.templateName.helper() code, I'm not sure where to put it.
Any help is much appreciated. Thanks!
I'd put it inside a global helper. You can use Template.registerHelper for that. I usually put these helpers inside client/helpers/global.js. I'm not sure why you would want to use a session variable for this. But here's a simple example of how this helper could look like:
client/helpers/global.js
Template.registerHelper('setPageTitle', function (newTitle) {
document.title = newTitle
})
Usage:
{{ setPageTitle 'Hi' }}
I'm also fairly new to Meteor, but it looks like you could use the Observe Collection from the docs - http://docs.meteor.com/#/full/observe and have a collection with your pageTitles in it.
So it would be something like
document.title = yourCursor.observe(function({
changed: function(id, field){return field.pageTitle}
})
As for location, anywhere on the client side should work.
Not sure if this is best way to do it, but hope it helps!

Knockout.js - cross-iframe observable binding

I would like to use knockout.js in cross iframe binding. Existence of iframes is dictated by actual app structure I am working on.
This is the scenario (simplified):
Main window: Knockout.js included. window.top.DATA is a global container for data, ex. var DATA = { username: ko.observable('John') };
Module iframe window: Knockout.js also included. View wants do display data stored in window.top.DATA object, using code: <div data-bind="text: window.top.DATA.username></div>
What is the result?
DIV's innerHTML contains ko.observable().toString() contents instead of John.
The cause
Knockout.js is unable to recognize an observable created in parent frame while performing binding, because knockout checks if variable is observable with ko.hasPrototype by comparing references. Since prototypes are different between parent and child frame ko instances, it is impossible to bind values.
Solutions
The simplest solution would be writing something like: window.ko = window.top.ko || setupKO() on the top of script file. Unfortunately, in this case binding like with: window.someLocalObj is referencing to window.top instead of window - we are not able to access local variables and also local templates using template binding.
Another way to fix the problem is simply allow knockout to recognize observables as it should, what would allow observables to track dependency, bind values and just work well. Unfortunately I expect it might be difficult thing to achieve. What options do you see here?
Thank you for all your responses.
Edit
Knockout.js version: 3.2.0.
One solution is to use a single ko instance to handle main window and its frames elements at the same time. iframe elements are acessible through window.frames[frame_index].document:
var DATA = { username: ko.observable('John') };
ko.applyBindings(DATA);
ko.applyBindings(DATA, window.frames[0].document.body);
Working example: Plunker

Bindings on self-written HandlebarsHelper?

i am trying add internationalization-abilities to my website.
I have written my own I18n.js which uses translation-objects out of the DS.store instead of its own (so there is a translation model and Ember preloads it on Application-start).
To get my translations into the Templates i have written this handlebars-helper
Ember.Handlebars.registerHelper('i18n', function(key) {
return Application.I18n.t(key);
});
so i could easily use it like:
{{i18n example_key}}
So far, everything works just perfect.
But the translations visible on screen are not bind to its translation-models.
If i change a translation in the administration-page which is places there too, i have to reload the page.
is it possible to add bindings between the helper and the translation model the helper have to display?
Thanks
Use registerBoundHelper instead of registerHelper. I don't even think registerHelper is part of the public API.
What does Application.I18n look like exactly? In order to make the {{i18n}} helper refresh its content when something changes, it needs to observe something that is observable.
Ember.Handlebars.helper is useful if you pass an object with observable properties (see http://emberjs.com/guides/templates/writing-helpers/#toc_dependencies). But if you only pass a key as a string, you'll have to set up the binding yourself in some way.

True custom attributes (e.g. Microdata) in React

The site I am developing makes use of Microdata (using schema.org). As we are shifting development over to use React to render our views I have hit a blocker where React will only render attributes in the HTML spec however Microdata specifies custom attributes such as itemscope.
As I'm relatively new to React and haven't had chance to fully understand the core just yet, my question is what would be the best way to extend the functionality of react.js to allow for defined custom attributes, e.g., Microdata?
Is there a way of extending the attributes/props parser or is it a job for a mixin which checks all passed props and modifies the DOM element directly?
(Hopefully we'll be able to put together a drop in extension for everyone to provide support for this when a solution is clear.)
You can also use "is" attribute. It will disable the attribute white-list of React and allow every attribute. But you have to write class instead of className and for instead of htmlFor if you use is.
<div is my-custom-attribute="here" class="instead-of-className"></div>
Update React 16 custom attributes are now possible
In react 16 custom attributes are now possible
React 16 custom attributes
It looks like these non-standard properties have been added to React
itemProp: MUST_USE_ATTRIBUTE, // Microdata: http://schema.org/docs/gs.html
itemScope: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, // Microdata: http://schema.org/docs/gs.html
itemType: MUST_USE_ATTRIBUTE, // Microdata: http://schema.org/docs/gs.html
Note that properties have capital letter in the middle:
<div itemProp="whatever..." itemScope itemType="http://schema.org/Offer">
will generate proper lowercase attributes as result.
You should be able to do it with componentDidMount:
...
componentDidMount: function() {
if (this.props.itemtype) {
this.getDOMNode().setAttribute('itemscope', true)
this.getDOMNode().setAttribute('itemtype', this.props.itemtype)
}
if (this.props.itemprop) {
this.getDOMNode().setAttribute('itemprop', this.props.itemprop)
}
}
...
The whole check for Microdata attributes can be wrapped into a mixin for convenient. The problem with this approach is that it won't work for built-in React component (components created by React.DOM). Update: Looking closer at React.DOM, I come up with this http://plnkr.co/edit/UjXSveVHdj8T3xnyhmKb?p=preview. Basically we wrap the built-in components in a custom component with our mixin. Since your components are built upon React 's built-in DOM components, this would work without you having to include the mixin in the components.
The real solution would be injecting a custom config instead of React's DefaultDOMPropertyConfig, however I can't find a way to do so in a drop-in manner (DOMProperty is hidden by the module system).
For those who's still looking for answers:
https://facebook.github.io/react/docs/tags-and-attributes.html
Example:
<div itemScope itemType="http://schema.org/Article"></div>
So far, the best method I've found is based off of some Amp interop code linked from a comment on react's bug tracker thread on the subject. I modified it slightly to work with a newer version of React (15.5.4) and TypeScript.
For regular ES6, you can just remove the type annotation for attributeName. Using require was needed in TS since DOMProperty isn't exposed in react's index.d.ts, but again import could be used in regular ES6.
// tslint:disable-next-line:no-var-requires
const DOMProperty = require("react-dom/lib/DOMProperty");
if (typeof DOMProperty.properties.zz === "undefined") {
DOMProperty.injection.injectDOMPropertyConfig({
Properties: { zz: DOMProperty.MUST_USE_ATTRIBUTE },
isCustomAttribute: (attributeName: string) => attributeName.startsWith("zz-")
});
}
Now you can use any attribute starting with zz-
<div zz-context="foo" />
Normally it'd be a bad idea to use internal parts of react like this, but I think it is better than any of the other methods. It works the same way as existing open-ended attributes like data- and the JSX is even type safe in TS. I believe the next major version of react is going to do away with the whitelist anyway, so hopefully changes won't be needed before we can remove this shim entirely.

Categories