IIFE视图模型似乎未定义

时间:2017-01-19 03:09:32

标签: javascript iife mithril.js

我正在使用Mithril.JS,看起来我的vm未定义,而事先并非如此。

我四处寻找,就mithril.js来说,那里几乎没有。

代码:

var app = {};

var apiData;

app.getData = function () {
  m.request({
    method: 'GET',
    url: '/api/stocks',
  }).then(function(data){
    data = apiData;
  })
};

app.App = function(data){ // model class
  this.plotCfg = {
    chart: {
        renderTo: "plot"
    },
    rangeSelector: {
        selected: 4
    },
    yAxis: {
        labels: {
            formatter: function () {
                return (this.value > 0 ? ' + ' : '') + this.value + '%';
            }
        },
        plotLines: [{
            value: 0,
            width: 2,
            color: 'silver'
        }]
    },

    plotOptions: {
        series: {
            compare: 'percent',
            showInNavigator: true
        }
    },

    tooltip: {
        pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
        valueDecimals: 2,
        split: true
    },

      series: [{
          name: 'Kyle\'s Chart',
          data: apiData
      }]
  };
};
app.controller = function() { // controller
  this.apk = new app.App();
  this.cfg = this.apk.plotCfg;
};
app.plotter = function(ctrl) { // config class
  return function(elem,isin) {
      if(!isin) {
        m.startComputation();
        var chart = Highcharts.StockChart(ctrl.cfg);
        m.endComputation();
      }
  };
};
app.view = function(ctrl) { // view
  return m("#plot[style=height:400px]", {config: app.plotter(ctrl)})
};

app.Stock = function(data) {
  this.date_added = m.prop(new Date());
  this.symbol = m.prop(data.symbol);
  this.id = m.prop(data.id)
};

app.SymbolList = Array;

app.vm = (function() {
    var vm = {}
    vm.init = function() {
        //a running list of todos
        vm.list = new app.SymbolList();
        //a slot to store the name of a new todo before it is created
        app.parseData = function (data) {
          for (var i =0; i< list.length ;i++) {
            console.log(list[i].stock);
            var stockSymbol = data[i].stock;
            vm.list.push(new app.Stock({symbol : stockSymbol}));
        }
        app.parseData(apiData);
        vm.symbol = m.prop("");
        //adds a todo to the list, and clears the description field for user convenience
        vm.add = function() {
            var data = vm.symbol();
            if (vm.symbol()) {
                data = {'text': data.toUpperCase()};
                m.request({method: 'POST',
                            url: '/api/stocks',
                            data: data,
                          }).then(function(list) {
                            vm.list = [];
                            for (var i =0; i< list.length ;i++) {
                              console.log(list[i].stock);
                              var stockSymbol = list[i].stock;
                              vm.list.push(new app.Stock({symbol : stockSymbol}));
                            }
                            return;
                          })
                vm.symbol("");
            }
        };
    }
    return vm
  }
}())

app.controller2 = function() {
  app.vm.init();
}
app.view2 = function() {
  return [
      m('input', { onchange: m.withAttr('value', app.vm.symbol),  value: app.vm.symbol()}),
      m('button.btn.btn-active.btn-primary', {onclick: app.vm.add}, 'Add Stock'),
      m('ul', [
        app.vm.list.map(function(item , index) {
          return m("li", [
            m('p', item.symbol())
          ])
        })
      ])
  ]
};
m.mount(document.getElementById('chart'), {controller: app.controller, view: app.view}); //mount chart
m.mount(document.getElementById('app'), {controller: app.controller2, view: app.view2}); //mount list
<div id="app"></div>
<div id="chart"></div>
<script src="https://cdn.rawgit.com/lhorie/mithril.js/v0.2.5/mithril.js"></script>
<script src="https://code.highcharts.com/stock/highstock.js"></script>

Chrome中弹出的错误是:

app.js:119 Uncaught TypeError: Cannot read property 'init' of undefined
    at new app.controllerT (app.js:119)
    at ea (mithril.js:1408)
    at Function.k.mount.k.module (mithril.js:1462)
    at app.js:135

在添加第二个挂载点,视图和控制器之前没事。

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

问题是app.vm不会公开init

app.vm的当前代码如下所示:

app.vm = (function(){
  var vm = {}
  vm.init = function(){
    /* lots of stuff... */

    return vm
  }
}())

这意味着内部vm.init会返回vm,但app.vm IIFE不返回任何内容。它应该是:

app.vm = (function(){
  var vm = {}
  vm.init = function(){
    /* lots of stuff... */
  }
  return vm
}())

阅读您的应用程序结构非常困难,因为它充满了各种奇特的模式,似乎没有用处。不可否认,'vm'闭包是Mithril指南中引入的模式,但我认为如果我们避免所有这些闭包,初始化调用,构造函数,嵌套对象和命名空间,编写,推理和调试应用程序要容易得多。

“视图模型”背后的想法来自于Mithril最初发布时(2014年初)Web应用程序开发的状态,前端应用程序开发中的一个主要问题是缺乏结构,而Mithril认为有必要向人们展示如何构建对象。但是这种形式的结构只有在澄清意图时才有用 - 在上面的代码中它会混淆事物。例如,app.getData不会在任何地方调用,它会在处理它之前为其自己的参数指定一个空的全局变量。如果我们有 less 对象,那么这种事情会更容易推理。

