Vue js结合了两个组件的元素 [英] Vue js combining the elements from two components

查看:65
本文介绍了Vue js结合了两个组件的元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试构建电子商务类型应用的结帐页面。在结帐时,我有一个来自数据库的OrderItem列表,每个都有价格,数量。您可以选择并组合这些。此外,我必须在其他地方渲染一个带有插件的清单。这也是OrderItems(相同的属性),但具有不同的类型。



我有一个Vue.js组件,用于渲染可以从中选择的OrderItems数组。我的方式是两次渲染相同的组件。但是,selected属性使模型形成一个列表或另一个列表,但不能同时从两个列表中。我希望选中的道具能够保存两个列表中的项目(简单的orderItems和插件)



小提琴: https://jsfiddle.net/w8vfb64L/



代码:



模板:

 < section class =content> 
< div class =rowid =app>
< div class =col-md-8>
< div class =box box-primary>
< div class =box-body>
< div class =row>
< div class =col-md-12>
< div class =form-group>
< label class =control-label required> Items< / label>
< div class =col-md-12>
< entries:entries ={0:{shareSize:'Small',数量:'1',itemPrice:'24',频率:''},1:{shareSize:'Medium',数量: '1',itemPrice:'35',频率:''},2:{shareSize:'大',数量:'1',itemPrice:'46',频率:''}}
:已选中= 选择 >< /条目>
< / div>
< div class =col-md-12>
< label class =control-label required> Addons< / label>
< entries:entries ={0:{shareSize:'Large',quantity:'1',itemPrice:'46',frequency:''}}:selected =selected>< /条目>

< / div>
< / div>
< / div>
< / div>
< / div>
< / div>
< div class =col-md-4>
< div class =box box-info>
< div class =box-bodystyle =padding:15px;>
< div class =container-fluid>
< div class =form-group>
< div class =control-label>
< label>摘要< / label>
< / div>
< div class =form-controlv-for =item in selected>
< span class =pull-left small-box-footer> {{item.shareSize}}< / span>
< span class =pull-right> {{item.quantity +'x $'+(item.itemPrice * item.quantity).toFixed(2)}}< / span>
< / div>
< div class =control-label>
< label>付款计划< / label>
< / div>
< div class =col-md-12>
{{'$'+ totalAdvance.toFixed(2)}} - 提前
< / div>
< div class =col-md-12>
{{'$'+ totalFirstWeek.toFixed(2)}} - 第一周
< / div>
< div class =col-md-12>订阅的{{week}}周内每周
{{'$'+ onDeliveryPayment.toFixed(2)}} /周
< / div>
< div class =col-md-12 row>
< div class =control-label>< strong>< span class =pull-left>总计< / span>< span class =pull-right> { {'$'+ total.toFixed(2)}}< / span>< / strong>< / div>
< / div>
< div class =col-md-12 row>
< div class =title>< strong>< span class =pull-left>现在到期总数< / span>< span class =pull-right> { {'$'+ totalAdvance.toFixed(2)}}< / span>< / strong>< / div>
< / div>
< / div>
< / div>
< / div>
< / div>
< / div>
< / div>
< / div>
< / section>

<! - 组件模板 - >
< template id =entries>
< div class =col-md-12> entries中的
< div class =form-groupv-for =(entry,key)复制v-bind:entry =entry>
< div class =form-group col-md-12>
< div class =col-md-12>
< div class =col-md-4>
< input type =checkboxv-bind:value =entryv-model =selectedCopy>
< / div>
< div class =col-md-4> {{entry.shareSize}}< / div>
< div class =col-md-4> {{'$'+ Number(entry.itemPrice).toFixed(2)}}< / div>
< / div>
< div class =form-group col-md-12>
< div class =col-md-6>
< input type =numberv-model =entry.quantity:value =entry.quantity/>
< / div>
< / div>

< / div>
< / div>
< / div>
< / template>

Javascript:

  var bus = new Vue(); 

var entriesComponent = Vue.component('entries',{
template:'#entries',
props:{
entries:[Array,Object],选择
:数组,
插件:数组,
频率:[数组,对象],
},
创建:function(){
this。 entriesCopy = this.entries;
this.selectedCopy = this.selected;
},
watch:{
selectedCopy:function(val,oldVal){
bus。 $ emit('selected-changed',val);
}
},
data:function(){
return {
entriesCopy:[],
selectedCopy:[]
}
}
});

