vue2 list of form elements - javascript

I have a bunch of fields that I want to display as a long form, with each field allowing the user to select from a list of values for that field.
So the basic thing looks like this:
Vue.component('master-field', require('./Vue/MasterField').default);
new Vue({
store,
el: '#app',
computed: {
sourceFields: function () {
return store.getters.visibleSource
}
}
})
then in the page, it does this to spit out the fields one by one, using the sourceFields data:
<div id="app">
<div class="container-fluid">
<master-field v-for="item in sourceFields" :key="item.id"></master-field>
</div>
</div>
How can I access the item.id from within the master-field component? If I try to do a console.log(item.id) from the mounted hook in the component it says item is not defined.

You have to pass it via props. Props Documentation
pseudo example:
<my-component :info="someInfo" />
then in the component:
Vue.component('myComponent', {
// declare the props
props: ['info'],
template: '<span>{{ info }}</span>'
})

Related

vue.js and slot in attribute

I'm trying to build a vue.js template that implements following:
<MyComponent></MyComponent> generates <div class="a"></div>
<MyComponent>b</MyComponent> generates <div class="a" data-text="b"></div>.
Is such a thing possible?
EDIT
Here is the best I can reach:
props: {
text: {
type: [Boolean, String],
default: false
}
},
and template
<template>
<div :class="classes()" :data-text="text">
<slot v-bind:text="text"></slot>
</div>
</template>
but the binding does not work, text always contains false.
You can use the mounted() method to get text using $slot.default property of the component to get the enclosing text. Create a text field in data and update inside mounted() method like this :
Vue.component('mycomponent', {
data: () => ({
text: ""
}),
template: '<div class="a" :data-text=text></div>',
mounted(){
let slot = this.$slots.default[0];
this.text=slot.text;
}
});
Note: It will only work for text, not for Html tags or components.
You're mixing slots and properties here. You'll have to pass whatever you want to end up as your data-text attribute as a prop to your component.
<MyComponent text="'b'"></MyComponent>
And in your template you can remove the slot
<template>
<div :class="classes()" :data-text="text"></div>
</template>
Another thing: it looks like your binding your classes via a method. This could be done via computed properties, take a look if you're not familiar.
You can try this.
<template>
<div :class="classes()">
<slot name="body" v-bind:text="text" v-if="hasDefaultSlot">
</slot>
</div>
</template>
computed: {
hasDefaultSlot() {
console.log(this)
return this.$scopedSlots.hasOwnProperty("body");
},
}
Calling
<MyComponent>
<template v-slot:body="props">
b
</template>
</MyComponent>

Vue 2.0 - How passing function to child component?