这是相同的代码,带有一些额外的修复和替代结构。在这个重构中工作的原则:

  1. 我们不再编写任何自己的构造函数或闭包,导致代码执行动态性降低,并避免出现app.vm.init
  2. 等错误的可能性
  3. 我们不再将对象附加到对象上,除非该结构有用或有意义,并且如果它们仅使用一次,则使用简单变量或在使用点声明事物,从而导致更少的引用和更少的结构复杂性
  4. 我们使用对象文字 - var x = { y : 'z' }而不是var x = {}; x.y = 'z',因此我们可以看到整体结构,而不是必须在心理上解释代码执行,以确定如何在运行时构建对象
  5. 我们不是使用一个大的通用app.vm来存储所有内容,而是将我们的应用模型分成相关的位置,并使用函数将值从一个地方传递到另一个地方,这样我们就可以分散我们的复杂性。在显示代码之后我会详细说明:
  6. // Model data
    var seriesData = []
    
    // Model functions
    function addToSeries(data){
      seriesData.push.apply(seriesData,data)
    }
    
    function getData( symbol ){
      m.request( {method: 'POST',
        url: '/api/stocks',
        data: { text : symbol.toUpperCase() },
      } ).then(function(list) {
        return list.map(function( item ){
          return makeStock( { symbol : item.stock } )
        } )
      } )
    }
    
    function makeStock( data ) {
      return {
        date_added : new Date(),
        symbol     : data.symbol,
        id         : data.id
      }
    }
    
    // View data
    var chartConfig = {
      rangeSelector: {
        selected: 4
      },
      yAxis: {
        labels: {
          formatter: function () {
            return (this.value > 0 ? ' + ' : '') + this.value + '%';
          }
        },
        plotLines: [{
          value: 0,
          width: 2,
          color: 'silver'
        }]
      },
    
      plotOptions: {
        series: {
          compare: 'percent',
          showInNavigator: true
        }
      },
    
      tooltip: {
        pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
        valueDecimals: 2,
        split: true
      },
    
      series: [{
        name: 'Kyle\'s Chart',
        data: seriesData
      }]
    }
    
    // Components
    var chartComponent = {
      view : function(ctrl) {
        return m("#plot[style=height:400px]", {
          config: function(elem,isin) {
            if(!isin)
              Highcharts.StockChart(elem, chartConfig)
          }
        })
      }
    }
    
    var todosComponent = {
      controller : function(){
        return {
          symbol : m.prop('')
        }
      },
    
      view : function( ctrl ){
        return [
          m('input', {
            onchange: m.withAttr('value', ctrl.symbol),
            value: ctrl.symbol()
          }),
    
          m('button.btn.btn-active.btn-primary', {
            onclick: function(){
              if( ctrl.symbol() )
                getData( ctrl.symbol() )
                  .then( function( data ){
                    addToSeries( data )
                  } )
    
              ctrl.symbol('')
            }
          }, 'Add Stock'),
    
          m('ul',
            todos.map(function(item) {
              return m("li",
                m('p', item.symbol)
              )
            })
          )
        ]
      }
    }
    
    // UI initialisation
    m.mount(document.getElementById('chart'), chartComponent)
    m.mount(document.getElementById('app'), todosComponent)
    

    不再有appvmlist。这些最终变得无益,因为它们是如此模糊和通用,它们习惯于存储所有东西 - 当一个对象包含所有东西时,你也可以免费获得这些东西。

    核心动态数据列表现在称为seriesData。这只是一个阵列。为了与之交互,我们有3个简单的函数来改变系列数据,获取新数据,以及从输入创建新的数据点。这里不需要构造函数,也不需要道具 - props是一个Mithril实用程序,可以方便地从输入读取和写入数据 - 它们在任何情况下都与Highcharts API不兼容。

    这就是我们需要的所有模型数据。接下来,我们将获得特定于UI的代码。 Highcharts配置对象引用seriesData,但除此之外,它的一个深奥的对象被编写为符合Highcharts API。我们遗漏renderTo,因为这是由我们的秘银UI动态决定的。

    接下来是组件,我们将其编写为对象文字而不是稍后将它们拼接在一起 - 组件控制器仅对其视图有意义。 chartComponent实际上并不需要控制器,因为它没有状态,只读取先前定义的模型和视图数据。我们直接向config函数中的Highcharts API提供元素引用。因为这个函数只在一个地方使用过一次,所以我们将它声明为内联而不是在一个地方定义并将其绑定到其他地方。 start / endComputation是不必要的,因为该过程是同步的,并且在此过程中无需停止Mithril渲染。

    我无法弄清楚'todos'模型是如何工作的,但我认为第二个组件旨在提供数据点的备用视图,并允许用户输入来定义和获取更多数据。我们在这里将'symbol'prop存储在控制器中,因为它是一个由视图专用的有状态属性。这是我们与该组件相关的唯一有状态属性,因此我们在控制器中定义了所有属性。之前我们简化了与模型相关的函数 - 现在在视图中我们与这些函数交互,显式传递符号数据,而不是在别处定义它并在另一个地方检索它。我们还在这里重置了值,因为这是该组件逻辑的一个方面,而不是整个数据模型。