javascript 对象 - 检索子 class 变量

javascript objects - retrieve child class variable

题底有javascriptcoffescriptjsfiddle。

两者 fiddle 都包含 需要按特定顺序阅读的解释性注释 ,它 将值打印到 console 当你点击 productsubmit div 时,另外我给你这个问题的基本解释。


My coffeescript jsfiddle is at the following link

点击下面打开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;
}
<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>
  <div id="submit">Create Purchase</div>
</ul>

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

在我编写开源代码时,您可以查看我的 commit history, the specific commit 并 fork 项目

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

我不确定你对 Item class 的计划,所以这里有一个使用建议的 serialize 方法的简单模型:

class Item
  constructor: (@purchase, @product, @quantity) ->

  serialize: =>
    purchase_id: @purchase.id.toString()
    product_id: @product.id.toString()
    quantity: parseInt(@quantity)

鉴于您的购买 class 将有一个 @items 的数组,那么 Purchaseserialize 方法将如下所示:

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

然后你的 ajax post 将使用 serialize 方法:

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

那么你应该得到 JSON post 的

正文
'purchase' => {
  'items_attributes' => [
    {
      'purchase_id' => '1'
    },
    {
      'purchase_id' => '2'
    }
  ]
}

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

params.require(:purchase).permit(item_attributes: [:purchase_id])

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

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

工作代码段: 我已经注释掉 ProductonSubmit

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

我已经更新了您的 coffeescript fiddle 以按照代码注释中的问题工作。

这是我的 updated version.

我已经更改了您的 class 结构,因此不需要任何静态变量,在这种情况下,这似乎是绕过糟糕设计的 hack。

您已将模型结构创建为:

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

但是您的 post 数据格式要求表明:

  • 一次购买有很多商品
  • 一件商品属于一件产品(通过参考 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) 是 shorthand 用于展平一层深度的嵌套数组(取自此 answer)。

现在每个产品实例都会在其自身上保存一组项目,而不是静态地保存在 class 上。

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)

我认为对这个 class 结构进行更好的重新设计是删除 ProductItem class,因为只有一个产品 ID,并且据我所知,项目就像一个计数器,显示正在购买多少单位的产品。您可以在产品上保留一个整数值,而不是为此使用 class:

作为 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

输出现在看起来不同,但更干净:

新:

{
  "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"
    }
  ]
}

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


最后,我想补充一点,这种将事件侦听器和逻辑附加到 DOM 的 "object-oriented" 方法比加载时执行的典型 jquery 函数更加结构化。但我过去曾使用过它,同时保持 DOM 结构和代码更新是一件痛苦的事情,而且经常会因为一个代码的更改没有反映到另一个而导致错误。

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

虽然通常与 JSX 一起使用,但它与 Coffeescript 结合得很好,但是这方面的资源很少。 Arkency 也写了一篇关于 react + coffeescript and I wrote a short post comparing coffeescript to jsx 的好博客。