Coder Social home page Coder Social logo

mithriljs / mithril.js Goto Github PK

View Code? Open in Web Editor NEW
13.9K 312.0 926.0 12.08 MB

A JavaScript Framework for Building Brilliant Applications

Home Page: https://mithril.js.org

License: MIT License

HTML 0.16% JavaScript 99.84%
javascript framework virtual-dom xhr router mithril vdom

mithril.js's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mithril.js's Issues

Mode abstraction and <a> elements

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.

m.route's defaultRoute blows away existing hash

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.

Allow for object controllers, not only constructors

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.

Render without DOM?

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.

Mithril redraw does not diff on element id or className

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/

Print objects when passed into the view

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.

Strange reordering in specific cases when using conditionals

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.

Transparent m.route.mode

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?

Mailing list suggestions?

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.

v0.1.4 breaks at least getting started app

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

In function build, parent is sometimes null

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.

Inheritance in templates?

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

knowing previous model value to optimize rendering

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.

Template for <p>Click <a># time</a></p>

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

Rendering lists in unexpected order

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

browser support?

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.

Realtime view updates with input

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);

Render frequency

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?

Multiple module binding question

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

Is rootElement in m.route and m.module supposed to change what mithril injects into the html?

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?

Multiple event listeners interfere with each other

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? :)

Updating controller attributes using routes

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!)

Sharing information between modules

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.

Promises

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.

http://jsfiddle.net/3na87/

(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.

Events?

Is there a list of available hooks/ events? For example, I would like to fade out/in views. Thanks

CI and tests.

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.

  • There should be a CI service for the project (for example travis-ci)
  • Errors exits with 0, hence CI would not pick this up.
  • Tests should ideally be run in a suite of browsers.

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.

Render while waiting for m.request

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!

Modularize

There's a good oppurtunity to modularize various parts of this project using something like browserify & npm

There is overlap with existing projects

  • observable (similar to m.prop())
  • virtual-dom (similar to m('div'))
  • bluebird (similar to m.deferred)
  • xhr (similar to m.request)

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

m.route rendering issue

When hardcoding "?" in hrefs, rendering works fine.

When using {config: m.route}, something funky happens. (note that refreshing always fixes the layout)

How it should look:
screen shot 2014-03-30 at 8 02 08 pm

How it looks when navigating away, and then clicking on a link that uses {config: m.route}.
screen shot 2014-03-30 at 8 03 18 pm

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*/]).

Add an m.if and/or m.switch

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.

CORS requests fail with Access-Control-Allow-Origin: *

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?

prepopulate node cache

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>

Rendering m.trust()

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...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.