Git Product home page Git Product logo

mithril-hx's Introduction

Mithril for Haxe

Mithril is a small, yet great javascript MVC framework that is faster and more flexible than most others. Here's the Haxe version for Mithril 2, with some useful extra features thanks to macros and the type inference.

Installation

Standard procedure: haxelib install mithril and then -lib mithril in your .hxml file.

How to use

Mithril has a great introduction on its website and plenty of documentation, so I'll only highlight what you need to get started with the Haxe version here.

Implement the Mithril interface

When using Mithril, you will create components that will be used with the Mithril API. The recommended way to create a component is using a class that implements the Mithril interface. Here's an example of a Mithril component:

import mithril.M;

class TodoComponent implements Mithril
{
    var todos : Array<Todo>;

    public function new(todos) {
        this.todos = todos;
    }

    // When implementing Mithril, the last m() expression 
    // or Array of m() is returned automatically.
    public function view()
        m("div", [
            m("h1", "To do"),
            m("table", [for(todo in todos)
                m("tr", [
                    m("td", m("input[type=checkbox]", { 
                        onclick: e -> todo.done = e.target.checked,
                        checked: todo.done
                    })),
                    m("td", todo.description)
                ])
            ])
        ]);
}

/**
 * The model
 */
class Todo
{
    public var done : Bool;
    public var description : String;

    public function new(description, done = false) {
        this.description = description;
        this.done = done;
    }
}

class Main
{
    // Program entry point
    static function main() {
        var todos = [
            new Todo("Learn Haxe"),
            new Todo("??"),
            new Todo("Profit!")
        ];
        
        M.mount(js.Browser.document.body, new TodoComponent(todos));
    }
}

The major API differences

  • Use M, not m! import mithril.M;, then use M instead of m for the whole API. As you see above, the only exception is when using m(), you can use that without prefixing with M.
  • m.redraw.sync() is available through M.redrawSync().

Upgrading from 1.x to 2.x

  • The M.route methods can now be called as in the Mithril syntax, M.route.param etc. To call M.route however, use M.route.route.
  • M.withAttr has been removed. Use an e -> e.target lambda function instead.

When using Node.js

If you're using Node.js, you can install and use Mithril from npm instead of the Haxe port (see below for server side examples). To do that, define -D mithril-native.

Side note: "this" is slightly different in native javascript

Because of the slight mismatch between Haxe classes and the classless Mithril structure, in lifecycle methods, the native javascript this points to vnode.tag instead of vnode.state. Otherwise it would have pointed to another object when inside instance methods.

This is usually nothing you have to worry about if you're using Haxe classes for your components and state. In that context, this works as expected.

Haxe examples

This repo has some examples that can be interesting to test. Clone it, open a prompt in the directory and run:

haxelib install mithril

Then select one of the following:

Some small apps

A collection of two demo apps, available on the Mithril site.

  1. haxe client.hxml
  2. nekotools server -d bin
  3. Open http://localhost:2000/ in a browser.

Webshop

A simple e-commerce site to demonstrate the power of Mithril.

  1. haxe webshop.hxml
  2. nekotools server -d bin/webshop
  3. Open http://localhost:2000/ in a browser.

Live demo here: http://ciscoheat.github.io/webshop

From scratch