新Vue({
el:'#app',
数据:{
条目:[],
选择:[],
插件:[],
频率:[],
paymentConfig:{
advance:25,
firstweek:25,
ondeveryvery:50,
},
周:12,
},
组件:{
'instryComponent':entriesComponent,
},
created:function(){
//将此存储与Vue.set一起使用
var temp = this;
bus。$ on('selected-changed',function(selected){
// vm 。$ set deprecated
Vue.set(temp,'selected',selected);
});
},
计算:{
totalAdvance:function() {
return(this.paymentConfig.advance * this.total)/ 100;
},
totalFirstWeek:{
get:function(){
return(this .paymentConfig.firstweek * this.total)/ 100;
},
},
onDeliveryPayment:{
get:function(){
return(this.paymentConfig.ondelivery * this.total)/(this.weeks * 100);
}
},
总计:{
get:function(){
var sum = 0;
var weeks = this.weeks;
this.selected.forEach(function(item){
sum + = weeks * item.itemPrice * item.quantity;
});
console.log(sum);
返还金额;
}
}
}
});


解决方案

不得不重构很多,试图密切关注到你想要建立购物车的方式。然而,就构建数据的方式而言,它确实需要重新思考:



这里是小提琴: https://jsfiddle.net/thebigsurf/w8vfb64L/11/



编辑



要允许更新产品对象上的多个字段,我认为您目前不能使用v-model。因此,不是在组件上设置v模型,而是传递一种方法,您可以通过以下方式更新任何项目字段:



小提琴: https://jsfiddle.net/thebigsurf/0chtzwjd/2/



< pre class =snippet-code-js lang-js prettyprint-override> var entriesComponent = Vue.component('entries',{template:'#entries',props:{item:Object,itemKey :String,selected:Boolean,updateSelected:Function,updateItem:Function,},data(){return {quantity:0,message:'',}},created(){this.quantity = this.item.quantity this。 message = this.item.message},watch:{quantity(){this.updateItem(this.itemKey,'quantity' ,this.quantity)},message(){this.updateItem(this.itemKey,'message',this.message)},}}); new Vue({el:'#app',data:{allProducts:{ 'foo':{shareSize:'小',数量:'1',itemPrice:'24',消息:''},'bar':{shareSize:'中',数量:'1',itemPrice:'35 ',message:''},'baz':{shareSize:'Large',数量:'1',itemPrice:'46',消息:'hello'},'bop':{shareSize:'Large',数量:'1',itemPrice:'46',消息:''},},items:['foo','bar','baz'],插件:['bop'],已选中:{},paymentConfig: {advance:25,firstweek:25,ondeveryvery:50,},weeks:12,},components:{entriesComponent,},created(){this.setSelectableItems()},computed:{ totalAdvance(){return(this.paymentConfig.advance * this.total)/ 100},totalFirstWeek(){return(this.paymentConfig.firstweek * this.total)/ 100},onDeliveryPayment(){return(this.paymentConfig。 ondelivery * this.total)/(this.weeks * 100)},total(){var sum = 0 Object.keys(this.selected).forEach((productKey)=> {if(this.selected [productKey]){sum + = this.weeks * this.allProducts [productKey] .itemPrice * this.allProducts [productKey] .quantity}})return sum},},methods:{setSelectableItems() {this.items .forEach((productKey)=> {Vue.set(this.selected,productKey,false)})this.addons .forEach((productKey)=> {Vue.set(this.selected,productKey ,false)})},setSelected(productKey,value){this.selected [productKey] = value},syncItem(key,field,value){this.allProducts [key] [field] = value},},}) ;

