I am getting an array of object returned from Database.I want to map that array and inside that i want to map another array.But i am getting some error. If anyone knows please help out with this problem.
displayMovies(){
if(!this.state.body) return ( <h2> Loading.... </h2>)
else{
return this.state.body.map((data) => {
return(
<div key={data._id}>
<li> {data.name} </li>
<ul>
{
return data.map((cast) => {
return <li> cast.name </li>
})
}
</ul>
</div>
)
})
}
}
Data response
You are not in any function inside nested loop so you do not need to return any thing
Replace
return data.map((cast) => {
to
data.map((cast) => {
and add apply map function on cast property of data
displayMovies(){
if(!this.state.body) return ( <h2> Loading.... </h2>)
else{
return this.state.body.map((data) => {
return(
<div key={data._id}>
<li> {data.name} </li>
<ul>
{
data.cast.map((cast) => {
return <li> {cast.name} </li>
})
}
</ul>
</div>
)
})
}
}
Your first map function returns an Object. Objects don't have map function use a for in loop instead, inside the loop, check if the property is an object.
Replace second map with something like this :
for (const key in object) {
if (object.hasOwnProperty(key) && typeof(object[key]=='object') {
// do something with object[key].name
}
}
Related
I'm building a SPA using vue.js, I need to assign a div background-image referencing something in the following path:
I'm trying to reference src/assets/img/firstCard.jpg but for some reason it doesn't shows the image, this is how I'm binding the image:
HTML:
<a class="card">
<div
class="card__background"
v-bind:style="secondCard">
</div>
<div class="card__content">
<p class="card__category">Gratuita</p>
<h3 class="card__heading">Ademas en diferentes plataformas.</h3>
</div>
</a>
JS:
<script>
export default {
data () {
return {
thirdCard: {
'background-image': require('#/assets/img/firstCard.jpg')
},
secondCard: {
'background-image': require('#/assets/img/firstCard.jpg')
},
firstard: {
'background-image': require('#/assets/img/firstCard.jpg')
}
}
}
}
</script>
Thank you all for your time.
You can try to make method or computed property:
getUrl (img) {
return require(`#/assets/img/${img}.jpg`);
}
then call that method in data object (for background-image you need to specify url):
data () {
return {
firstCard: {
'background-image': `url(${this.getUrl('firstCard')})`
}
}
},
I'm working on a NodeJS/Angular project and while trying to do a simple CRUD, I'm blocked when I try to get an element by ID.
I would like to retrieve all the info of a "Member" based on its ID and display the info in a table. I manage to get my JSON with the API call but when trying to display it in the table, it doesn't show anything.
My service, with the API call :
public getMember(id: number) {
return new Promise((resolve, reject) => {
this.http.get(this.config.apiServer + `astreintes/member/get/${id}`)
.subscribe((res) => {
resolve(res as Member);
console.log(res);
}, err => {
reject(err);
});
});
}
Result of the console.log: My correct JSON with the info of the member
My component.ts :
public search(){
this.memberService.getMember(this.id).then((data) => {
if(data){
this.member = (data as any).recordset;
console.log("Get member :"+ data);
this.indice = true;
}else{
}},
(error) => {
console.log(error);
}
);
}
Result of the console.log: "Get member : [Object Object]"
For the interface, I just have a dropdown list of all my members, and when I select one and click on the button "Search", it gets the info of my member correctly in the console. Then, I want to display it in my table below. My html code:
<form (submit)='search()' #searchMemberForm="ngForm" class="form-horizontal">
<select [(ngModel)]="id" name="member">
<option *ngFor="let member of membersList"
[value]="member.Id_OnCall_Member">{{member.Oncall_Member_Name}}</option>
</select>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-save btn-primary">Search</button>
</div>
</div>
</form>
<div *ngIf="indice">
<h1 style="text-align: center">
Informations
</h1>
<table class="table table-striped" *ngIf="indice">
<th>ID</th>
<th>Nom</th>
<th>Numéro de téléphone</th>
<th>Statut d'activité</th>
<tr *ngFor="let m of member">
<td>{{m.Id_OnCall_Member}}</td>
<td>{{m.Oncall_Member_Name}}</td>
<td>{{m.OnCall_Member_Phone}}</td>
<td>{{m.OnCall_Member_Status}}</td>
</tr>
</table>
</div>
Thanks for your help !
you used "+" in console.log: console.log("Get member :"+ data);
it means javascript is trying to convert the output to one type (string), but data is object. Below you can find how to get the correct output.
const res = {
memberId: 1
}
console.log(res) // {memberId: 1}
console.log('Member: '+ res); //Member: [object Object]
console.log('Member: ', res); // Member: {memberId: 1}
console.log('Member: '+ JSON.stringify(res)); // Member: {memberId: 1}
thanks for your answers.
My problem wasn't the console.log though, it was just an indication.
My real problem was that it didn't show anything in my table. But I solved it, here's how in case it might help other people.
In my component.ts, I replaced:
this.member = (data as any).recordset;
By this, simply:
this.member = data;
I'm trying to loop through an object with children, that can have children, that can have children.
Basically I won't know how many child elements and levels the object can contain. Is there an easy way to loop through these, and render out a component in a list view without having to call the Object.keys(myobject).map many times in side eachother. Inside the render function?
parent: {
term: "cats",
children: {
child: {
term: "kids",
otherdata: "dekme",
children: {
granddaughter: {term: "kids", otherdata: "dekme"},
grandson: {term: "mip", some other data: "si"}
}
},
}
}
And also the output should be something like:
<ul>
<li>
Parent
<ul>
<li>child</li>
<li>child
<ul>
<li>grandkid
</li>
</ul>
</li>
</ul>
</li>
</ul>
Great question! This class of problem is generally referred to as walking / tree walking, and there are probably some great libraries on npm that you could use to make it easier. We can write a simple recursive one by hand though. I'll assume your nested objects containing child elements look something like this:
var treeOfObjects = {
children: [
{
name: 'billy'
},
{
name: 'wanda',
children: [
{
name: 'stanley'
}
]
}
]
};
Here's a walk function that will crawl that tree, call an iterator function you supply on each child it finds, and collect up the results in an Array.
function walk(object, iterator, results) {
// no results array it means we're at the root.
// create the shared array, then map the root through our iterator.
if (!results) {
results = [iterator(object, null, object)];
}
if (object.children) {
for (let i=0; i<object.children.length; i++) {
// call the iterator on this child and append the result:
let value = object.children[i];
results.push(iterator(value, i, object));
// now "walk into" the child object (appending its children, etc):
walk(value, iterator, results);
}
}
return results;
}
Here's what it looks like in practice:
class Foo extends Component {
render({ someTreeOfObjects }) {
return (
<ul>
{walk(someTreeOfObjects, object => (
<li>{object.name}</li>
))}
</ul>
);
}
}
// render(<Foo someTreeOfObjects={treeOfObjects} />)
// .. produces:
// <ul>
// <li>billy</li>
// <li>wanda</li>
// <li>stanley</li>
// </ul>
Update - for nested output:
function nodeToListItem (object) {
// if we have children, we'll recurse into them
const children = object.children || [];
return (
<li>
{object.name}
{children.length>0 && (
<ul>
{children.map(nodeToListItem)}
<ul>
)}
</li>
);
}
class Foo extends Component {
render({ someTreeOfObjects }) {
return (
<ul>
{nodeToListItem(someTreeOfObjects)}
</ul>
);
}
}
Because the nesting from the original structure matches what we want to produce in the DOM, this ends up being a simple graph transformation.
I have the following pagination component.
If users adds remove items dynamically i.e via some ajax call, how do i ensure the correct active or disabled classes are applied on the pagination links?
For example if the user is currently on the last page which only has 1 item, if the user deletes that item, the pagination links re-render but then i lose the active disable class becuase that page no longer exists. i.e. the links should update to move the user to previous page.
<div class="comment-pager ">
<div class="panel panel-default panel-highlight no-border ">
<div class="panel-body padded-5">
<nav v-if="totalItems > pageSize">
<ul class="pagination">
<li v-bind:class="[currentPage == 1 ? disabled : '']">
<a v-on:click.prevent="previous()" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li v-bind:class="[currentPage == pages ? active : '']" v-for="page in pages" v-on:click.prevent="changePage(page)">
<a>{{ page }}</a>
</li>
<li v-bind:class="[currentPage == pages.length ? disabled : '']">
<a v-on:click.prevent="next()" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['totalItems', 'pageSize']
data: function () {
return {
currentPage: 1,
pages: [],
}
},
watch: {
totalItems: function () {
var pagesCount = Math.ceil(this.totalItems / this.pageSize);
this.pages = [];
for (var i = 1; i <= pagesCount; i++)
this.pages.push(i);
}
},
methods: {
changePage: function (page){
this.currentPage = page;
this.$emit('pageChanged', page);
}
previous: function (){
if (this.currentPage == 1)
return;
this.currentPage--;
this.$emit('pageChanged', this.currentPage);
}
next: function () {
if (this.currentPage == this.pages.length)
return;
this.currentPage++;
this.$emit('pageChanged', this.currentPage);
}
},
}
</script>
<paginator v-bind:total-items="totalItems" v-bind:page-size="query.pageSize" v-on:pageChanged="onPageChange"></paginator>
There is no complete equivalent to ngOnChanges() in vue.
ngOnChanges() is a lifecycle hook which takes in an object that maps each changed property name to a SimpleChange object holding the current and previous property values.
If you want the lifecycle hook that gets invoked after every change in data and before re-rendering the virtual DOM then you should be using beforeUpdate() hook.
But as in ngOnChanges() you can't get the hold of which property is updated or what is it's oldvalue or newValue is.
As mklimek answered you can set up watcher on the properties you want to watch for changes.
In watcher you get what the oldValue is and what it's changed new value is
new Vue({
el:'#app',
data:{
prop1: '',
prop2: '' // property to watch changes for
},
watch:{
prop#(newValue, oldValue){
console.log(newValue);
console.log(oldValue);
}
}
});
EDIT
For your case you do not need a watcher. You can setup the pages[] property as a computed property:
computed:{
pages(){
var pageArray = [];
var pagesCount = Math.ceil(this.totalItems / this.pageSize);
for (var i = 1; i <= pagesCount; i++)
pages.push(i);
}
return pageArray;
}
computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed in your case the props
totalItems and pageSize
Now you can use the pages computed property as normal data property
You probably want to use watch property of a Vue instance.
var vm = new Vue({
data: {
count: 1
},
watch: {
count: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
})
I definitely know that something is wrong with this snippet but can't figure out the right way to get it done. I want to paginate the page where students are displayed. If I put it plainly like this return SchoolStudents.find();, it works perfectly by returning all the students but this defeats the main purpose of pagination. I'm either not sure where the problem is, either in the publish function or the helper function. What I want to achieve is that the records in SchoolStudents colleciton should be paginated to display 2 records on a page.
This is the autorun
Session.setDefault('skip', 0);
Template.view.onCreated(function () {
Session.setPersistent('ReceivedSlug', FlowRouter.getParam('myslug'));
this.autorun(function () {
Meteor.subscribe('SchoolStudents', Session.get('skip'));
});
});
this is the helper method
students(){
let myslug = trimInput(Session.get('ReceivedSlug'));
if (myslug) {
let mySchoolDoc = SchoolDb.findOne({slug: myslug});
if (mySchoolDoc) {
let arrayModuleSchool = StudentSchool.find({schoolId: mySchoolDoc._id});
if (arrayModuleSchool) {
var arrayStudentIds = [];
arrayModuleSchool.forEach(function(studentSchool){
arrayStudentIds.push(studentSchool.studentId);
});
let subReadiness = SchoolStudents.find({_id: {$in: arrayStudentIds}}).fetch();
if (subReadiness) {
return subReadiness;
}
}
}
}
}
This is the publish method
Meteor.publish('SchoolStudents', function (skipCount) {
check(skipCount, Number);
user = Meteor.users.findOne({_id:this.userId})
if(user) {
if(user.emails[0].verified) {
return SchoolStudents.find({userId: this.userId}, {limit: 2, skip: skipCount});
} else {
throw new Meteor.Error('Not authorized');
return false;
}
}
});
Blaze template
<section class="tab-section" id="content4">
{{#each student in students}}
<div class="row" style="margin-top: -20px;">
<!-- Begin Listing: 609 W GRAVERS LN-->
<div class="brdr bgc-fff pad-10 box-shad btm-mrg-20 property-listing card-1">
<div class="media">
<div class="media-body fnt-smaller">
<h4 class="media-heading">{{student.firstname}} {{student.lastname}}</h4>
<p class="hidden-xs" style="margin-bottom: 5px; margin-top: -10px;">{{trimString student.useremail 0 110}}</p><span class="fnt-smaller fnt-lighter fnt-arial">{{course.createdAt}}</span>
</div>
</div>
</div><!-- End Listing-->
</div>
{{/each}}
<ul class="pager">
<li class="studentprevious">Previous </li>
<li class="studentnext">Next </li>
</ul>
</section>
the pagination event
'click .studentprevious': function () {
if (Session.get('skip') > 0 ) {
Session.set('skip', Session.get('skip') - 2 );
}
},
'click .studentnext': function () {
Session.set('skip', Session.get('skip') + 2 );
}