javascript对象 - 检索子类变量

时间:2018-04-19 06:44:11

标签: javascript ajax coffeescript javascript-objects

问题底部有一个javascriptcoffescript jsfiddle。

这两个小提琴都包含需要按特定顺序阅读的解释性评论,当您点击{{1>时打印出值console }}或product,另外我给你这个问题的基本解释。

  • 我有3个Javascript课程submit divPurchaseProduct
  • 一个 Item有多个Purchase一个 Products有多个Product
  • Items对象在Purchase上设置了click event handler,而$('submit')会将onClick()数据发布到我的后端api
  • 这是从my backend api

    接受的items格式
    data

My coffeescript jsfiddle is at the following link

点击下方打开 { 'purchase' => { 'items_attributes' => { '0' => { 'purchase_id' => '1' }, '1' => { 'purchase_id' => '2' } } } }

javascript fiddle
(function() {
  var Item, Product, Purchase,
    bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  Purchase = (function() {
    function Purchase() {
      /* on $(document).ready a new Purchase is created */ 
      this.submit = $('#submit');
      /* for each div.product a new Product instance is created */
      this.products = $.map($('.product'), function(product, i) {
        return new Product(product);
      });
      / @onSubmit() */
      
      /* Comment 3) 
      My issue here is how to I access the this.items from the Purchase class and call serialize()?
      onSubmit: function () {
        @submit.click(function(){console.log(Product.serialize())};
      }     */
    }

    return Purchase;

  })();

  Product = (function() {
    Product.items = [];

    function Product(product) {
      this.product = $(product);
      this.id = this.product.data("id");
      this.submit = $('#submit');
      this.setEvent();
      this.onSubmit();
    }

    Product.prototype.setEvent = function() {
      return this.product.click((function(_this) {
        return function() {
          /* Comment 1)
             Product.items is a class variable of Product, because I need to access it from the Purchase class and send post request. When the user clicks on the $('submit') button*/
          Product.items.push(new Item(_this.id));
          return console.log(Product.items);
        };
      })(this));
    };

    Product.prototype.onSubmit = function() {
      return this.submit.click(function() {
      /* Comment 2) 
      This works as you can see, but we have 4 products and this operation will 
      be performed 4 times. I want to achieve this in the Purchase object so it is perfomed only once, by creating a sumit event handler in Purchase */      
        return console.log(Product.serialize());
      });
    };

    Product.serialize = function() {
      var item;
      return {
        items_attributes: (function() {
          var j, len, ref, results;
          ref = Product.items;
          results = [];
          for (j = 0, len = ref.length; j < len; j++) {
            item = ref[j];
            results.push(item.serialize());
          }
          return results;
        })()
      };
    };

    return Product;

  })();

  Item = (function() {
    function Item(product_id) {
      this.product_id = product_id;
      this.serialize = bind(this.serialize, this);
    }

    Item.prototype.serialize = function() {
      return {
        product_id: this.product_id.toString()
      };
    };

    return Item;

  })();

  $(document).ready(function() {
    return new Purchase();
  });

}).call(this);
.console {
  background-color: grey;
  color: white;
  height: 500px;
}      # I print to the console Product.items 

h4 {
  color: red;
  width: 100%;
  text-align: center;
}

在我编写opensource时,您可以查看我的commit historyspecific commit并分叉项目

3 个答案:

答案 0 :(得分:2)

我是Active Model Serializer宝石的粉丝,它现在是Rails的一部分。我会尝试通过向所有类添加序列化方法将此模式扩展到您的coffeescript中,并在将数据传递到服务器时调用它们。

我不确定您对projectname-projectversion.rpm类的计划,所以这里有一个简单的模型,其中包含建议的Item方法:

serialize

鉴于您的购买类将包含class Item constructor: (@purchase, @product, @quantity) -> serialize: => purchase_id: @purchase.id.toString() product_id: @product.id.toString() quantity: parseInt(@quantity) 数组,那么@items的{​​{1}}方法将如下所示:

Purchase

然后你的ajax帖子会使用serialize方法:

serialize: =>
  items_attributes: (item.serialize() for item in @items)

然后你应该得到一个

的JSON帖子体
serialize

你可以通过强大的参数在你的rails控制器中使用:

$.ajax
   url: "/items"
   method: "POST"
   dataType: "json"
   data: 
     purchase: @serialize()
   error: (jqXHR, textStatus, errorThrown) ->
   success: (data, textStatus, jqXHR) ->

答案 1 :(得分:2)

初始化时,您可以简单地将事件绑定到Purchase对象中。

this.submit.click(function() {
    return console.log(Product.serialize());
});

工作代码段:我已对onSubmit Product进行了评论。