I have one issue. I want to pass function link to the child component. It's working but in HTML I get that code. It's correct how improve it?
I have Vue instance
app = new Vue({
... some code
data: {
onAppClose: null,
onAppSend: null
}
})
I want to add from global window any function. Or register function in Vue instance
app.onSend = () => console.log('data')
And pass this function to child
<div id="app">
<dynamsoft-component v-if="displayComponent"
:docs="docs"
:onAppSend="onSend"
:onAppClose="onClose"
></dynamsoft-component>
</div>
But I get this HTML template in console
<div id="app">
<div onappsend="()=>{}" onappclose="function (data) {
console.warn('dwdawad')
console.log('data')
}"></div>
</div>
You example code is not making a lot of sense - do you want to add a listener not a div or pass a function to a child component?`
I assume the latter. Vue has custom events for that .
Parent template:
<div v-on:appsend="someMethod" v-on:appclose="someOtherMethod"></div>
Parent component methods:
methods: {
someOtherMethod: function (data) {
console.warn('dwdawad')
console.log('data')
},
// ...
}
And then emit form the child:
this.$emit('appclose', {id: 'whatever'} /*pass data here*/)
Edit:
I still don't see how those functions would end up directly in the template, but the real problem is: HTML is not case-sensitive. so :onAppSend becomes :onappsend. You have to use kebap-case: :on-app-send. Vue will convert it to onAppSend in the component.
I have never used Vue.js before now..
But having a look at the how to on their site, this seems to work
In Vue style guide have recommendations about props naming
https://v2.vuejs.org/v2/style-guide/#Prop-name-casing-strongly-recommended
Vue.component('dynamsoft-component', {
props: ['onAppSend'],
template: '<button v-on:click="buttonclick">click me</button>',
methods: {
buttonclick(e){
// Check if onAppSend is defined.
if(Boolean(this.onAppSend)){
this.onAppSend();
}
}
}
})
new Vue({
el: '#app',
methods: {
onSend: function(){
console.log('child clicked');
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<dynamsoft-component :on-app-send="onSend"></dynamsoft-component>
</div>

how to share data between components in VUE js (while creating list)

Could you please tell me how to share data between components in VUE js (while creating list).I have two components list components and add todo component.I want to add items in list when user click on add button.But issue is input field present in different component and list is present in different component
here is my code
https://plnkr.co/edit/bjsVWU6lrWdp2a2CjamQ?p=preview
// Code goes here
var MyComponent = Vue.extend({
template: '#todo-template',
props: ['items']
});
var AddTODO = Vue.extend({
template: '#add-todo',
props: ['m'],
data: function () {
return {
message: ''
}
},
methods: {
addTodo: function () {
console.log(this.message)
console.log(this.m);
//this.m =this.message;
},
},
});
Vue.component('my-component', MyComponent);
Vue.component('add-todo', AddTODO)
var app = new Vue({
el: '#App',
data: {
message: '',
items: []
},
});
The whole point of having a great MVVM framework is to let you have a view-model: a central store of all the state in your page/app/whatever. Components can emit events. You can have an event bus. But if you can save the day with a simple, global variable containing all your state, this is by far the cleanest, best solution. So just put your to-dos in an array, in a variable in global scope, then declare them in the data of every component that needs them. Here it is working in Plunkr.
markup
<div id="App" >
<add-todo></add-todo>
<my-component></my-component>
</div>
<template id="add-todo">
<div>
<input type="text" v-model="message">
<button #click="addTodo">Add todo</button>
</div>
</template>
<template id="todo-template">
<div>
<ul >
<li v-for="(item,index) in store.items">
{{item.message}}
</li>
</ul>
</div>
</template>
<script src="vue.js"></script>
<script src="script.js"></script>
code
// This is the magic store. This is all you need.
var vueStore = {items : []};
var MyComponent = Vue.extend({
template: '#todo-template',
data : function(){return {store : vueStore}}
});
var AddTODO = Vue.extend({
template: '#add-todo',
data: function () {
return {
message: '',
store : vueStore
}
},
methods: {
addTodo: function (event) {
this.store.items.push({'message' : this.message})
},
},
});
Vue.component('my-component', MyComponent);
Vue.component('add-todo', AddTODO)
var app = new Vue({
el: '#App',
data: {
store : vueStore
},
});
This is not a savage hack! We're being called to stop thinking about events, move up the food chain, and think about reactive pipes. Components don't care when or by who the central store gets updated. Vue takes care of it.
Here's the page on state management.
So you could use events and emit the created todo to the root vue instance.
I edited / forked your plunkr (I'm rather the fiddle type).
https://plnkr.co/edit/bnMiDmi30vsj3a8uROBK?p=preview
So I edited this line here, which listens for a custom event added and pushes the first argument to items.
<add-todo v-on:added='items.push(arguments[0])'></add-todo>
And also these lines, which emit the event. And i changed from the property m to the data message, because you shouldnt mutate props:
<input type="text" v-model="message">
<button #click="$emit('added', message)">Add todo</button>

Accessing and filtering child components defined in a Vue.js component's <slot>

I'm in the process of implementing a combined select dropdown and searchbox component for a UI library I'm writing, using Vue.js to drive interactions, but I'm running into a problem with how Vue.js seems to handle communication between parent and child components.
Basically, what I'm aiming to achieve is to allow users of the library to define search-select elements in HTML using something like this:
<search-select>
<search-option value="foo">Foo</search-option>
<search-option value="bar">Bar</search-option>
<search-option value="baz">Baz</search-option>
<search-option value="foobar">Foobar</search-option>
</search-select>
My template for the search-select component looks like this:
<template>
<div class="search-select">
<div class="placeholder" ref="placeholder"></div>
<ul class="dropdown">
<input
type="text"
placeholder="Type to filter..."
v-on:input="updateFilter"
>
<slot></slot>
</ul>
</div>
</template>
The idea is that the search-select component will add a text input box above the specified inputs, which the user can type in to filter the options that can be selected. The finished component would look something like this.
However, from what I can tell in the documentation, Vue.js doesn't provide any direct way for me to access the properties of the child components from within the parent, nor any way to listen for click events or similar from children within a parent's <slot>.
This means that I don't have a way to filter visibility of the children based on the user's input, or to update the value of the search-select component when a user clicks on one of the children.
The Vue.js documentation mentions ways to pass events from child components to parents, but none seem applicable to elements defined within slots - it appears that parents can only listen for events from components explicitly defined within them.
How would one implement the type of two-way communication between parent and child components required for this use case, without violating any Vue.js best practices related to sharing information between components?
You can use event bus to solve this problem. Just keep every option listening to input event, then letting it decide whether hide itself or not depending on the argument passed.
See fiddle or demo below.
const bus = new Vue();
Vue.component('search-option', {
template: `
<option v-if="show" :value="this.$attrs.value"><slot></slot></option>
`,
created() {
bus.$on('filter', (input) => {
this.show = this.$attrs.value.includes(input);
});
},
beforeDestory() {
bus.$off('filter');
},
data() {
return {
show: true,
};
},
});
Vue.component('search-select', {
template: `
<div class="search-select">
<div class="placeholder" ref="placeholder"></div>
<ul class="dropdown">
<input
type="text"
placeholder="Type to filter..."
v-on:input="updateFilter"
v-model="myinput"
>
<slot></slot>
</ul>
</div>
`,
methods: {
updateFilter() {
console.log('update filter');
bus.$emit('filter', this.myinput);
},
},
data() {
return {
myinput: undefined,
};
},
});
Vue.component('parent', {
template: `
<div class="parent">
<search-select>
<search-option value="foo">Foo</search-option>
<search-option value="bar">Bar</search-option>
<search-option value="baz">Baz</search-option>
<search-option value="foobar">Foobar</search-option>
</search-select>
</div>
`,
});
new Vue({
el: '#app',
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app">
<parent></parent>
</div>

Parent onChange sets child property to true

Form (parent) component:
export default {
template: `
<form name="form" class="c form" v-on:change="onChange">
<slot></slot>
</form>`,
methods: {
onChange:function(){
console.log("something changed");
}
}
};
and a c-tool-bar component (child) that is (if existing) inside the c-form's slot
export default {
template: `
<div class="c tool bar m006">
<div v-if="changed">
{{ changedHint }}
</div>
<!-- some other irrelevant stuff -->
</div>`,
props: {
changed: {
type: Boolean,
default: false,
},
changedHint: String
}
};
What I want to achieve is, that when onChange of c-form gets fired the function
checks if the child c-tool-bar is present && it has a changedHint text declared (from backend) it should change the c-tool-bar's prop "changed" to true and the c-bar-tool updates itself so the v-if="changed" actually displays the changedHint. I read the vue.js docs but I don't really know where to start and what the right approach would be.
You can use the two components like this:
<parent-component></parent-component>
You will be passing in the :changed prop a variable defined in the parent component in data(). Note the :, we are using dynamic props, so once the prop value is changed in the parent it will update the child as well.
To change the display of the hint, change the value of the variable being passed in as prop to the child component:
export default {
template: `
<form name="form" class="c form" v-on:change="onChange">
<child-component :changed="shouldDisplayHint"></child-component>
</form>`,
data() {
return {
shouldDisplayHint: false,
};
},
methods: {
onChange:function(){
this.shouldDisplayHint = true; // change the value accordingly
console.log("something changed");
}
}
};
One simple way is to use $refs.
When you will create your child component inside your parent component, you should have something like: <c-tool-bar ref="child"> and then you can use it in your parent component code:
methods: {
onChange:function(){
this.$refs.child.(do whatever you want)
}
}
Hope it helps but in case of any more questions: https://v2.vuejs.org/v2/guide/components.html#Child-Component-Refs

Categories

Resources