Mithril template converter usage - javascript

I am trying to accomplish the exact thing that happens here: https://arthurclemens.github.io/mithril-template-converter/index.html
Basically, I am letting users insert a HTML string into a textbox, and I want to convert that string into an object that I can use with the m helper method.
For example I must convert this:
<div class="foo"><p>bar</p></div>
Into something like this:
m("div", {"class":"foo"},
m("p",
"bar"
)
)
Ideally, I am looking for some type of workflow such as this:
function myComponent() {
let myHTML = "<div class="foo"><p>bar</p></div>";
return(m(convertHTML(myHTML));
}
I have looked into various hypertext/vdom transpilers, but none of them create a tree in the exact format that Mithril expects, so I need very similar functionality of the above listed website for this to work.
I'm guessing this simply isn't possible because of the aspect of nested function calls to m.

I was able to use the templates on the fly with eval:
import { templateBuilder } from "mithril-template-builder"
const source = '<div class="foo"><p>bar</p></div>';
const template = templateBuilder({
source
});
function myComponent(props) {
let object = eval(template);
return(object);
}

Related

Use [hash:base64:5] in JavaScript / TypeScript file

I am using CSS Modules in an Angular Project with Webpack.
I had already transformed the class names in .css and .scss files with postcss-modules.
Then with posthtml-css-modules I had changed the values on the class property in html elements for his hash value defined by postcss-modules.
I can say that everything is working fine 💪.
Now, I have a new challenge to resolve.
Angular, has a feature that allows you to change dynamically the value of class in a html element depending on a condition:
https://angular.io/api/common/NgClass
For Example, I can do:
<div [className]="myVar ? 'myClass1' : 'myClass2' " >
If myVar = true, the html element will be:
<div class="myClass1" >
And if myVar = false, the html element will be:
<div class="myClass2" >
Like I do not know what is going to be the value of myVar during compilation time (because the value of myVar depends on user actions) I am not able to set the value for <div css-module="myClass1" > or <div css-module="myClass2" > in order to hash the class names of myClass1 or myClass2.
BUT (Here comes my solution)...
If I can invoke the same function that does [hash:base64:5] (https://github.com/css-modules/postcss-modules#generating-scoped-names)
I can create a function that receives a string as parameter and return the class name as a hash.
It would be something like this:
<div [className]="setMyClassNameInHash(myVar)">
Then in javascript:
function setMyClassNameInHash(condition) {
return condition ? doHash64('myClass1') : doHash64('myClass1');
}
doHash64() would be the function that takes a string and returns the hash using [hash:base64:5].
In conclusion, my question is:
¿How I can invoke the same function that does [hash:base64:5] in a javascript file?
Thanks!!!!
You'll need to integrate a templating step into your build process. The plugin exports the class names and their mapped names to a json file, so you can look up the hashed class name from the original.
Edit: Since the built in templating only works for a single class name and doesn't appear to support replacing class names in things like angular attributes, you could do the templating yourself using a templating library like lodash. If you're already using grunt or gulp, I'd recommend using their template tasks instead of this manual way because they do a lot of things for you, like supporting multiple files.
In your html, you would use lodash delimiters to get the hashed class name like:
<div [className]="myVar
? '<%= getHashedClass('myClass1') %>'
: '<%= getHashedClass('myClass2') %>' " >
Your node build script might look like this:
var fs = require('fs');
postcss([
require("postcss-modules")({
// Callback to get hashed class names
getJSON: function(cssFileName, classNames, outputFileName) {
var filePath = '/path/to/outputDir/file.html';
// Function to lookup hashed class names
var getHashedClass = function(unhashedClass) {
return classNames[unhashedClass];
}
// Read your html file as a string
var html = fs.readFileSync(path),
// Use lodash to template it, passing the class lookup function
var compiled = _.template(html);
var templated = compiled({
getHashedClass: getHashedClass
});
// Write the templated html
fs.writeFileSync(path, templated);
}
})
]);

Inject javascript into object function

I have an object function like this:
var batman = function () {
this.constructor.prototype.go = function(params){
......
}
}
When calling batman.go() I'm passing an object in with a few keys such as:
{
a:1,
b:2,
action:function(){..code to scan and inject into...}
}
My question is, how do I in batman.go() function, scan through the input param function code of 'action' and if a match is found, inject code into a certain place.
The code I am looking for is:
history.pushState({name:'homepage'},null,uri);
I want to inject so it looks like this:
history.pushState({id:an_id_variable,name:'homepage'},null,uri);
What is being inserted is:
id:an_id_variable
Use function.toString() to get the source of params.action, String.replace() to find and replace occurences of the snippet in question, and then the Function() constructor to dynamically create a new function with the amended source code:
var batman = function () {
this.constructor.prototype.go = function(params){
...
let newAction = new Function(params.action.toString().replace(
/history\.pushState\({name:'homepage'},null,uri\);/g,
`history.pushState({id:${an_id_variable},name:'homepage'},null,uri);`
));
//use newAction() however you like
}
}
It should be noted that if any end user has any amount of control over the content that can go in params.action, this would allow for completely arbitrary code injection by that user - but as pointed out in comments, arbitrary code can already be run on browsers via developer console. Just be aware of the security implications of a solution like this.
Also note that using the Function constructor binds the function to the global scope and it will lose any this context. You can bind it to an appropriate this context with function.bind() like this:
newAction = newAction.bind(params.bindTarget);
Then, when newAction executes, whatever params.bindTarget references will be this.

Is i possible to call t() instead of I18n.t() in javascript?

I am using i18n-js to load locales in to my js app. When I use the translate function in my view I would like to be able to just say t('Hello') instead of I18n.t('Hello'). How can I get such an alias to work?
import I18n from 'i18n';
const Hello = () => {
return (
<div>{I18n.t("hello", {name: "John"})}</div>
)
}
You could assign the t function to window:
window.t = I18n.t;
Then you can just use t('Hello'). However, as #Thilo mentioned, this may result in some complications.
A better alternative is to write a small wrapper:
function t(key){
return I18n.t(key);
}
The best option, suggested by #deceze, would be this single line:
var t = I18n.t.bind(I18n);
Single-line, and should work regardless of the scope it's called in.

Lightswitch HTML global JS file to pass variable

I know how this works in C#, however not so much in javascript so I am hoping it is similar.
With Javascript can I create say a master.js, with a variable (var defaultValue = "1234"), which I can reference in all other javascript files associated with the project?
so in terms of Lightswitch HTML, each screen has the ability to have a js file, and on the screen I want to be able to retrieve this defaultValue.
Can this be done?
If yes, how can I get this value onto the current screen?
so far I have created a main.js file, added this function:
function getDefaultValue(value) {
var value = "1234";
return value;
}
and declared the js file in the default.htm file:
<script type="text/javascript" src="Scripts/main.js"></script>
I know this is how i am using other JavaScript files like blob.js, lsWires.js etc...
using this method in by screen.js doesn't work so one of these stages is causing an error...
window.alert(main.getDefaultValue(value));
ideally i would like to use this defaultvalue for setting a value, i.e. var test = main.getDefaultValue(value)
This is certainly possible, and the script declaration you've used in your default.htm appears correct.
However, as the approach you've described creates a global getDefaultValue function (added to the global window object context) you wouldn't specify a main 'namespace' prefix like you would in c#.
Instead, rather than calling the function using main.getDefaultValue, you'd use the the following approach within your LightSwitch screens:
myapp.BrowseProducts.created = function (screen) {
window.alert(window.getDefaultValue("123")); // This will display 1234
// As window is a global object, its window prefix can be omitted e.g.
alert(getDefaultValue("123")); // This will display 1234
};
Or, if you want to define a global defaultValue variable in your main.js (probably the approach you're looking to implement) you would have the following code in your main.js file:
var defaultValue = "5678";
Then you'd access it as follows in your LightSwitch screens:
myapp.BrowseProducts.created = function (screen) {
alert(defaultValue); // This will display 5678
defaultValue = "Hello World";
alert(defaultValue); // This will now display Hello World
};
Also, if you'd like to organise your functions/properties in a main 'namespace', you could use the following type of approach in your main.js file: -
var main = (function (ns) {
ns.getDefaultValue = function (value) {
var value = "1234";
return value;
};
ns.defaultValue = "5678";
return ns;
})(main || {});
These would then be called as follows in your LightSwitch screens: -
myapp.BrowseProducts.created = function (screen) {
alert(main.getDefaultValue("123")); // This will display 1234
alert(main.defaultValue); // This will display 5678
main.defaultValue = "Hello World";
alert(main.defaultValue); // This will now display Hello World
};
This type of approach is covered in the following posts: -
How to span javascript namespace across multiple files?
JavaScript Module Pattern: In-Depth

How best to overwrite a Javascript object method

I'm using a framework that allows the include of JS files. At the top of my JS file I have something like:
<import resource="classpath:/templates/webscripts/org/mycompany/projects/library/utils.lib.js">
I want to override a fairly small method that is defined in the very large utils.lib.js file. Rather than make the change directly in utils.lib.js, a file that's part of the framework, I want to overwrite just one method. The utils.lib.js file has something that looks like:
var Evaluator =
{
/**
* Data evaluator
*/
getData: function Evaluator_getData(input)
{
var ans;
return ans;
},
...
}
I want to change just what the method getData does. Sorry for the basic question, but after importing the file which copies the JS contents into the top of my JS file, can I just do something like:
Evaluator.getData = function Mine_getData(input)
{
...
};
Yes, you can just reassign that method to your own function as you have proposed with:
Evaluator.getData = function Mine_getData(input)
{
...
};
This will successfully change what happens when the .getData(input) property is called.
Yes you can.
However Evaluator is not a proper 'class'. You can't write var x = new Evaluator();
So you are not overriding, but just changing the variable getData. That's why we say that in JavaScript, functions are first-class citizen, treated like any variable.

Categories