代码之家  ›  专栏  ›  技术社区  ›  fabOnReact

javascript对象-检索子类变量

  •  2
  • fabOnReact  · 技术社区  · 7 年前

    这个问题有一个 javascript coffescript jsfiddle位于问题的底部。

    两种小提琴都包括 解释性意见 需要按照特定的顺序阅读 将值打印到 console 当你点击 product submit div ,此外,我对我的问题作了基本解释。

    • 我有3个Javascript类 Purchase ,则, Product Item
    • 购买 有很多 Products ,则, 产品 有很多 Items
    • 这个 购买 对象设置 click event handler $('submit') onClick() 将发布 items 数据到我的后端api
    • 这是 data 格式接受自 my backend api

      {
        'purchase' => {
          'items_attributes' => {
            '0' => {
              'purchase_id' => '1'
            },
            '1' => {
              'purchase_id' => '2'
            }
          }
        }
      }
      

    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 这个 specific commit 和分叉项目

    3 回复  |  直到 7 年前
        1
  •  2
  •   caffeinated.tech    7 年前

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

    我不确定你的计划 Item 类,所以这里有一个简单的模型 serialize 方法:

    class Item
      constructor: (@purchase, @product, @quantity) ->
    
      serialize: =>
        purchase_id: @purchase.id.toString()
        product_id: @product.id.toString()
        quantity: parseInt(@quantity)
    

    假设您的purchase类将有 @items ,然后 Purchase 序列化 方法如下所示:

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

    然后,您的ajax帖子将使用 序列化 方法:

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

    那么您应该得到

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

    可通过强参数在rails控制器中使用:

    params.require(:purchase).permit(item_attributes: [:purchase_id])
    
        2
  •  2
  •   Munim Munna    7 年前

    您只需将事件绑定到 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>
        3
  •  1
  •   caffeinated.tech    7 年前

    我已经根据代码注释中的问题更新了您的coffeescript小提琴。

    这是我的 updated version

    我已经更改了您的类结构,这样就不需要任何静态变量,在这种情况下,这似乎是一种绕过糟糕设计的方法。

    您已将模型结构创建为:

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

    但您的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) 是将嵌套数组展平一层深度的速记(取自此 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)
    

    我认为对这个类结构更好的重新设计是删除 Product Item 类,因为只有一个产品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
    

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

    新建:

    {
      "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的“面向对象”方法比加载时执行的典型jquery函数更结构化。但我在过去使用过它,保持DOM结构和代码的更新是一件痛苦的事,而且由于其中一个中的代码更改没有映射到另一个上,常常会导致错误。

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

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