If you prefer a bare-bones example (doesn't require cloning), create the following two files and follow the instructions below:

index.html

<!doctype html>
<body>
<script src="https://unpkg.com/mithril/mithril.js"></script>
<script src="example.js"></script>
</body>

Example.hx

import mithril.M;

class User
{
    public var name : String;

    public function new(name) {
        this.name = name;
    }
}

class Example implements Mithril
{
    var user : User;

    public function new() {
        this.user = new User("Thorin Oakenshield");     
    }

    public function view() [
        // Display an input field
        m('input', {
            // Updates the model on input
            oninput: e -> user.name = e.target.value,

            // The redraw triggered by the oninput event will update
            // the input field value from the model automatically
            value: user.name
        }),
        
        // Display a div with class .user and some style
        m('.user', {style: {margin: "15px"}}, user.name)
    ];

    // Program entry point
    static function main() {
        M.mount(js.Browser.document.body, new Example());
    }
}

Compile and run with:

  1. haxe -lib mithril -js example.js -main Example
  2. Open index.html in a browser.

Server side - All targets

The rendering part of Mithril has been ported to Haxe, so you can now enjoy writing Mithril templates and have them rendered to HTML anywhere. Here's a class to get you started:

import mithril.MithrilNodeRender;
import mithril.M.m;

class Main {
    static function main() {
        var view = m("ul", [
            m("li", "item 1"),
            m("li", "item 2"),
        ]);

        // <ul><li>item 1</li><li>item 2</li></ul>
        Sys.println(new MithrilNodeRender().render(view)); 
    }
}

(Note: The above code may not work in interp mode. Test it with neko instead.)

Server side - Node.js & isomorphism

Without too much hassle, it's possible to render a Mithril component/view serverside on Node.js. Run the following in the repo directory:

  1. npm install
  2. haxelib install hxnodejs
  3. haxe server.hxml
  4. cd bin

Example 1: Simple rendering

node server.js outputs a simple HTML rendering example.

Example 2: Isomorphic code

node server.js server

Starts a server on http://localhost:2000 that executes the same code on server and client. The server generates the HTML so the page is perceived to load quickly and search engines can index it, then the client enables the functionality.

Example 3: Cross-platform rendering

As a bonus, a Neko version of Example 1 will also be compiled. Test it with

neko server.n

The MithrilNodeRender is tested with travix and should work on all targets.

Feedback please!

Feedback is always welcome! Open an issue and give me a piece of your mind. :)

mithril-hx's People

Contributors

andywhite37 avatar ciscoheat avatar freakinruben avatar hamaluik avatar

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

mithril-hx's Issues

Changing/adding elements to HEAD

I am trying to add elements to the HEAD of the html page. Can this be done from the code?
If I do this:

HTML([
    HEAD(...),
    BODY(...),
]);

My final structure in the browser has 2 html/head and body tags (nested into each other).
How is this done correctly?

Broken in Haxe 4.2?

I wish I could figure this out myself, but unfortunately I'm not making any traction. The Either abstract seems to be broken in Haxe 4.2?

Using Haxe 4.2 and running haxe client.hxml you get:

➜ haxe client.hxml
example/DashboardComponent.hx:81: characters 18-27 : DashboardComponent should be mithril._M.Either<mithril.Component, mithril.RouteResolver<Dynamic>>
example/DashboardComponent.hx:81: characters 18-27 : ... For function argument 'routes'

This replicates what I'm seeing in my projects as well, whenever I try to use a route.

Minimal example showing the issue:

package;

import mithril.M;
import js.Browser;

class TestPage implements Mithril {
    public function new() {}

    public function view(vnode: Vnode<TestPage>): Vnodes {
        return m('p', 'Hello');
    }
}

class Main {
    static function main() {
        var testPage = TestPage();

        M.route.route(Browser.document.body, '/', {
            '/': testPage,
        });
    }
}

I believe I've traced this to the abstract Either definition, but I can't figure out how to fix it yet (other than just defining Either as Dynamic and calling it a day).

Putting together a table from functions or components

I am trying to do something like this:

TABLE([
  TR(),
  calcRows(1),
  calcRows(2),
  TR(),
])

Where calcRows outputs several table rows (so its output is an array).

function calcRows() {
  return [TR(),TR()];
}

To get this work I tried:

