In vuejs.org, there said:
The two-way binding will sync the change of child’s msg property back to the parent’s parentMsg property. (Here is the link.)
But I'm confused that how could I change child's property so that this change can be synced back to its parent?
router
// Define rule of router.
router.map({
'/categories': {
// The List.vue
component: CategoryList,
subRoutes: {
// ... some rules ...
'/add': {
// The DetailAdd.vue
component: CategoryDetailAdd
}
}
}
});
List.vue (the parent)
<template>
<tab v-bind:tabs="tabs" :active="active"></tab>
<div class="col-lg-12">
<router-view :categories="categories"></router-view>
</div>
</template>
<script>
var Tab = require('../common/Tab.vue');
export default{
components:{
tab: Tab
},
data() {
return {
categories: [],
tabs: [],
active: '1'
};
},
ready() {
this.$http.get('/categories').then((response) => {
// success
this.$set('categories', response.data.categories);
this.$set('tabs', response.data.tabs);
this.$set('active', response.data.active);
}, (response) => {
// error
})
}
}
</script>
DetailAdd.vue (the child)
<template>
<form class="form-horizontal" method="post" action="/categories/add">
<div class="form-group">
<label for="name" class="col-md-2 control-label">name</label>
<div class="col-md-10">
<input id="name" type="text" class="form-control" name="name" value="" />
</div>
</div>
<div class="form-group">
<label for="category_id" class="col-md-2 control-label">superiror</label>
<formselect></formselect>
</div>
<div class="form-group">
<label for="sort_order" class="col-md-2 control-label">sort</label>
<div class="col-md-10">
<input id="name" type="text" class="form-control" name="sort_order" value="" />
</div>
</div>
<formbutton></formbutton>
</form>
</template>
<script>
var FormSelect = require('../common/FormSelect.vue');
var FormButton = require('../common/FormButton.vue');
export default{
components: {
formselect: FormSelect,
formbutton: FormButton
}
}
$(function() {
$('.nav-tabs').on('ready', function() {
$('.nav-tabs li').attr('class', '');
$('.nav-tabs li:last').attr('class', 'active');
});
});
</script>
I just want to mutate the active property in parent (List.vue), how to achieve this?
Thank all of you!
The two-way binding works as you might think it does: when you change a property in the parent, it gets changed in the children, and vice versa. Take a look at this as an example: https://jsfiddle.net/u0mmcyhk/1/, the children is able to change the state of the parent. If you remove .sync from the parent template, it stops working.
Having said that, .sync will be deprecated on 2.0, in favour of communication (broadcast, dispatch) or some state management like vuex.
More information: https://vuejs.org/api/#v-bind
Related
See this images
i dont want to display here those elements
See the Below code sample
sample.blade.php
<create-form id= "create_form" title="Sample Form" >
<div class="col-md-6">
<label class="form-label" for="multicol-username">Username</label>
<input type="text" id="multicol-username" class="form-control" placeholder="john.doe" name="ma_user_id" data-type="varchar" required>
</div>
<div class="col-md-6">
<label class="form-label" for="multicol-email">Email</label>
<div class="input-group input-group-merge">
<input type="text" id="multicol-email" class="form-control" placeholder="john.doe" aria-label="john.doe" aria-describedby="multicol-email2" name="password" data-editmode="false" data-editname="email">
<span class="input-group-text" id="multicol-email2">#example.com</span>
</div>
</div>
</create-form>
createForm.vue
<template>
<div class="card mb-4" v-show="showForm">
<h5 class="card-header">{{title}}</h5>
<form class="card-body" id="createForm" enctype="multipart/form-data" ref="createForm">
<div class="row g-3">
<slot></slot>
<div class="pt-4">
<button type="submit" class="btn btn-primary me-sm-3 me-1" id="save_return">Submit</button>
<button type="reset" class="btn btn-label-secondary" #click="hideForm">Cancel</button>
</div>
</div>
</form>
</div>
</template>
<script>
export default {
props: ['title'],
setup() {},
data() {
return {
}
},
created() {
},
mounted() {
},
methods: {
},
}
</script>
app.js
require('./bootstrap')
import { createApp } from 'vue'
import createForm from './components/createForm';
const app = createApp({})
app.component('create-form', createForm);
app.mount('#app')
It sounds like you want the template that's rendered by the php backend to not be visible until vue is able to handle it, though I'm not 100% sure that's the case.
If that is the case, you could use the v-cloak directive
This directive is only needed in no-build-step setups.
When using in-DOM templates, there can be a "flash of un-compiled templates": the user may see raw mustache tags until the mounted component replaces them with rendered content.
v-cloak will remain on the element until the associated component instance is mounted. Combined with CSS rules such as [v-cloak] { display: none }, it can be used to hide the raw templates until the component is ready.
<create-form id="create_form" title="Sample Form" v-cloak>
...
<style>
[v-cloak] { display: none }
</style>
I have a custom component called HobbyForm which is a simple form with two controls, a checkbox and an input, this component is being called from a parent component called Content, along with other similar 'form' components.
<template>
<form>
<div class="row align-items-center">
<div class="col-1">
<Checkbox id="isHobbyActive" :binary="true" v-model="isActive"/>
</div>
<div class="col-5">
<InputText id="hobby" placeholder="Hobby" type="text" autocomplete="off" v-model="hobby"/>
</div>
</div>
</form>
</template>
<script>
export default {
name: 'HobbyForm',
data() {
return {
hobby: {
isActive: false,
hobby: null
}
}
},
}
</script>
My Content component is something like:
<template>
<language-form></language-form>
<hobby-form v-for="(hobbie, index) in hobbies" :key="index" v-bind="hobbies[index]"></hobby-form>
<Button label="Add Hobby" #click="addHobby"></Button>
</template>
<script>
export default {
name: "Content",
components: {
LanguageForm,
HobbyForm
},
data() {
return {
language: '',
hobbies: [
{
isActive: false,
hobby: null
}
]
};
},
methods: {
addHobby() {
this.hobbies.push({
isActive: false,
hobby: null
});
}
},
};
</script>
The idea is to be able to add more instances of the HobbyForm component to add another hobby record to my hobby data property; but I don't know how to keep track of these values from my parent without using an emit in my child components, since I don't want to manually trigger the emit, I just want to have the data updated in my parent component.
How should I access my child component's data from my parent and add it to my array?
In the current form passing parent data into a child component via v-bind="hobbies[index]" makes no sense as the child component (HobbyForm) has no props so it does not receive any data from the parent...
To make it work:
Remove data() from the child HobbyForm
Instead declare a prop of type Object
Bind form items to the properties of that Object
Pass the object into each HobbyForm
<template>
<form>
<div class="row align-items-center">
<div class="col-1">
<Checkbox id="isHobbyActive" :binary="true" v-model="hobby.isActive"/>
</div>
<div class="col-5">
<InputText id="hobby" placeholder="Hobby" type="text" autocomplete="off" v-model="hobby.hobby"/>
</div>
</div>
</form>
</template>
<script>
export default {
name: 'HobbyForm',
props: {
hobby: {
type: Object,
required: true
}
}
}
</script>
Even tho props are designed to be one way only so child should not mutate prop value, this is something else as you do not mutate prop value, you are changing (via a v-model) the properties of the object passed via a prop (see the note at the bottom of One-Way Data Flow paragraph)
Also change the parent to:
<hobby-form v-for="(hobby, index) in hobbies" :key="index" v-bind:hobby="hobby"></hobby-form>
Demo:
const app = Vue.createApp({
data() {
return {
hobbies: [{
isActive: false,
hobby: null
}]
};
},
methods: {
addHobby() {
this.hobbies.push({
isActive: false,
hobby: null
});
}
},
})
app.component('hobby-form', {
props: {
hobby: {
type: Object,
required: true
}
},
template: `
<form>
<div class="row align-items-center">
<div class="col-1">
<input type="checkbox" id="isHobbyActive" v-model="hobby.isActive"/>
</div>
<div class="col-5">
<input type="text" id="hobby" placeholder="Hobby" autocomplete="off" v-model="hobby.hobby"/>
</div>
</div>
</form>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.1.5/dist/vue.global.js"></script>
<div id='app'>
<hobby-form v-for="(hobby, index) in hobbies" :key="index" v-bind:hobby="hobby"></hobby-form>
<button #click="addHobby">Add Hobby</button>
<hr/>
<pre> {{ hobbies }} </pre>
</div>
Hi everyone i have a big trouble i need to modify the value of checkbox from parent to another parent to child , is not this difficulty i know but i'm at the start and after spent 2 days for this i can't try anything else , now i'll add all the code the child is named "UICheckbox" and i pass it to "ToDoListItem" and this last is passed to "ToDoList" , and every Todos is an in an array into "store.js"
UICheckbox
<template>
<div class="checkbox-container">
<input
type="checkbox"
:id="id"
:v-model="localTodo"
>
<label :for="id">
<p class="font-bold">{{ text }}</p>
</label>
</div>
</template>
<script>
export default {
props: [
"id",
"value",
"text"
],
data() {
return {
localTodo: this.value
}
},
watch: {
localTodo:{
handler(newVal, oldVal) {
this.$emit("change", newVal)
},
deep: true,
}
}
}
</script>
<style>
</style>
ToDoListItem
<template>
<div class="flex flex-row w-full my-2">
<UICheckbox
:v-model="localTodo.checked"
:id="todo.id"
:text="todo.text"
#change="localTodo.cheked = $event"
/>
<delete-button :todo="todo" />
</div>
</template>
ToDoList
<to-do-list-item
:todo="todo"
#click="changeChecked(todo)"
#change="todo.checked = $event"
/>
I'm trying to make a Mixin with which I'd like to calculate the offsetWidth of an element.
This is my Mixin:
export const boxWidth = (selector) => ({
mounted() {
selector.addEventListener(
'resize',
this.setBoxWidth
);
},
methods: {
setBoxWidth(e) {
let box = e.target;
this.myBoxWidth = box.offsetWidth;
console.log(this.myBoxWidth);
}
}
})
I trying to call this in a component:
<template>
<div
ref="visuraBox"
class="container"
>
<div class="field">
<label class="label">Name</label>
<div class="control">
<input
class="input"
type="text"
placeholder="e.g Alex Smith"
>
</div>
</div>
<div class="field ">
<label class="label">Name</label>
<div class="control">
<input
class="input"
type="text"
placeholder="e.g Alex Smith"
>
</div>
</div>
</div>
</template>
<script>
import { boxWidth } from '../mixins/boxWidth'
export default {
name: 'VisuraCatForm',
mixins: [boxWidth(this.$refs.visuraBox)],
data() {
return {
myBoxWidth: 0
}
},
created() {
const myBox = this.$refs.visuraBox
this.myBoxWidth = myBox.offsetWidth;
}
}
</script>
<style lang='scss' scoped>
#import '#/assets/design/components/_form.scss';
</style>
Obviously in this way the ref doesen't exist yet, so my answer is:
How can i pass a selector inside a mixin?
(P.S. I prefer to don't declare the mixin globally)
You're almost there...
I suggest you add a method to your mixin that handles what you already have within the mounted hook of the mixin:
listenForResize(selector) {
selector.addEventListener('resize', this.setBoxWidth);
}
Then you can call this new method from the mounted hook of your original component.
I'm new to Ember.js and I'm trying to create an application that mimics Youtube by using their API. Currently I have a route that is responsible for grabbing the initial information from the Youtube Api to populate the page on load. I have a search bar component that is used to gather the input from the user and repopulate the list with results based on the string. The problem is that while I am getting the input from the user my Route model is not refreshing to grab the update data from the api. Below is my code.
Template for my video route video.hbs:
// app/templates/video.hbs
<div class="row">
{{search-bar}}
<div class="row">
<div class="col-md-12">
<hr>
<br>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="row">
{{video-list model=model}}
<div class="col-md-4 pull-right video-container">
{{#if videoId}}
<iframe id="video-player" src="https://www.youtube.com/embed/{{videoId}}"></iframe>
{{else}}
<iframe id="video-player" src="https://www.youtube.com/embed/kEpOF7vUymc"></iframe>
{{/if}}
</div>
</div>
</div>
</div>
</div>
Template for my search bar
// app/templates/components/search-bar.hbs
<div class="col-md-12 col-md-offset-4">
<form class="form-inline">
<div class="form-group" onsubmit="return false">
{{input type="text" class="form-control" value=search id="search" placeholder="Search Videos..."}}
</div>
<button type="submit" {{action "updateSearch"}}class="btn btn-success">Search</button>
</form>
</div>
Component for my search bar
// app/components/search-bar.js
import Ember from 'ember';
export default Ember.Component.extend({
userSearch: "",
actions: {
updateSearch: function() {
this.set("userSearch", this.get("search"));
this.modelFor("videos").reload();
}
}
});
Video Route
// app/routes/video.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var userSearch = this.get("search") === undefined ? "Code" : this.get("search");
this.set("search", userSearch);
var url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q="+ userSearch +"&maxResults=50&key="api key goes here";
return Ember.$.getJSON(url).then(function(data) {
return data.items.filter(function(vid) {
if(vid.id.videoId) {
return vid;
}
});
});
}
});
reload - will not call model hook method, in this case, you can send action to video route and try refresh from there.
EDIT:
Adjusting your code for your use case, Let me know if it's not working or anything wrong in this approach.
app/routes/video.js
Here we are using RSVP.hash function for returning multiple model. I am including userSearch too. Its better to implement query parameters for this use case, but I implemented it without using it.
import Ember from 'ember';
export default Ember.Route.extend({
userSearch: '',
model: function() {
var userSearch = this.get("userSearch") === undefined ? "Code" : this.get("userSearch");
var url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=" + userSearch + "&maxResults=50&key=apikey";
return Ember.RSVP.hash({
videosList: Ember.$.getJSON(url).then(function(data) {
return data.items.filter(function(vid) {
if (vid.id.videoId) {
return vid;
}
});
}),
userSearch: userSearch
});
},
actions: {
refreshRoute(userSearch) {
this.set('userSearch',userSearch);
this.refresh();
},
}
});
app/controllers/viedo.js
It contains refreshRoute function and this will call refreshRoute function available in video route file.
import Ember from 'ember';
export default Ember.Controller.extend({
actions:{
refreshRoute(userSearch){
this.sendAction('refreshRoute',userSearch);
}
}
});
app/templates/video.hbs
1. I am passing userSearch property and refreshRoute action name to search-bar component
2. Accessing videosList using model.videosList
<div class="row">
{{search-bar userSearch=model.userSearch refreshRoute="refreshRoute"}}
<div class="row">
<div class="col-md-12">
<hr>
<br>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="row">
{{video-list model=model.videosList}}
<div class="col-md-4 pull-right video-container">
{{#if videoId}}
<iframe id="video-player" src="https://www.youtube.com/embed/{{videoId}}"></iframe>
{{else}}
<iframe id="video-player" src="https://www.youtube.com/embed/kEpOF7vUymc"></iframe>
{{/if}}
</div>
</div>
</div>
</div>
</div>
app/components/search-bar.js
Here you will get userSearch property as external attributes ie. it will be passed as an argument on including the component.
import Ember from 'ember';
export default Ember.Component.extend({
userSearch:'',//external attributes
actions: {
updateSearch() {
var userSearch = this.get('userSearch');
this.sendAction('refreshRoute',userSearch); //this will call corresponding controller refreshRoute method
}
}
});
app/templates/components/search-bar.hbs
<div class="col-md-12 col-md-offset-4">
<form class="form-inline">
<div class="form-group" onsubmit="return false">
{{input type="text" class="form-control" value=userSearch id="search" placeholder="Search Videos..."}}
</div>
<button type="submit" {{action "updateSearch"}}class="btn btn-success">Search</button>
</form>
</div>