mithriljs / mithril.js Goto Github PK
View Code? Open in Web Editor NEWA JavaScript Framework for Building Brilliant Applications
Home Page: https://mithril.js.org
License: MIT License
A JavaScript Framework for Building Brilliant Applications
Home Page: https://mithril.js.org
License: MIT License
While trying out Mithril I experienced an issue when using mode abstraction in <a>
tags. Upon a redraw, the content of the anchor element simply disappears. A reproduction of the issue can be found here: http://jsfiddle.net/qHxZj/7/
I have noticed the issue renders differently in different browsers. In chrome nothing seems to be displayed at all, while in Firefox the content first renders, but disappears on the delayed redraw.
I have the config:
m.route(document.getElementById("nodesList"), "/", {
"/": MKI.Pages.NodesList,
"/nodes/:nodeID": MKI.Pages.NodeDetails
});
m.route.mode = "hash";
When I try to deep link to a page (from outside the app) it initializes to the defaultRoute instead of rendering the deep linked view. Once I am in the app I can switch to the deep link.
Remove new
keyword in:
currentController = new module.controller
I want to pass objects directly, thus having an option to save a reference to them. As it is now, I can't access the actual controller object.
I did this https://gist.github.com/gunnarahlberg/9844657#file-gistfile1-js-L36 - a Todo list with a sort function thrown in.
When the elements is checked, two check boxes get the event.
It is possible to render a component without a DOM, in node.js for example?
React has the function React.renderComponentToString for example to allow that.
Quite useful to pre-render the page on the server and for tests in node.js.
Click on the button a few times and you should see "multiplies" gets cloned. This isn't an issue if you change null to "", though.
It also only seems to happen if the if-ed elements have the same tag, if they're different tags, it doesn't happen.
This will take some time to explain.
I'm using an external library: it's a drag-drop uploader. I have multiple tabs, and on each tabview, I have an instance of a drag-drop uploader. These instances are separated into tabs because on one tab I need to upload images of a disaster site, and on another I need to upload images of scanned documents.
these individual instances of the drag-drop uploader, i give unique ids.
m("div#uniqueIdOne, {config: ctrl.initFunction})
...
m("div#uniqueIdTwo, {config:ctrl.initFunctionTwo})
Each element containing the instance should have their own initialization, so they have their own initialization functions assigned to config
. To make sure that I don't unnecessarily reinitialize when the instance already exists, I take isInit
as a second argument to my initFunction
.
...
this.initFunction(elem, isInit){
if(!isInit){
new ExternalLibraryInstance(elem, /*other initialization options*/); // or something like that
}
}
I do this for all the instances, each with different initialization options. When I test, however, they always adopt the options of the first instance. Turns out, this is because isInit
is set as true, even if I'm technically supposed to be initializing these things each time I switch tabs.
It seems like mithril, when diffing the elements, is only diffing on the tag, and not on the id or className. i.e. When I switch...
m("div#uniqueIdOne, {config: ctrl.initFunction})
...
m("div#uniqueIdTwo, {config:ctrl.initFunctionTwo})
to...
m("div#uniqueIdOne, {config: ctrl.initFunction})
...
m("section#uniqueIdTwo, {config:ctrl.initFunctionTwo})
it suddenly works as expected.
You can see everything in the jsfiddle below. Please open the javascript console, and click between all the buttons to see the behavior of isInit
.
http://jsfiddle.net/6n3rH/12/
For example, when going
var debugObject = m.prop({
property: "that I want to test",
or: "in general want to see objects splayed out on the markup"
});
m("div", [
debugObject().property, // will print properly
debugObject() // will throw Uncaught TypeError: Object.keys called on non-object
])
This is especially useful when debugging, and I do this a lot with angular projects. If you think this is somehow wrong, I guess that's fine, but it may be useful to throw a specific error message about this because I suspect a lot of people coming from angular will do this.
Since the cdns aren't working, I'll just go directly into code here, rather than using jsfiddle.
works:
app = {
controller: function(){
this.someList = m.prop([1, 2, 3, 4, 5, 6]);
this.isVisible = m.prop(true);
this.toggleVisible = function(){
this.isVisible(!this.isVisible());
}
},
view: function(ctrl){
return m("div", [
m("button",
{onclick: ctrl.toggleVisible.bind(ctrl)},
"toggle!"),
m("div", [
ctrl.isVisible() ? m("div", "on") : m("div", "off"),
m("div", "all the other shit")
])
]);
}
}
m.module(document, app);
Funky cases:
...
ctrl.isVisible() ? m("div", "on") : "",
...
ctrl.isVisible() ? m("div", "on") : m("span", "off"),
...
ctrl.isVisible() ? m("div", "on") : m("button", {onclick: /*something*/},"off"),
When they're generated, they're appended to the bottom of the list, like the previous bugs.
As far as I've tested, conditional ordering really only works properly on divs.
If I'm just creating a small app embedded in a website (i.e. a "Chat with us" box in a corner or something), I don't want it to be represented in URL, but I still need the functionality of routes.
Would a transparent m.route.mode
be possible? I'd event say it should be the default :)
Or did I missed something in docs?
You can just fork their code and create a pull request. Probably best done by the author, but I can do it if you wish.
Details here: https://github.com/cdnjs/cdnjs
When going from a route that has params to one that doesn't, for example
Anyone have experience with a good mailing list provider? Google groups seems like a popular one, but I hear it's terrible.
Looking to find a channel to get more discussions going, and to be able to let people know when new releases are available.
When i use ternary operator for conditional rendering, I get wrong order of elements in result render.
Only occurs when use null
, false
or empty string placeholder to skip element.
Workaround is to use real placeholder.
Good morning,
It seems that the last stable version breaks the getting started application. In Firefox, I got :
TypeError: index is undefined mithril.js:79
In Chromium :
Uncaught TypeError: Cannot read property 'undefined' of undefined mithril.js:79
Uncaught TypeError: Cannot call method 'appendChild' of null
in line 73.
Console.logging parent shows that it starts out null, then starts displaying all the other elements. I'm unable to reproduce this with jsfiddle, unfortunately.
This problem is specific to 0.1.3. I've tried with 0.1.2, and my project works fine.
Mithril looks nice and compact, great work :)
I would like to have a base layout template and let other smaller page templates inherit/extend the base template. The base template declares a header and footer. How is this achieved in Mithril?
Thanks
when rendering a big list of elements, it may be useful to cache the html and not to compute it again if the model has not changed.
An example for this with the quick start tutorial:
todo.controller = function() {
[...]
for (var i = 0 ; i < 10000 ; i ++) {
var descr = m.prop("TODO " + i);
this.add(descr);
}
};
when clicking on a selectbox, the rendering lags because the whole list is rendered again. It would be better to render only the changed element.
I am comparing with React that I know better:
React provides shouldComponentUpdate for this purpose.
How can we use the template system to represent this HTML structure:
<p>Click <a># time</a></p>
The part bugging me is the Click
, considering I can't overwrite the p
content. The only way I found is this one:
m('p', m.trust('Click <a>' + ctrl.times + ' time(s)</a>'))
Thanks
This is the behaviour I'm seeing rendering lists in 0.1.6 (a pared down test case based on the behaviour of the Add Another button here):
<!DOCTYPE html>
<div id="app"></div>
<script src="mithril.min.js"></script>
<script>
m.module(document.getElementById('app'), {
controller: function() {
this.howMany = m.prop(2)
this.addAnother = function addAnother() {
this.howMany(this.howMany() + 1)
}.bind(this)
}
, view: function(ctrl) {
var things = []
for (var i = 0, l = ctrl.howMany(); i < l; i++) {
things.push(m('div', [i + 1]))
}
return m('div', [
things
, m('button', {type: 'button', onclick: ctrl.addAnother}, ['Add Another'])
])
}
})
</script>
Initial render:
1
[Add Another]
2
After a few cicks of the button:
1
[Add Another]
3
4
5
2
Hi, I am looking for a light weight MVC framework for my customer embedded page, which use IE as a wrapper. Is mithril only support modern browser? I checkout the website, but i cannot find any related info. Can anybody show me the compatibility of mithril. Thanks in advance.
The line
else if (attrName in node) node[attrName] = dataAttr
fails to correctly set some svg attributes.
Would love to see this on bower or npm for quick installs :)
For reference:
https://groups.google.com/forum/#!topic/mithriljs/6L2fU4qdhgg
Currently it's not possible to have more than one app in a single page due to the way m.redraw is designed.
Fixing this may require a breaking change in the API
Getting 415 (Unsupported Media Type) error on POST
Service is expecting application/json, but there seems to be no easy way to set it.
Very cool project! :)
How would you update the view realtime on keypresses? The view updates the input value on changes. This seems to move the caret position to the end of the field. The cachedAttr for the input value becomes invalid with respect to the dom state.
The code for my experiment:
var app = {}
app.controller = function() {
this.title = m.prop("title");
}
app.view = function(ctrl) {
return m("body", [
m("h1", "Title: " + ctrl.title()),
m("input", {
onkeyup: m.withAttr("value", ctrl.title),
value: ctrl.title()
}),
]);
}
m.module(document, app);
Using the todo app again,
// snip
todo.view = function(ctrl) {
console.log('rendering...');
return m("form", [
m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()}),
m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add"),
m("table", [
ctrl.list.map(function(task, index) {
return m("tr", [
m("td", [
m("input[type=checkbox]", {onclick: m.withAttr("checked", task.done), checked: task.done()})
]),
m("td", {style: {textDecoration: task.done() ? "line-through" : "none"}}, task.description()),
])
})
])
]);
};
m.module(document.getElementById('bar'), todo);
// In the HTML
<div id="bar"></div>
Looking at the console output, I see that "'rendering...'" is logged 3 times, can you tell me what I am doing wrong?
Hi,
Does it work with SVG?
Thanks
I noticed that binding more than one module to a page does not work. Using the demos as an example, I tried this:
// initialize the todo app from the demo application
m.module(document, todo);
//controller class
var dashboardController = function() {
this.greeting = "Hello";
};
//view class
var dashboardView = function(ctrl) {
return m("h1", ctrl.greeting);
};
//initialize an anonymous module
m.module('#foo', {controller: dashboardController, view: dashboardView});
What would be the correct way to do this? Thanks
m.route(document.getElementById("main"), {/*routing stuff*/})
and
m.route(document.body, {/*routing stuff*/})
both wipe out any existing html in index.html, and are therefore effectively identical. It would be nice if I could control which parts to route and which parts to m.module (a persistent navbar with a session manager, for example) to keep things DRY.
However, if this is non-idiomatic, and their identical behaviors are by design, then perhaps the rootElement argument could be eliminated altogether?
Example: http://jsfiddle.net/qHxZj/
Select all text in the input and press delete. Nothing happens. Also copying text and moving cursor within the input with arrow keys doesn't work.
These issues go away when you comment out the onkeydown
binding, even when its handler isn't doing event.preventDefault()
or anything of the sorts.
Just to be sure I rewrote it in pure JS, and the issues mentioned above are not present there: http://jsfiddle.net/u8mzS/
The only issue consistent between mithril and pure JS examples is when you position the cursor into the middle of a string and start typing. After first letter your cursor is teleported to the end of a string, making edits super annoying.
It's obvious that this is due to input.value = newValue
, but isn't there a way how to do this only when necessary?
For example, if input, select or other interactive element values changed, reflect this in virtual DOM so next diff won't complain about differences. Is something like that doable? Or even a good idea? :)
Here is the js fiddle
http://jsfiddle.net/UBD42/3/
I suspect it has something to do with arr.push(elem) or something like that, because the most recently "unhidden" element shows up as the last element.
This behavior was not present in 0.1.2, and is present in both 0.1.3 and 0.1.4 (next).
I'm trying to finalize a TodoMVC prototype to bring more visibility to Mithril.
https://github.com/jpmonette/todomvc-mithril
Everything seems to be working, but I'm still trying to figure out how I can use the routes to modify controller attributes. I need three routes: /
, /active
, /completed
. When I load the page with one of them, I need to filter todos by their completion state (completed()
).
I tried this route setup, but I get uncaught errors:
m.route(document.getElementById('todoapp'), '/', {
'/:filter': app
})
Any suggestion how to support that?
Thanks for the support (and for this great JS MVC Framework!)
If for example I do an ajax request (POST) for http://127.0.0.1:1234/something
it'll resolve as http://127.0.0.1undefined/something. It's probably something to do with url parsing for routing with http://domain.com/something:param, because the workaround is hilarious.
return m.request({
method: "PUT",
url: "http://127.0.0.1:port/databaseName",
data: {test: "hi", port: ":5984"} // port 5984 is the port that couchdb uses.
}).then(function(data){
// lalala
})
Follow up to #1
So mithril.min.map doesn't work.
When I make prototypes for webapps, I like making sample data generators in js (rather than in the database, because why even use databases in prototypes).
My data involves a list of users, and a list of government projects that they request. I generate the list of users first by pulling data from randomuser.me via m.request(), and then I make them generate the list of projects. These are methods within their own controllers (and their own modules), which I instantiate in another controller.
Because routing necessarily requires that I use different modules for each route, I'm at a loss at how to share this data between different modules. The only way I can think of is assigning it to a global variable
var data = {}
data.users = new User.controller();
data.projects = new Project.controller();
data.users.genUsers(5).then(function(){data.projects.genProjects(50)})
// genUsers and genProjects create the lists at data.users.list, and data.projects.list
And I'm trying to avoid this because global variables are yuck. Maybe my mindset is wrong? I'm too used to angular services.
You provide a basise deferred implementation in mithril.
However, it is quite erm... bad.
Not only it uses the old deferred API in favor of the ES6 promise contructor - it doesn't provide type safety at all which is a major part of promises.
(correct behavior btw http://jsfiddle.net/yN6wF/ works in Chrome and browsers that support promises, alternatively you can include a promise library)
Please consider reading some more about promises and perhaps improving your implementation. Please reassure me this is not just cargo culting.
For example, 404, 500, etc...
Is there a list of available hooks/ events? For example, I would like to fade out/in views. Thanks
Currently the test suite is not going to set off any alarms if you are breaking it. One example:
If I change one test to deliberately fail, this happens:
$ grunt test
Running "concat:test" (concat) task
File "./archive/v0.1.1/mithril-tests.js" created.
Running "execute:tests" (execute) task
-> executing /Users/eirikmorland/github/mithril.js/archive/v0.1.1/mithril-tests.js
[Error]
function () {return m("div").tag === "divs"}
tests: 46
failures: 1
-> completed /Users/eirikmorland/github/mithril.js/archive/v0.1.1/mithril-tests.js (59ms)
>> 1 file and 0 calls executed (60ms)
Done, without errors.
So, for a new contributor this may seem like all is well (after all it said "without errors").
And the exit code was 0.
$ echo $?
0
Now, there are several issues here, as I see it.
Following my usual style of problem solving, let's be pragmatic at first:
If the test print function would just throw an error, the error would be pretty obvious, and the exit code would not be 0. This way travis would pick it up too :)
diff in test.js:
+ if (test.failures.length > 0) {
+ throw new Error('Test suite did not pass')
+ }
I'm sure you know about travis-ci, so integrating that would also build the project on pull requests and commits. And of course we can put that fancy badge on top of the README file :)
Please let me know how you feel about this, and if I should send in a pull request. For the sake of examples, here is a failing build on travis:
https://travis-ci.org/eiriksm/mithril.js/builds/21336472
...and a successful build:
https://travis-ci.org/eiriksm/mithril.js/builds/21336580
Now, I also have some thoughts on next steps of better CI, but as I said, please let me know what you think about this.
When I initialize the controller, I look up my model in a global data cache. If I find it I would like to render. Otherwise I want to initialize an empty model with a particular id. After both of those paths I would like to make a request for the current data.
The code I have is below (Note: I'm using msx + msx-loader):
var Page = {};
Page.controller = function() {
var id = m.route.param("nodeID");
this.node = _.findWhere(cache.nodes(), {id: id}) || new Node({id: id });
this.node.fetch();
};
Page.view = function(ctrl) {
return (
<div>
<h1>{ctrl.node.name}</h1>
</div>
);
};
When I switch to the page, I don't see it render until the xhr returns. If I remove the .fetch() statement then it renders immediately. .fetch is doing a m.request behind the scenes.
Do you have any recommendations on how to get the desired behavior? Thanks!
There's a good oppurtunity to modularize various parts of this project using something like browserify & npm
There is overlap with existing projects
You can then also break your one monolithic file into clean seperate multiple files then use commonJS to compose them.
You can also break route related stuff into a seperate optional module
e.g.
m.render(document, m("a", "test"));
m.render(document, m("a.foo", "test"));
Bower mithril is still at 1.1.0
When hardcoding "?" in hrefs, rendering works fine.
When using {config: m.route}, something funky happens. (note that refreshing always fixes the layout)
How it looks when navigating away, and then clicking on a link that uses {config: m.route}
.
The markup itself appears to be missing.
If we focus entirely on the list of projects,
m("div#view", [
common.banner("List of Requested Projects"),
m("section", [
// console.log("you've got to be"),
m("div.row", [
// omitted for brevity
]),
// console.log("kidding me")
])
])
The entire div.row disappears when rendered. BUT when both console.logs are uncommented (the ones before and after the div.row), the div.row appears correctly. When EITHER of them are commented out, the div.row disappears again.
Pretty much anything can make the div.row appear again. Be it a console.log or a string, or a more substantial m("something"), as long as it sandwiches the div.row.
Addendum:
Turns out it also renders properly if I use m("div", {class: "row"}, [/*other stuff*/])
. instead of m("div.row", [/*other stuff*/])
.
Currently m() returns an error if i add an if statement (understandably). To make an inline conditional for an element, i have to make a self-calling function.
(function(){
//do stuff
return m(/*element*/)
})()
A simple way to do m.if would simply be
m.if = function (bool, elem){
if(bool){
return elem
}
}
Especially useful in forms when you have inputs showing and hiding depending on the values of other inputs.
mithril sets withCredentials to true when making XHR requests. Chrome and Firefox disallow CORS requests with credentials if the allow origin is *.
Is there any reason it's set to true instead of being configurable?
In React it is possible to reuse serverside generated html/dom nodes. This is useful when the server already generates a page representation for seo purposes. Is it possible to do something simular in Mithril?
Workaround at this moment seems to explicitly remove any serverside generated html. This seems kind of hacky, is there a nicer way?
<!doctype html>
<body>
<div id="test">
<p>Server Test</p>
</div>
<script src="/js/mithril.js"></script>
<script>
var app = {
controller: function() {
this.paragraph = 'Client Test'
},
view: function(ctrl) {
return m('p', ctrl.paragraph)
}
}
function cleanUp(node) {
while (node.firstChild) {
node.removeChild(node.firstChild)
}
}
var mountAt = document.getElementById('test')
cleanUp(mountAt)
m.module(mountAt, app)
</script>
</body>
Was this broken at some stage in the last few versions?
Adding this test to mithril-tests.js
, it fails for me on master:
test(function() {
var root = mock.document.createElement("div")
m.render(root, m.trust("test"))
return root.childNodes[0].nodeValue === "test"
})
TypeError: parent.insertAdjacentHTML is not a function
I'm getting an even weirder error in the code which prompted me to add this test - a String
created with m.trust()
which is coming back with '[object Object]'
when rendering does its toString()
call and going down the wrong branch. Trying to determine if that's something silly being done on my part first...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.