TABLE(
  [TR()].concat(
  calcRows(1)).concat(
  calcRows(2)).concat(
  [TR()]
)

Which gives a type error. I do not know which type "calcRows" should return.

Can this be done? Maybe using components? This compiles:

TABLE(
  [TR()].concat(
  M.component(calcComp1)).concat(
  M.component(calcComp2)).concat(
  [TR()]
)

Here the calcComp1/calcComp2 view function are identically to calcRows.

But executing this, I get the error:

Component template must return a virtual element, not an Array, String, e.t.c.

Thanks!

CS Serverrendering: namespace error (T4, T3)

Hi Andreas!

When compiling a server renderer example for cs, the Haxe compilation works fine, but the C# compiler complains as follows:

src\mithril\M.cs(170,93): error CS0246: Det gick inte att hitta typ- eller namnområdesnamnet T4 (saknas ett using-direktiv eller en sammansättningsreferens?)
src\mithril\M.cs(170,100): error CS0246: Det gick inte att hitta typ- eller namnområdesnamnet T3 (saknas ett using-direktiv eller en sammansättningsreferens?)

(Sorry for swedish compilation messages - should be no problem for you, I guess :-)

Any idea? (Doesn't seem to be a dce thing - no difference with -dce no. Btw, I don't know if cs cares about dce at all...)

No hurry on this issue - no breaks in production for me because of this..!

Best! / Jonas

How to handle sub-components

Looking at this mithril example: https://lhorie.github.io/mithril/components.html I see that in original mithril components are nested like this:

view: function(ctrl) {
        return [
            m.component(ContactForm, {onsave: ctrl.save}),
            m.component(ContactList, {contacts: ctrl.contacts})
        ]
    }

How should this be done in mithril-hx? Should I just say "new ContactForm"? But then every time view is executed a now contact form is created. Should I have a "ContactForm" propertie in my main component?

component args not passed to view nor controller

hello,

id ont get why args from the M.component method don't get passed to view nor controller as said in the doc.
perhaps i'm doing something wrong .. i'm just starting to understand how mithril works.

return M.component(new ShopItemView(i),{message:"hello"});

with this:

class ShopItemView implements Mithril{
var item:Track;
public function new(item:Track){
    this.item=item;
    this.signal=signal;
    trace(" new Shopitem");
}
public function controller(?args{message:String}){


}
public function view(?ctrl ,?args:{message:String}){
    return m('.vide',args.message+item.title);

}

}

Wiki bug - MVC

The GOF patterns did not come till the 90's. Specifically 1992. It was released at OOPSLA '92. It took approx 2 years before they took hold in a massive way.

Unknown identifier : M

Hey, since commit 740cddf I have been noticing the following error:
ModuleBuilder.hx:215: characters 8-9 : Unknown identifier : M

This only happens if you import one of the interfaces that use "injectCurrentModule" directly like so:
import mithril.M.Component;
In this case M is not defined, only Component.
This can be easily fixed by changing
if(M.__haxecomponents.length && untyped !this.controller) {
to include the package like so:
if(mithril.M.__haxecomponents.length && untyped !this.controller) {

Error when using mithril 0.27.0

Hey,

I just upgraded von mithril 0.25.6, now I get this error in my program:

Uncaught ReferenceError: $hxClasses is not defined

I am using haxe 3.2.1

this in module.controller()

I've noticed mithril calls a controller method with new in JS, setting this to a context inside the controller method.

var controller = new (module.controller || function() {});

https://github.com/lhorie/mithril.js/blob/next/mithril.js#L520

This means the following does not work:

class Test {
    var context: String;
    public function new(context: String) {
        this.context = context;
    }
    public function controller() {
        trace(context);
    }
}

When used as a module this would throw an error because context is not found in this in JS.
Do you think we could use the macros to always bind the controller method with the module object?

Multiple properties in sugaertags

Can I have multiple properties in sugertags syntax? How would I list them?

Something like this ...?

INPUT[type=password;name=password]()

current version of mithril does not use Mithril name.

current version of mithril does not use Mithril name. (I checked version 0.1.26)
so, readme tutorial code does not work.

after running below code, edit example.js to replace Mithril -> m, then, running successfully.

haxe -lib mithril -js example.js -main Example
sed -i 's/Mithril/m/g;' example.js

Works with Mithril 2?

Hi Andreas!

I'm about to start a new js project and would like to use Mithril and Mithrilhx. Is Mithril 2+ is supported by your lib?

Cheers!

Mithril __init__ code, causes problems if included twice

If you compile and use two scripts, using the mithril externs, in the same page the init code runs twice, which causes an infinite loop in m.mount. Could you provide a way to not include the init code using a define for example, or maybe even better check if the initialization has already run so it doesn't run twice?

Compile error for XHRTypeOptions in Haxe 3.2.0

I ran into an error compiling mithril.hxml and webshop.hxml with the 3.2.0 Haxe compiler.

[awhite@acw][~/dev/andywhite37/mithril-hx][git:master][5789][0]>>> haxe mithril.hxml
src/mithril/M.hx:160: characters 12-104 : Constraint check failure for mithril.XHRTypeOptions.T
src/mithril/M.hx:160: characters 12-104 : request.T should be mithril._M.Either<mithril.DataConstructible<request.T5>, Array<mithril.DataConstructible<request.T5>>>

I think I have a fix in a PR, but if there's a better solution or a different approach, feel free to do what you think is best.

Problems with @prop

Trying to follow the tutorial, I created a model class like this:

package models;

import mithril.M;

class AuthData {

  // Authentication data
  @prop public var username : String;
  @prop public var password : String;

  public function new() {
    this.username = M.prop("");
    this.password = M.prop("");
  }
}

But I get this compile error:

Error:(12, 4) mithril.GetterSetter<String> should be String

Mmh, am I using @prop not correctly?

Question on controller macro

Hi again,
I had a question on the controller macro here:

https://github.com/ciscoheat/mithril-hx/blob/master/src/mithril/macros/ModuleBuilder.hx#L176-L178

I'm trying out a couple different ways of componentizing views, and one pattern I tried looked like this (some code is left out):

class Button implements Module<Button> {
  public function new () {
    // something here
  }
  public function controller() {
    // do something?
  }
  public function view() {
    m("button", "My Button");
  }
}

class HomePage implements Module<HomePage> {
  @prop var button : Button;

  public function new(button : Button) {
    this.button = M.prop(button);
  }

  public function controller() {
    // Stack overflow issue here, due to macro logic for controller
    button().controller();
  }

  public function view() {
    m("div", button().view());
  }
}

When the HomePage controller is called, and it calls the Button controller, the button.controller() body ends up re-calling the HomePage controller, because of the if check inserted by the macro at the top of the controller methods. This eventually results in a call stack overflow. Is there anything that could be changed with that macro to allow nested controller calls like this?

if(m.__cm != this && Type["typeof"](m.__cm) != ValueType.TObject) return m.__cm.controller();

In this case m.__cm is HomePage and is != this (Button), and m.__cm is not a TObject, so it re-calls HomePage controller, rather than calling the button controller.

mitrhil-hx not working properly with mithril > 0.2.0

I'm not sure why, but the this scope of controllers is not being changed for controller functions when I use mithril 0.2.1 and higher.

When I pause the debugger, it seems that

    if(m.__haxecomponents.length && !$bind(this,this.controller)) {
        return m.__haxecomponents.pop().controller();
    }

m.__haxecomponents.length is 0 while with 0.2.0 it will be at least 1.

Any chance you know a fix for this?

replacing prop by custom getter, setter

If I understand correctly, we use M.prop so that we later can replace these with custom getter/setter functions if neede. Correct?

What is the way of doing this when it actually happens? If I just replace the property by a haxe property with custom get/set functions, the syntax for access changes.

Calling controllers

I'm trying to achieve something like this:

class Test implements Component {
    public function new() {}
    public function controller() {
        trace('ok');
    }
    public function view() {return m('');}
}

class TestApp implements Component {
    var comp: Component;
    public function new() {}
    public function controller() {}
    public function setView(comp) {
        this.comp = comp;
    }
    public function view() 
        return m('.app', {}, M.component(comp));
}

class Main {
    static function main() {
        var a = new Test();
        var b = new Test();
        var test = new TestApp();
        test.setView(a);
        M.mount(Browser.document.body, test);
        test.setView(b);
        M.redraw(true);
        test.setView(a);
        M.redraw(true);
    }
}

Running the example above traces 'ok' only once, where I would expect it to run for each time I change the view. I don't really know if this is the right way to go about doing something like this, or if there really is a problem with mithril or the externs. Maybe you could point me in the right direction?

noderendering dependency missing

just tried to run the noderendering example and got:
node noderendering.js "server"

/var.../noderendering.js:172
this.ip = Mithril.prop(""); //DashboardModule.hx:24
ReferenceError: Mithril is not defined

also inside of the NodeRendering class there are two calls to
require("mithril-node-render");

I don't have any idea how this will work.

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.