(function() {
  var Item, Product, Purchase,
    bind = function(fn, me) {
      return function() {
        return fn.apply(me, arguments);
      };
    };

  Purchase = (function() {
    function Purchase() {
      /* on $(document).ready a new Purchase is created */
      this.submit = $('#submit');
      /* for each div.product a new Product instance is created */
      this.products = $.map($('.product'), function(product, i) {
        return new Product(product);
      });
      / @onSubmit() */

      /* Comment 3) 
      My issue here is how to I access the this.items from the Purchase class and call serialize()?
      onSubmit: function () {
        @submit.click(function(){console.log(Product.serialize())};
      }     */
      this.submit.click(function() {
        return console.log(Product.serialize());
      });
    }

    return Purchase;

  })();

  Product = (function() {
    Product.items = [];

    function Product(product) {
      this.product = $(product);
      this.id = this.product.data("id");
      this.submit = $('#submit');
      this.setEvent();
      // this.onSubmit();
    }

    Product.prototype.setEvent = function() {
      return this.product.click((function(_this) {
        return function() {
          /* Comment 1)
             Product.items is a class variable of Product, because I need to access it from the Purchase class and send post request. When the user clicks on the $('submit') button*/
          Product.items.push(new Item(_this.id));
          return console.log(Product.items);
        };
      })(this));
    };

    // Product.prototype.onSubmit = function() {
    //   return this.submit.click(function() {
    //     /* Comment 2) 
    //     This works as you can see, but we have 4 products and this operation will 
    //     be performed 4 times. I want to achieve this in the Purchase object so it is perfomed only once, by creating a sumit event handler in Purchase */
    //     return console.log(Product.serialize());
    //   });
    // };

    Product.serialize = function() {
      var item;
      return {
        items_attributes: (function() {
          var j, len, ref, results;
          ref = Product.items;
          results = [];
          for (j = 0, len = ref.length; j < len; j++) {
            item = ref[j];
            results.push(item.serialize());
          }
          return results;
        })()
      };
    };

    return Product;

  })();

  Item = (function() {
    function Item(product_id) {
      this.product_id = product_id;
      this.serialize = bind(this.serialize, this);
    }

    Item.prototype.serialize = function() {
      return {
        product_id: this.product_id.toString()
      };
    };

    return Item;

  })();

  $(document).ready(function() {
    return new Purchase();
  });

}).call(this);
.console {
  background-color: grey;
  color: white;
  height: 500px;
}

h4 {
  color: red;
  width: 100%;
  text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul>
  <li class="product" data-id="1">Product 1</li>
  <li class="product" data-id="2">Product 2</li>
  <li class="product" data-id="3">Product 2</li>
  <li class="product" data-id="4">Product 3</li>
  <li class="product" data-id="5">Product 4</li>
  <button type="button" id="submit">Create Purchase</button>
</ul>

<h4>check logs by opening the console</h4>

答案 2 :(得分:1)

我已根据代码评论中的问题更新了您的咖啡因小提琴。

这是我的updated version

我已经改变了你的类结构,因此不需要任何静态变量,在这种情况下看起来像是一个糟糕的设计。

您已将模型结构创建为:

  • 一次购买有很多产品
  • 一个产品有很多项目

但您的发布数据格式要求显示:

  • 一次购买有很多项目
  • 一个项目属于一个产品(通过引用ID)

为了解决这种不一致问题,我将产品中的序列化数据展平,以便items_attributes是序列化项目对象的数组:

class Purchase
  ...
  serialize: =>
    items = (product.serialize() for product in @products)
    # flatten array of array of items:
    items_attributes: [].concat.apply([], items)

这个神秘的线条[].concat.apply([], items)是展平嵌套数组的一层深度的简写(取自answer)。

现在,每个产品实例都会自行保存一系列项目,而不是静态地保存在类中。

class Product  
  constructor: (product) ->
    @product = $(product)
    @id = @product.data("id")
    @submit = $('#submit')
    @items = []
    @registerEvents()

  addItem: =>
    console.log "added item #{@id}"
    @items.push new Item(@id) 

  registerEvents: ->
    @product.click @addItem

  serialize: =>
    (item.serialize() for item in @items)

我认为更好地重新设计此类结构将删除ProductItem类,因为只有产品ID,据我所知,项目就像计算购买产品的数量单位的计数器。您可以在产品上保留一个整数值,而不是为此设置类:

作为fiddle

class Purchase
  constructor: () -> 
    # on $(document).ready a new Purchase is created
    @submit = $('#submit')
    # for each div.product a new Product instance is created
    @products = $.map $('.product'), (product, i) -> 
      new Product(product)
    @registerEvents()

  onSubmit: => 
    console.log "send to server..."
    console.log JSON.stringify(@serialize(), null, 2)

  registerEvents: -> 
    @submit.click @onSubmit

  serialize: =>
    items_attributes: (product.serialize() for product in @products when product.amount isnt 0)

class Product  
  constructor: (product) ->
    @product = $(product)
    @id = @product.data("id")
    @submit = $('#submit')
    @amount = 0
    @registerEvents()

  addItem: =>
    console.log "added item #{@id}"
    @amount++

  registerEvents: ->
    @product.click @addItem

  serialize: =>
    product_id: @id
    amount: @amount

输出现在看起来不同但是IMO更清洁:

{
  "items_attributes": [
    {
      "product_id": 1,
      "amount": 1
    },
    {
      "product_id": 2,
      "amount": 3
    }
  ]
}

<强>旧

{
  "items_attributes": [
    {
      "product_id": "1"
    },
    {
      "product_id": "2"
    },
    {
      "product_id": "2"
    },
    {
      "product_id": "2"
    }
  ]
}

但是这可能不适合您当前的后端实现,具体取决于当前处理重复项的方式,因此如果无法更改任何遗留约束,请忽略此最后一部分。

最后,我想补充一点,这个&#34;面向对象&#34;将事件侦听器和逻辑附加到DOM的方法比在加载时执行的典型jquery函数更加结构化。但我过去曾经使用它,并且保持DOM结构和代码更新是一件痛苦的事情,并且经常会因为代码更改而导致错误,而另一方则没有镜像。

作为替代方案,我强烈建议调查reactjs或类似的DOM抽象类型库。这些允许您将逻辑强烈地耦合到它们所依赖的视图元素。

虽然通常与JSX一起使用,但它与Coffeescript配合得很好,但是这方面的资源非常少。 Arkency写了一篇关于react + coffeescript的好博客,我也写了一篇短文comparing coffeescript to jsx