MithrilJS / mithril.js

A JavaScript Framework for Building Brilliant Applications
https://mithril.js.org
MIT License
13.99k stars 926 forks source link

mithril require loader error #1626

Closed togoog closed 7 years ago

togoog commented 7 years ago

var asyncModule = function(name) { return { oninit: function() { require([name], function(module) { this.controller = new module.controller() this.view = module.view }.bind(this)) }, view: function(ctrl) { return ctrl.view(ctrl.controller) } } }

the code error

tivac commented 7 years ago

Could you provide some more context on the issue you're having? What's the actual error? What's the expected behavior? Do you have a repo available that shows the issue? As it stands this isn't a very actionable bug report.

togoog commented 7 years ago

var asyncModule = function(name) { return { oninit: function(vnode) { require([name], function(module) { vnode.state.oninit= new module.oninit() vnode.state.view = module.view }) }, view: function(vnode) { return vnode.state.view(vnode.state.controller) } } }

Edit by Mithril.js 1.0.1 But Error "Uncaught TypeError: Cannot read property 'view' of undefined"

In v1.x there is no more controller property in components, use oninit instead.

tivac commented 7 years ago

Without more context I can only guess, and that's not useful for anyone. Post as complete an example of your issue as possible please, ideally on jsbin or jsfiddle where it can be inspected.

togoog commented 7 years ago

@tivac Thanks!!

ChrisGitIt commented 7 years ago

Hi Tivac,

i think your problem happens because of the following situation:

oninit: You load the desired module, BUT vnode.state is just an empty object. You just populate it AFTER the require took place. But in the meantime mithril wants to draw something ... with its designated "view" function ... so when mithril wants to render the view (view:function() ....) the vnode.state.view is undefined => the error occurs.

I'm not sure, but there might be two solutions i can think of now.

First: Rewrite the view like this:

view: function(vnode) {
 if(vnode.state.view && vnode.state.controller) {
  return vnode.state.view(vnode.state.controller);
 } else {
  return m('div', 'Loading the page');
}

Second: Tell mithril not to render anything ... i'm not sure if this is possible with the current 1.0.1 Mithril Version because m.startComputation/m.endComputation have been removed ... so maybe ... sorry, can not find the method i had in mind (m.redraw.strategy("none") ... myabe also removed in the current version? You should check that ... might be easier).

Greets,

Chris

ChrisGitIt commented 7 years ago

By the way, you are assign vnode.state.oninit and not vnode.state.controller, so your view should look like this:

view: function(vnode) {
 if(vnode.state.view && vnode.state.oninit) {
  return vnode.state.view(vnode.state.oninit);
 } else {
  return m('div', 'Loading the page');
}

I think it would be much easier if you set up a quick jsfiddle so its not just imaginary ...

barneycarroll commented 7 years ago

@togoog the code you've written is a mixture of Mithril v0 and v1 APIs, so it can't work in either. You should use one or the other. It's also unnecessary and dangerous to write a function that creates new components - you should simply write it as a component instead. I wrote more about this in the components documentation. Let's aim for a component that can be invoked as follows:

m(AsyncComponent, {name: 'ComponentName'})

Here's how to fix it in Mithril v0.2 (live demo here):

// v0.2 definition:
var AsyncComponent = {
  controller: function(attrs) {
    require([attrs.name], function(component) {
      // Components don't necessarily have controllers
      this.controller = new (component.controller || Function)
      this.view = component.view

      // Make sure to redraw when the async component resolves
      m.redraw()
    }.bind(this))
  },

  view: function(ctrl) {
    // A Mithril v0.2 view must return a virtual element,
    // so we need a div while we wait for the async component to resolve
    return ctrl.view ? ctrl.view(ctrl.controller) : m('div')
  }
}

And here's how to fix it in Mithril v1.0 (live demo here):

// v1 definition:
var AsyncComponent = {
  // In Mithril v1, we access input (attrs) & state (ctrl) from the vnode interface
  oninit: function(vnode) {
    require([vnode.attrs.name], function(component) {
      // We no longer need to (and shouldn't) manually build components:
      // just bind the reference to state
      vnode.state.component = component

      m.redraw()
    })
  },

  view: function(vnode) {
    // We can now return arrays from views:
    // If vnode.component isn't ready yet, no DOM will be rendered.
    return [ vnode.state.component && m(vnode.state.component) ]
  }
}
togoog commented 7 years ago

@barneycarroll You are right, Thank you!

dead-claudia commented 7 years ago

Can this be closed now?

togoog commented 7 years ago

Closed thanks!