.row {background:#f1f1f1;填充:25px; margin-top:10px;}。row:nth-​​child(even){background:#f9f9f9;}。item {background:#dcdcdc; border:1px solid#a2a2a2;填充:10px; margin-top:10px;}。item span {margin-left:10px;}。item input {display:inline-block;}

 < script src =https://unpkg.com/vue/dist/vue.js>< / script>< div id = 应用程序 > < div class =row> < label class =control-label required> Items< / label> < entries v-for =itemKey in items:update-item =syncItem:item =allProducts [productKey]:item-key =productKey:update-selected =setSelected:selected =selected [的ProductKey]>< /条目> < / DIV> < div class =row> < label class =control-label required> Addons< / label> < entries v-for =productKey in addons:update-item =syncItem:item =allProducts [productKey]:item-key =productKey:update-selected =setSelected:selected =selected [的ProductKey]>< /条目> < / DIV> < div class =row> <标签>总结< /标签> < div class =itemv-for =(value,productKey)在所选的v-if =值中> < span> {{allProducts [productKey] .shareSize}}< / span> <跨度> {{allProducts [productKey] .quantity}} x $ {{(allProducts [productKey] .itemPrice * allProducts [productKey] .quantity).toFixed(2)}}< / span> < span> {{allProducts [productKey] .message}}< / span> < / DIV> < / DIV> < div class =row> < label>付款计划< / label> < p> {{'$'+ totalAdvance.toFixed(2)}}  -  advance< / p> < p> {{'$'+ totalFirstWeek.toFixed(2)}}  - 第一周< / p> < p> {{'$'+ onDeliveryPayment.toFixed(2)}} /周订阅{{week}}周的每一周< / p> < / DIV> < div class =row> < p为H. <跨度个总< /跨度> < span> {{'$'+ total.toFixed(2)}}< / span> < / p为H. < p为H. < span>现在总计< / span> < span> {{'$'+ totalAdvance.toFixed(2)}}< / span> < / p为H. < / div>< / div><! - 组件模板 - >< template id =entries> < div class =item> < input type =checkboxv-bind:value =selected@ change =updateSelected(itemKey,$ event.target.checked)> < span>尺寸:{{item.shareSize}}< / span> < span>价格:{{'$'+ Number(item.itemPrice).toFixed(2)}}< / span> < input type =numberv-model =quantity/> < input type =textv-model =message/> < / div>< / template>  


I am trying to build the checkout page of a e-commerce type of app. On the checkout I have a list of OrderItems coming from the database, each with price, quantity. You can pick and combine these. In addition I have to render somewhere else a list with "addons" to your basket. This are also OrderItems (same properties) but have a different type.

I have a Vue.js component for rendering an array of OrderItems from which you can pick. The way I though of this is rendering the same component twice. However the "selected" property takes models either form one list or the other, but not from both at the same time. I would like the selected prop to hold items from both lists (simple orderItems and addons)

The fiddle: https://jsfiddle.net/w8vfb64L/

The code:

Template:

<section class="content">
  <div class="row" id="app">
    <div class="col-md-8">
      <div class="box box-primary">
        <div class="box-body">
          <div class="row">
            <div class="col-md-12">
              <div class="form-group">
                <label class="control-label required">Items</label>
                <div class="col-md-12">
<entries :entries="{ 0 : { shareSize : 'Small', quantity : '1', itemPrice : '24', frequency : '' }, 1 : { shareSize : 'Medium', quantity : '1', itemPrice : '35', frequency : '' }, 2 : { shareSize : 'Large', quantity : '1', itemPrice : '46', frequency : '' } }"
                  :selected="selected"></entries>
                  </div>
                  <div class="col-md-12">
                  <label class="control-label required">Addons</label>
                  <entries :entries="{ 0 : { shareSize : 'Large', quantity : '1', itemPrice : '46', frequency : '' } }" :selected="selected"></entries>

                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="col-md-4">
        <div class="box box-info">
          <div class="box-body" style="padding:15px;">
            <div class="container-fluid">
              <div class="form-group">
                <div class="control-label">
                  <label>Summary</label>
                </div>
                <div class="form-control" v-for="item in selected">
                  <span class="pull-left small-box-footer">{{ item.shareSize }}</span>
                  <span class="pull-right">{{ item.quantity + ' x $ ' + (item.itemPrice*item.quantity).toFixed(2)}}</span>
                </div>
                <div class="control-label">
                  <label>Payment plan</label>
                </div>
                <div class="col-md-12">
                  {{ '$ ' + totalAdvance.toFixed(2) }} - advance
                </div>
                <div class="col-md-12">
                  {{ '$ ' + totalFirstWeek.toFixed(2) }} - first week
                </div>
                <div class="col-md-12">
                  {{ '$ ' + onDeliveryPayment.toFixed(2) }}/ week on each of the {{ weeks }} weeks of the subscription
                </div>
                <div class="col-md-12 row">
                  <div class="control-label"><strong><span class="pull-left">Total</span><span class="pull-right">{{ '$ ' + total.toFixed(2) }}</span></strong></div>
                </div>
                <div class="col-md-12 row">
                  <div class="title"><strong><span class="pull-left">Total due now</span><span class="pull-right">{{ '$ ' + totalAdvance.toFixed(2) }}</span></strong></div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

<!-- component template -->
<template id="entries">
  <div class="col-md-12">
    <div class="form-group" v-for="(entry, key) in entriesCopy" v-bind:entry="entry">
      <div class="form-group col-md-12">
        <div class="col-md-12">
          <div class="col-md-4">
            <input type="checkbox" v-bind:value="entry" v-model="selectedCopy">
          </div>
          <div class="col-md-4">{{entry.shareSize}}</div>
          <div class="col-md-4">{{'$ ' + Number(entry.itemPrice).toFixed(2) }}</div>
        </div>
        <div class="form-group col-md-12">
          <div class="col-md-6">
            <input type="number" v-model="entry.quantity" :value="entry.quantity" />
          </div>
        </div>

      </div>
    </div>
  </div>
</template>

Javascript:

var bus = new Vue();

var entriesComponent = Vue.component('entries', {
  template: '#entries',
  props: {
    entries: [Array, Object],
    selected: Array,
    addons: Array,
    frequencies: [Array, Object],
  },
  created: function() {
    this.entriesCopy = this.entries;
    this.selectedCopy = this.selected;
  },
  watch: {
    selectedCopy: function(val, oldVal) {
      bus.$emit('selected-changed', val);
    }
  },
  data: function() {
    return {
      entriesCopy: [],
      selectedCopy: [] 
    }
  }
});

new Vue({
  el: '#app',
  data: {
    entries: [],
    selected: [],
    addons: [],
    frequencies: [],
    paymentConfig: {
      advance: 25,
      firstweek: 25,
      ondelivery: 50,
    },
    weeks: 12,
  },
  components: {
    'entriesComponent': entriesComponent,
  },
  created: function() {
    // store this to use with Vue.set
    var temp = this;
    bus.$on('selected-changed', function(selected) {
      // vm.$set deprecated
      Vue.set(temp, 'selected', selected);
    });
  },
  computed: {
    totalAdvance: function() {
      return (this.paymentConfig.advance * this.total) / 100;
    },
    totalFirstWeek: {
      get: function() {
        return (this.paymentConfig.firstweek * this.total) / 100;
      },
    },
    onDeliveryPayment: {
      get: function() {
        return (this.paymentConfig.ondelivery * this.total) / (this.weeks * 100);
      }
    },
    total: {
      get: function() {
        var sum = 0;
        var weeks = this.weeks;
        this.selected.forEach(function(item) {
          sum += weeks * item.itemPrice * item.quantity;
        });
        console.log(sum);
        return sum;
      }
    }
  }
});

解决方案

Had to refactor quite a lot, tried to stick closely to the way in which you wanted to build the cart. However it really required a bit of rethinking in terms of how you would structure your data:

here's the fiddle: https://jsfiddle.net/thebigsurf/w8vfb64L/11/

EDIT

To allow updating more than 1 field on the product object I don't believe you can currently use v-model for this. Therefore instead of setting a v-model on the component pass it a method that you can update any of the items fields via:

fiddle: https://jsfiddle.net/thebigsurf/0chtzwjd/2/

var entriesComponent = Vue.component('entries', {

    template: '#entries',
    
    props: {
        item: Object,
        itemKey: String,
        selected: Boolean,
        updateSelected: Function,
        updateItem: Function,
    },
    
    data () {
    	return {
        	quantity: 0,
            message: '',
        }
    },
    
    created () {
    	this.quantity = this.item.quantity
        this.message = this.item.message
    },
    
    watch: {
    	quantity () {
        	this.updateItem(this.itemKey, 'quantity', this.quantity)
        },
    	message () {
        	this.updateItem(this.itemKey, 'message', this.message)
        },
    }
    
});

new Vue({

    el: '#app',
    
    data: {
    	allProducts: {
        	'foo': { shareSize: 'Small', quantity: '1', itemPrice: '24', message: '' }, 
            'bar': { shareSize: 'Medium', quantity: '1', itemPrice: '35', message: '' }, 
            'baz': { shareSize: 'Large', quantity: '1', itemPrice: '46', message: 'hello' },
        	'bop': { shareSize: 'Large', quantity: '1', itemPrice: '46', message: '' },
        },
    	items: [ 'foo', 'bar', 'baz' ],
        addons: [ 'bop' ],
        selected: {},
        paymentConfig: {
            advance: 25,
            firstweek: 25,
            ondelivery: 50,
        },
        weeks: 12,
    },
    
    components: {
        entriesComponent,
    },
    
    created () {
        this.setSelectableItems()
    },
    
    computed: {
    
        totalAdvance () {
            return (this.paymentConfig.advance * this.total) / 100
        },
        
        totalFirstWeek () {
            return (this.paymentConfig.firstweek * this.total) / 100
        },
        
        onDeliveryPayment () {        
            return (this.paymentConfig.ondelivery * this.total) / (this.weeks * 100)
        },
        
        total() {
        
            var sum = 0
            
            Object.keys(this.selected)
                .forEach((productKey) => {
                	if (this.selected[productKey]) {
                        sum += 
                            this.weeks * 
                            this.allProducts[productKey].itemPrice * 
                            this.allProducts[productKey].quantity
                    }
                })
            
            return sum
            
        },
        
    },

	methods: {
    
    	setSelectableItems () {
        
        	this.items
                .forEach((productKey) => {
                    Vue.set(this.selected, productKey, false)
                })
                
            this.addons
                .forEach((productKey) => {
                    Vue.set(this.selected, productKey, false)
                })
        
        },
    
    	setSelected (productKey, value) {
        
        	this.selected[productKey] = value
        
        },
        
        syncItem (key, field, value) {
        
        	this.allProducts[key][field] = value
        
        },
    
    },
    
});

.row {
    background: #f1f1f1;
    padding: 25px;
    margin-top: 10px;
}

.row:nth-child(even) {
    background: #f9f9f9;
}

.item {
    background: #dcdcdc;
    border: 1px solid #a2a2a2;
    padding: 10px;
    margin-top: 10px;
}

.item span {
    margin-left: 10px;
}

.item input {
    display: inline-block;
}

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">

    <div class="row">
        <label class="control-label required">Items</label>
        <entries 
            v-for="productKey in items"
            :update-item="syncItem"        
            :item="allProducts[productKey]"
            :item-key="productKey"
            :update-selected="setSelected"
            :selected="selected[productKey]"></entries>
    </div>

    <div class="row">
        <label class="control-label required">Addons</label>
        <entries 
            v-for="productKey in addons"
            :update-item="syncItem"    
            :item="allProducts[productKey]"
            :item-key="productKey"
            :update-selected="setSelected"
            :selected="selected[productKey]"></entries>
    </div>
    
    <div class="row">
        <label>Summary</label>
        <div class="item" v-for="(value, productKey) in selected" v-if="value">
            <span>{{ allProducts[productKey].shareSize }}</span>
            <span>
                {{ allProducts[productKey].quantity }}
                 x $
                {{ (allProducts[productKey].itemPrice * allProducts[productKey].quantity).toFixed(2)}}
            </span>
            <span>{{ allProducts[productKey].message }}</span>
        </div>
    </div>
    
    <div class="row">
        <label>Payment plan</label>
        <p>{{ '$ ' + totalAdvance.toFixed(2) }} - advance</p>
        <p>{{ '$ ' + totalFirstWeek.toFixed(2) }} - first week</p>
        <p>{{ '$ ' + onDeliveryPayment.toFixed(2) }}/ week on each of the {{ weeks }} weeks of the subscription</p>
    </div>

    <div class="row">
        <p>
            <span>Total</span>
            <span>{{ '$ ' + total.toFixed(2) }}</span>
        </p>

        <p>
            <span>Total due now</span>
            <span>{{ '$ ' + totalAdvance.toFixed(2) }}</span>
        </p>
    </div>

</div>


<!-- component template -->
<template id="entries">

    <div class="item">

        <input 
            type="checkbox"
            v-bind:value="selected"
            @change="updateSelected(itemKey, $event.target.checked)">
        
        <span>size: {{item.shareSize}}</span>
        
        <span>price: {{'$ ' + Number(item.itemPrice).toFixed(2) }}</span>
        
        <input type="number" v-model="quantity" />
        
        <input type="text" v-model="message" />

    </div>

</template>

这篇关于Vue js结合了两个组件的元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