I'm not getting any errors and it's compiling, so I'm not sure what I'm doing wrong. I've searched on the subject with no success.
I have BulkExpenses.vue which pulls and shows some expense records, then BulkExpense.vue is a nested component that displays each record. I want to click the trash icon and have it emit the expense ID back to the parent component to run a delete script. I'm doing this in another project and it's working perfectly, so I'm not sure what I'm doing wrong here.
There's also a Lookup component which is a TypeAhead for pulling trips to connect to the Expense, but that's working.
BulkExpenses.vue
<template>
<div>
<form action="#" #submit.prevent="createBulkExpense()" class="publisher bt-1 border-fade bg-white" autocomplete="off">
<table>
<thead>
<tr>
<td><input v-model="bulk_name" type="text" placeholder="Name"></td>
<td><input id="bulk_expensed_at" type="text" name="bulk_expensed_at" placeholder="Date Expense Incurred"></td>
<td><input id="bulk_type_id" type="text" name="bulk_type" placeholder="Type"></td>
<td>
<lookup
source="/api/lookup/trip"
placeholder="Search your trips"
filter-key="name"
:start-at="3"
v-on:selected-link="onSelectedTrip"
:modellink="modellink">
</lookup>
</td>
<!-- <td><input id="bulk_tags" type="text" name="bulk_tags" placeholder="Tags"></td> -->
<td><input id="bulk_vendor" type="text" name="bulk_vendor" placeholder="Vendor"></td>
<td><input id="bulk_method" type="text" name="bulk_method" placeholder="Payment Method"></td>
<td><input id="bulk_total" type="text" name="bulk_total" placeholder="Total Amount"></td>
<td><input id="bulk_paidby_user_id" type="text" name="bulk_paidby" placeholder="Paid By"></td>
<td><input id="bulk_status" type="text" name="bulk_status" placeholder="Payment Status"></td>
<td><input id="bulk_notes" type="text" name="bulk_notes" placeholder="Notes"></td>
</tr>
</thead>
<tbody>
<expense v-for="expense in expenses"
:key="expense.id"
:expense="expense"
#expense-deleted="deleteExpense($event)">
</expense>
</tbody>
</table>
</form>
</div>
</template>
<script>
// import CommentsManager from './CommentsManager.vue';
var axios = require("axios");
import lookup from './Lookup.vue';
import expense from './BulkExpense.vue';
export default {
components: {
lookup, expense
},
data: function() {
return {
modellink: {
"name": "n/a",
"description": "",
"id": null,
"model": "n/a"
},
bulk_trip: {
"name": "n/a",
"description": "",
"id": null
},
selectName: "",
bulk_name: "",
bulk_expensed_at: "",
bulk_type: "",
bulk_tags: "",
bulk_vendor: "",
bulk_method: "",
bulk_total: "",
bulk_paidby: {
name: "",
id: ""
},
bulk_status: "",
bulk_notes: "",
expense: {
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
},
expenses: [
{
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
}
]
};
},
created() {
this.fetchExpenses();
},
methods: {
// onSelectedLink: function (talink) {
// // alert(JSON.stringify(talink.description, null, 4));
// this.modellink = talink
// },
onSelectedTrip: function (talink) {
// alert(JSON.stringify(talink.description, null, 4));
this.bulk_trip = talink
this.modellink = talink
},
fetchExpenses() {
axios.get('/api/expense').then((res) => {
//alert(JSON.stringify(res.data[0], null, 4));
this.expenses = res.data;
});
},
createExpense() {
axios.post('/api/expense', {name: this.expense.name, vessel_id: Laravel.vesselId, expensed_at: this.expense.expensed_at })
.then((res) => {
this.expense.content = '';
// this.expense.user_id = Laravel.userId;
// this.task.statuscolor = '#ff0000';
this.edit = false;
this.fetchExpenses();
})
.catch((err) => console.error(err));
},
deleteExpense(expense) {
console.log(expense.id);
alert(expense.id);
axios.delete('/api/expense/' + expense.id)
.then((res) => {
this.fetchExpenses()
})
.catch((err) => console.error(err));
},
}
}
</script>
BulkExpense.vue
<template>
<tr>
<td><input v-model="expense.name" type="text" name="name"></td>
<td><input v-model="expense.expensed_at"></td>
<td v-if="expense.type"><input v-model="expense.type.name"></td>
<td>
<trip-select v-bind:tripId="expense.trip_id" selectName="trip_id"></trip-select>
</td>
<td><input v-model="expense.vendor"></td>
<td><input v-model="expense.method"></td>
<td><input v-model="expense.total"></td>
<td v-if="expense.paidby"><input v-model="expense.paidby.name" ></td>
<td v-if="expense.status"><input v-model="expense.status.name" ></td>
<td><input v-model="expense.notes"></td>
<td>
<a class="text-lighter hover-light" v-on:click="deleteExpense" href="#"><i class="fas fa-trash"></i></a>
</td>
</tr>
</template>
<script>
import TripSelect from './TripSelect.vue';
import typeahead from './Typeahead.vue';
export default {
name: 'expense',
components: { TripSelect, typeahead },
props: {
bulk_name: {
type: String,
required: false
},
expense: {
required: true,
type: Object,
default: function () {
return {
id: 1,
name: "",
expensed_at: "",
type: {
id: "",
name: ""
},
trip: {
id: "",
name: ""
},
tags: [],
vendor: "",
method: "",
total: "",
paidby: {
id: "",
name: ""
},
status: {
id: "",
name: ""
},
notes: ""
}
}
}
},
data() {
return {
}
},
methods: {
deleteExpense() {
alert(this.expense.id)
this.$emit('expense-deleted', {
'id': this.expense.id,
});
}
}
}
</script>
While it may not directly answer your question, this will for sure work. Also, this solves a fundamental problem you have which is an example of an anti-pattern.
Instead of creating another method inside of your component that emits to the parent, just emit from the click handler in the child. Also, just send the expense back and forth, so you're not property-juggling:
No need to declare $event as you can already have it by default if you want it.
<expense v-for="expense in expenses" :key="expense.id" :expense="expense" #expense-deleted="deleteExpense"></expense>
Then in your click handler, just emit the action and pass the expense back:
<a class="text-lighter hover-light"
#click="$emit('expense-deleted', expense)"
href="#">
<i class="fas fa-trash"></i>
</a>
This will address those fundamental problems above and likely your issue in the process.
Also I see you mixing v-on:click and #click, but they're the same thing, one is just shorthand for the other. I'd suggest just #click for cohesion.
Finally, here's an MCVE for this approach.
Edit
Just in case you're curious, you can delete it from your existing collection after your promise resolves by doing this:
this.expenses.slice(this.expenses.findIndex(o => o.id === expense.id), 1)
Since our expense will always exist in our collection, we never have to worry about checking for existence, so you can use a one liner.
Or you can replace the entire array, but you'll need to use Vue.set for this:
Vue.set(this, 'expenses', this.expenses.filter(o => o.id !== expense.id))
Related
So lets say i have a data - news: {id:3, name:"book", author:'author'}.
And select with options: {id: 1, book: "book1"}, {id: 2, book: "book2"} .
Now i want to v-bind options with news.id and news.book
Is there a way of doing it or ready component for this?
<select v-model="selected">
<option v-for="option in options" :value="option.id">
{{ option.name}}
</option>
</select>
data() {
return {
selected: 1,
options: [
{ name: 'book1', id: 1 },
{ name: 'book2', id: 2 },
{ name: 'book3', id: 3 }
]
}
So in the end i made this:
<select1
:selected="{ id: book.id, name: book.name }"
v-model:id="book.id"
v-model:name="book.name"
urldata="url/to/get/options"
></select1>
And my component look like this:
<template>
<div>
<select v-model="selected" #change="change" class="form-select">
<option v-for="option in options" :value="option">
{{ option.name }}
</option>
</select>
</div>
</template>
export default {
name: "select1",
emits: ["update:selected", "update:id", "update:name"],
props: {
selected: Object,
urldata: String,
},
data: function () {
return {
options: [],
};
},
mounted() {
this.GetListData();
},
methods: {
GetListData() {
if (this.urldata !== "") {
axios
.get(`${this.urldata}`)
.then((response) => (this.options = response.data.results));
}
},
change(event) {
console.log(this.selected);
this.$emit("update:selected", this.selected);
this.$emit("update:id", this.selected.id);
this.$emit("update:name", this.selected.name);
},
},
};
I have a file called PostJobCreate.vue where an Employer can create a new job post.
I also have a table called locations with address columns and location_name column.
An employer can have many locations.
I'm trying to load the employers Locations into a select field so that when they create a new Job Post, they know what Location it is for.
I'm loading all of the employers locations in locations: [].
so I could get a column like this this.locations.location_name.
How can I load all of the location_name into a select field, so that a User can select the location the Job Post is for?
This is what I have so far very basic:
<b-form-group label="Location Name" label-for="location_name">
<b-form-select
v-model="locations.location_name"
:options=""
id="location_name"
size="sm"
>
</b-form-select>
<div class="invalid-feedback" v-if="errors.location_name">{{ errors.location_name[0] }}</div>
</b-form-group>
I want to load the location_name field for every record in the table, inside the :options. I might need a v-for, but I'm not sure what to do next.
PostJobCreate.vue:
<template>
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h3 class="card-title">Post a Job</h3>
<form class="forms-sample" v-on:submit.prevent="createJobPosts">
<b-form-group
label="Employment Type"
label-for="employment-type"
>
<b-form-radio-group
id="employment-type"
v-model="jobPostsData.employment_type"
:options="jobPostsData.optionsTwo"
name="employment-type"
>
</b-form-radio-group>
<div class="invalid-feedback" v-if="errors.employment_type">{{ errors.employment_type[0] }}</div>
</b-form-group>
<b-form-group label="Job Title" label-for="job_title">
<b-form-select
v-model="jobPostsData.job_title"
:options="jobPostsData.optionsThree"
id="job_title"
placeholder="Enter job title"
>
</b-form-select>
<div class="invalid-feedback" v-if="errors.job_title">{{ errors.job_title[0] }}</div>
</b-form-group>
<b-form-group label="Job Description" label-for="job_description">
<b-form-textarea
v-model="jobPostsData.job_description"
id="job_description"
placeholder="Enter job description"
:rows="10"
:max-rows="12"
class="mb-2"
></b-form-textarea>
<div class="invalid-feedback" v-if="errors.job_description">{{ errors.job_description[0] }}</div>
</b-form-group>
<b-form-group label="Salary Range" label-for="salary">
<b-form-select
v-model="jobPostsData.salary"
:options="jobPostsData.options"
id="salary"
size="sm"
>
</b-form-select>
<div class="invalid-feedback" v-if="errors.salary">{{ errors.salary[0] }}</div>
</b-form-group>
<b-form-group label="Location Name" label-for="location_name">
<b-form-select
v-model="locations.location_name"
:options=""
id="location_name"
size="sm"
>
</b-form-select>
<div class="invalid-feedback" v-if="errors.location_name">{{ errors.location_name[0] }}</div>
</b-form-group>
<b-row>
<b-col class="text-left">
<b-button type="submit" variant="success" class="mr-2"
><i class="mdi mdi-check-circle"></i> Save</b-button
>
</b-col>
</b-row>
</form>
</div>
</div>
</div>
</template>
<script>
import * as employerService from '../../../services/employer_service.js';
import * as employerLocationService from '../../../services/employer_location_service.js';
export default {
name: "postJobCreate",
data() {
return {
locations: [],
jobPostsData: {
job_title: null,
optionsThree: [
{ value: null, text: 'Please select an option' },
{ value: 'Budtender/Retail', text: 'Budtender/Retail' },
{ value: 'Delivery/Courier', text: 'Delivery/Courier' },
{ value: 'Security', text: 'Security' },
{ value: 'Grower/Horticulturalist', text: 'Grower/Horticulturalist'},
{ value: 'Master Grower', text: 'Master Grower'},
{ value: 'Extraction', text: 'Extraction'},
{ value: 'Chemist', text: 'Chemist'},
{ value: 'Trimmer', text: 'Trimmer'},
{ value: 'Packagers', text: 'Packagers'},
{ value: 'Manufacturing', text: 'Manufacturing'},
{ value: 'Edible Production', text: 'Edible Production'},
{ value: 'Sales', text: 'Sales'},
{ value: 'Marketing', text: 'Marketing'},
{ value: 'Management', text: 'Management'},
{ value: 'Executive Level', text: 'Executive Level'},
{ value: 'Other', text: 'Other'},
],
job_description: '',
employment_type: 'Freelance',
optionsTwo: [
{ value: "Freelance", text: "Freelance", },
{ value: "Full Time", text: "Full Time", },
{ value: "Internship", text: "Internship", },
{ value: "Part Time", text: "Part Time", },
],
salary: '40,000 and under',
options: [
{ value: '40,000 and under', text: '40,000 and under' },
{ value: '40,000-50,000', text: '$40,000-50,000' },
{ value: '50,000-60,000', text: '$50,000-60,000' },
{ value: '60,000-70,000', text: '$60,000-70,000' },
{ value: '70,000-80,000', text: '$70,000-80,000' },
{ value: '80,000-90,000', text: '$80,000-90,000' },
{ value: '90,000-100,000', text: '$90,000-100,000' },
{ value: '100,000-150,000', text: '$100,000-150,000' },
{ value: '150,000-200,000', text: '$150,000-200,000' },
{ value: '200,000-250,000', text: '$200,000-250,000' },
{ value: '250,000-300,000', text: '$250,000-300,000' },
{ value: '300,000-350,000', text: '$300,000-350,000' },
{ value: '350,000-400,000', text: '$350,000-400,000' },
{ value: '400,000-450,000', text: '$400,000-450,000' },
{ value: '450,000-500,000', text: '$450,000-500,000' },
{ value: '500,000-1,000,000', text: '$500,000-1,000,000' },
{ value: '1,000,000+', text: '1,000,000+' }
],
},
errors: {}
};
},
mounted() {
this.loadLocations();
},
methods: {
createJobPosts: async function() {
let formData = new FormData();
formData.append('job_title', this.jobPostsData.job_title);
formData.append('job_description', this.jobPostsData.job_description);
formData.append('employment_type', this.jobPostsData.employment_type);
formData.append('salary', this.jobPostsData.salary);
formData.append('location_name', this.locations.location_name);
try {
const response = await employerService.createJobPosts(formData);
this.$router.push('/post-a-job');
this.$toast.success("Job Post created Successfully!");
} catch (error) {
switch (error.response.status) {
case 422:
this.errors = error.response.data.errors;
break;
default:
this.$toast.error("Some error occurred, please try again!");
break;
}
}
},
loadLocations: async function() {
try {
const response = await employerLocationService.loadLocations();
this.locations = response.data.locations.data;
} catch (error) {
this.$toast.error('Some error occurred, please refresh!');
}
}
}
};
</script>
<style scoped>
</style>
Thank you.
You could Change the option field names as follows :
<b-form-select
v-model="selectedLocation"
:options="locations"
id="location_name"
size="sm"
value-field="location_name"
text-field="location_name"
>
...
selectedLocation should be defined in data as follows :
data() {
return {
locations: [],
selectedLocation:null
. ..
I'm attempting to nest some components - ultimately I would like to have a component which displays Posts, with a PostItem component used to render each post. Inside the PostItem, I want a list of related comments, with CommentItem to render each comment. I have the posts displayed using the PostItem with no errors, but as soon as I add the Comments I get errors. So to simplify, I've pulled the CommentsList component out and I'm just trying to display it on the page, manually loading in all comments - it's an exact copy of PostsList, except with comment replacing post, but it generates an error:
[Vue warn]: Property or method "commment" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
found in
---> <Comment> at resources/assets/js/components/CommentItem.vue
<CommentsList> at resources/assets/js/components/CommentsList.vue
<Root>
CommentsList.vue
<template>
<div class="media-list media-list-divided bg-lighter">
<comment v-for="comment in comments"
:key="comment.id"
:comment="comment">
</comment>
</div>
</template>
<script>
import comment from './CommentItem'
export default {
components: { comment },
data: function() {
return {
comment: {
id: 1,
content: "",
edited: false,
created_at: new Date().toLocaleString(),
user: {
id: 1,
name: '',
}
},
comments: [
{
id: 1,
content: "",
edited: false,
created_at: new Date().toLocaleString(),
user: {
id: 1,
name: '',
}
}
]
};
},
created() {
this.fetchCommentsList();
},
methods: {
fetchCommentsList() {
axios.get('/api/comments').then((res) => {
//alert(JSON.stringify(res.data[0], null, 4));
this.comments = res.data;
});
},
createComment() {
axios.post('api/comments', {content: this.comment.content, user_id: Laravel.userId, vessel_id: Laravel.vesselId })
.then((res) => {
this.comment.content = '';
// this.comment.user_id = Laravel.userId;
// this.task.statuscolor = '#ff0000';
this.edit = false;
this.fetchCommentsList();
})
.catch((err) => console.error(err));
},
deleteComment(id) {
axios.delete('api/comments' + id)
.then((res) => {
this.fetchCommentsList()
})
.catch((err) => console.error(err));
},
}
}
</script>
CommentItem.vue
<template>
<div>
<a class="avatar" href="#">
<img class="avatar avatar-lg" v-bind:src="'/images/user' + comment.user.id + '-160x160.jpg'" alt="...">
</a>
<div class="media-body">
<p>
<strong>{{ commment.user.name }}</strong>
</p>
<p>{{ comment.content }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'comment',
props: {
comment: {
required: true,
type: Object,
default: {
content: "",
id: 1,
user: {
name: "",
id: 1
}
}
}
},
data: function() {
return {
}
}
}
</script>
I'm pretty new to Vue - I've read so many tutorials and have been digging through the documentation and can't seem to figure out why its working for me with my PostsList component thats an exact copy. It also seems odd that I need both comment and comments in the data return - something that my working Posts version requires.
I'll drop in my working Posts components:
PostsList.vue
<template>
<div>
<div v-if='posts.length === 0' class="header">There are no posts yet!</div>
<post v-for="post in posts"
:key="post.id"
:post="post">
</post>
<form action="#" #submit.prevent="createPost()" class="publisher bt-1 border-fade bg-white">
<div class="input-group">
<input v-model="post.content" type="text" name="content" class="form-control publisher-input" autofocus>
<span class="input-group-btn">
<button type="submit" class="btn btn-primary">New Post</button>
</span>
</div>
<span class="publisher-btn file-group">
<i class="fa fa-camera file-browser"></i>
<input type="file">
</span>
</form>
</div>
</template>
<script>
// import CommentsManager from './CommentsManager.vue';
import post from './PostItem.vue';
export default {
components: {
post
},
data: function() {
return {
post: {
id: 1,
content: "",
edited: false,
created_at: new Date().toLocaleString(),
user: {
id: 1,
name: '',
}
},
posts: [
{
id: 1,
content: "",
edited: false,
created_at: new Date().toLocaleString(),
user: {
id: 1,
name: '',
}
}
]
};
},
created() {
this.fetchPostsList();
},
methods: {
fetchPostsList() {
axios.get('/api/posts').then((res) => {
//alert(JSON.stringify(res.data[0], null, 4));
this.posts = res.data;
});
},
createPost() {
axios.post('api/posts', {content: this.post.content, user_id: Laravel.userId, vessel_id: Laravel.vesselId })
.then((res) => {
this.post.content = '';
// this.post.user_id = Laravel.userId;
// this.task.statuscolor = '#ff0000';
this.edit = false;
this.fetchPostsList();
})
.catch((err) => console.error(err));
},
deletePost(id) {
axios.delete('api/posts' + id)
.then((res) => {
this.fetchPostsList()
})
.catch((err) => console.error(err));
},
}
}
</script>
PostItem.vue
<template>
<div class="box">
<div class="media bb-1 border-fade">
<img class="avatar avatar-lg" v-bind:src="'/images/user' + post.user.id + '-160x160.jpg'" alt="...">
<div class="media-body">
<p>
<strong>{{ post.user.name }}</strong>
<time class="float-right text-lighter" datetime="2017">24 min ago</time>
</p>
<p><small>Designer</small></p>
</div>
</div>
<div class="box-body bb-1 border-fade">
<p class="lead">{{ post.content }}</p>
<div class="gap-items-4 mt-10">
<a class="text-lighter hover-light" href="#">
<i class="fa fa-thumbs-up mr-1"></i> 0
</a>
<a class="text-lighter hover-light" href="#">
<i class="fa fa-comment mr-1"></i> 0
</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'post',
props: {
post: {
required: true,
type: Object,
default: {
content: "",
id: 1,
user: {
name: "",
id: 1
}
}
}
},
data: function() {
return {
}
}
}
</script>
Having the following vue component
<employee-times :employees="{{$employees}}" :supplies="{{$supplies}}" :commits="{{$commits}}" :times="{{$times}}"></employee-times>
where I render the passed props
<template>
<div>
<select class="form-control input" v-model="currentYear" #change="filter()">
<option v-for="option in yearsOptions" v-bind:value="option">
{{ option }}
</option>
</select>
<tr v-for="employee,key in _employees" v-if="commits[key]">
<td>{{ key }}</td>
<td>{{ employee[0].first_name }}</td>
<td>{{ employee[0].last_name }}</td>
<td>{{ employee[0].nick }}</td>
<td>{{ employee[0].role }}</td>
<td>{{ employee[0].skill }}</td>
<td v-for="n in 12">
<div v-if="_commits[key][n]">{{ _commits[key][n].hours }}</div>
<div v-else> </div>
</td>
</tr>
</div>
</template>
than I try to filter the ajax data on change but the data will not update
here is the script what I'm trying, but from method function I'm not able to push the new data to the template
<script>
export default {
name: 'employee-times',
props: ['supplies', 'times', 'commits', 'employees'],
components: {
},
created() {
axios.get('/api/v1/roles', {
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
}).then(response => {
th
is.roles = response.data
}).catch(error => {
})
this._times = this.times;
this._commits = this.commits;
this._supplies = this.supplies;
this._employees = this.employees;
},
data() {
return {
count: 0,
yearsOptions: [],
_employees: {},
_supplies: {},
_times: {},
_commits: [],
currentYear: null,
currentStatus: 1,
currentPosition: 0,
statusOptions: [
{'id': '1',
'text': 'Active'
}, {'id': '0',
'text': 'Inactive'
}],
currentSkillset: 'all',
skillsetOptions: [
{'id': 'all',
'text': 'All'
}, {'l1': 'l1',
'text': 'L1'
}, {'l1': 'l2',
'text': 'L2'
}, {'l1': 'l3',
'text': 'L3'
}, {'l1': 'l4',
'text': 'L4'
}, {'l1': 'l5',
'text': 'L5'
}],
status: {},
roles: {},
skillsets: {}
};
},
mounted() {
this.currentYear = moment().format('Y')
var from = moment().subtract('4', 'years').format('Y')
var to = moment().add('2', 'years').format('Y')
while (from <= to) {
this.yearsOptions.push(from);
from++;
}
},
watch: {
'_employees': function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
},
methods: {
commit() {
},
clear() {
},
months() {
return moment.monthsShort()
},
filter() {
var data = {
year: this.currentYear,
status: this.currentStatus,
position: this.currentPosition,
//skill: currentSkillset
}
axios.post('/api/v1/supply/track-times', data, {
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
}).then(response => {
this._employees = {}
this.$set(this, '_employees', JSON.parse(response.data.employees))
this.$set(this,'_times', JSON.parse(response.data.times))
this.$set(this,'_supplies', JSON.parse(response.data.supplies))
this.$set(this, '_commits', JSON.parse(response.data.commits))
}).catch(error => {
})
}
},
};
</script>
What i missed in this case?
There's a problem with v-for and tables (ref 'v-for with 2 every time') that requires a template wrapper.
See example CodePen.
<div id='app'>
<table>
<template v-for="(employee, key) in employees">
<tr>
<td>{{ employee.first_name }}</td>
<td>{{ employee.last_name }}</td>
</tr>
</template>
<table>
</div>
It also appears that you do need to remove the underscores (try changing employee to _employee in the codepen).
Remove the "_" prefix from your data properties, then it should work. Vue uses underscores for internal stuff, so best avoid using it (see https://v2.vuejs.org/v2/api/#data)
What I need is:
1) Represent list of partners
App.Partner= DS.Model.extend(
{
name: attr(),
site: attr(),
contacts: DS.hasMany('partner-contact', { async: true, polymorphic: true })
}
);
2) and names of partners contacts
App.PartnerContact = DS.Model.extend(
{
name: attr(),
phone: attr(),
post: attr(),
email: attr(),
partnerId: attr(),
partner: DS.belongsTo('partner')
}
);
So I have route:
App.CallsMyContactsRoute = Ember.Route.extend({
//needs: "partner-contacts",
model: function () {
return this.store.find('partner');
}
});
And template:
{{#each partner in model}}
<tr class="gradeA even" role="row">
<td class="sorting_1">{{partner.name}}</td>
<td>{{partner.name}}</td>
<td>{{partner.phone}}</td>
<td class="center">
{{partner.contacts}}
{{#each contact in partner.contacts}}
{{contact.name}}
{{/each}}
</td>
<td class="center">A</td>
</tr>
{{/each}}
But {{partner.contacts}} is DS.PromiseManyArray
And it ever doesn't make a query to server.
Answer from server (Partners)
{
"Partner":[
{
"id":4,
"name":"CompanyNamej",
"site":"www.no-site.ru"
},
{
"id":13,
"name":"test",
"site":"sdasdasd"
},
{
"id":14,
"name":"",
"site":""
}
]
}
What is the problem?
DEBUG: Ember : 1.10.0
vendor.js:16928DEBUG: Ember Data : 1.0.0-beta.15
vendor.js:16928DEBUG: jQuery : 2.1.3