v-for doesn't output anything even though data is returned from the GET - vue.js

Creating a VUE 2 app using NUXT. My async method returns data fine. But, for some reason my v-for doesn't produce any html markup.
The test data, as returned by my node.js api, via postman, is...
{
    "status": true,
    "message": [
        {
            "rating": [],
            "_id": "5e113ce50d41592e38976e45",
            "title": "Book 2",
            "description": "This is amazon book2",
            "photo": "https://amazon-clone-v1-rg.s3.amazonaws.com/xxxxx",
            "stockQuantity": 14,
            "price": 33,
            "__v": 0
        },
        {
            "rating": [],
            "_id": "5e113cf00d41592e38976e46",
            "title": "Book 1",
            "description": "This is amazon book 1",
            "photo": "https://amazon-clone-v1-rg.s3.us-east-2.amazonaws.com/xxxxx",
            "stockQuantity": 14,
            "price": 25,
            "__v": 0
        }
    ]
}
Here's my code...
<template>
  <main>
    <div class="a-spacing-large">
      <div class="container-fluid browsing-history">
        <div class="row">
          <div class="col-sm-8 col-8">
            <h1 class="a-size-large a-spacing-none a-text-normal">All Products</h1>
            <div class="a-spacing-large"></div>
            <!-- Button -->
            <a href="#" class="a-button-buy-again">Add a new product</a>
            <a href="#" class="a-button-history margin-right-10">Add a new category</a>
            <a href="#" class="a-button-history margin-right-10">Add a new owner</a>
          </div>
          <!-- Listing page -->
        </div>
      </div>
    </div>
    <div class="a-spacing-large"></div>
    <div class="container-fluid browsing-history">
      <div class="row">
        <div
          v-for="(product, index) in products"
          :key="product._id"
          class="col-xl-2 col-lg-2 col-md-3 col-sm-6 col-6 br bb"
        >
          <div class="history-box">
            <!-- Product image -->
            <a href="#" class="a-link-normal">
              <img :src="product.photo" class="img-fluid" />
            </a>
            <!-- Product title -->
            <div class="a-spacing-top-base asin-title">
              <span class="a-text-normal">
                <div class="pl3n-sc-truncated">{{product.title}}</div>
              </span>
            </div>
            <!-- Product rating -->
            <div class="a-row">
              <a href="#">
                <i class="fas fa-star"></i>
                <i class="fas fa-star"></i>
                <i class="fas fa-star"></i>
                <i class="fas fa-star"></i>
                <i class="fas fa-star"></i>
              </a>
              <span class="a-letter-space"></span>
              <span class="a-color-tertiary a-size-small asin-reviews">(1732)</span>
            </div>
            <!-- Product price -->
            <div class="a-row">
              <span class="a-size-base a-color-price">
                <span class="pl3n-sc-price">${{product.price}}</span>
              </span>
            </div>
          </div>
          <!-- Product Buttons -->
          <div class="a-row">
            <a href="#" class="a-button-history margin-right-10">Update</a>
            <a href="#" class="a-button-history margin-right-10">Delete</a>
          </div>
        </div>
      </div>
    </div>
  </main>
</template>
<script>
export default {
  async asyncData({ $axios }) {
    try {
      let response = await $axios.$get("http://localhost:3000/api/products");
      console.log(response);
      return {
        products: response.products
      };
    } catch (err) {
      console.log("error in code: " + err);
    }
  }
};
</script>
I even tried a very simple v-for loop like this, but it also returned no data...
<ul>
<li v-for="(product, index) in products">{{product.title}}</li>
</ul>
Any suggestions would be appreciated. Thanks!

In your front-end you should return {products: response.message} because the field called message have yours "products" list.
or
In your API back-end should be use products instead of message to return your "product" list then you could be used as you mentioned.

Related

vue js append parameters to URL

I am using vuejs3 and I want to make a filter. When user click to the link I want to append the url and push it to browser address bar for now. Later I will do ajax request to update page with product list.
So far I am able to send parameters to URL, but only one item from one group.From first color group I want user to select only one but from second size group I want user to select multiple.
I want this type of URL: localhost:8080/product?color=red&size=medium&size=large
<template>
<div class="products">
<div class="multi_filters">
<h1>Multi Filter By Color</h1>
Red color
Blue color
</div>
<div class="single_filter">
<h1>Multi Size</h1>
Medium
Large
</div>
</div>
</template>
<script>
export default {
data() {
return {
filters:{},
selectedFilters:{}
}
},
methods:{
activateFilter(key,value){
this.selectedFilters = Object.assign({},this.selectedFilters,{[key]:value})
console.log(this.selectedFilters)
this.$router.replace({
query: {
...this.selectedFilters
}
})
}
}
}
</script>
You are expecting size to be an array, this post will helps.
Submitting multi-value form fields, i.e. submitting arrays through GET/POST vars, can be done several different ways, as a standard is not necessarily spelled out.
Three possible ways to send multi-value fields or arrays would be:
?cars[]=Saab&cars[]=Audi (Best way- PHP reads this into an array)
?cars=Saab&cars=Audi (Bad way- PHP will only register last value)
?cars=Saab,Audi (Haven't tried this)

How to fetch media data for each loop item

Basically I got two fetching paths, I want to display each media (jpg,audio) etc. for each clue. Do I need to fetch it by id in the html, are there better ways to do it?
chapters/:chapter_id/clues
chapters/:chapter_id/clues/:clue_id/media
Do I need something like?
<div v-for="clue in clues" :key="clue.id">
[{clue.name}}
Media: {{ fetchMediaClue(route.params.id, clue.id) }}
</div>
you have 2 possibilities!
once (I guess in the mounted) loaded with data of the clues, load in another prop the data of the media
this.mediaClues = await Promise.all(
this.clues.map((clue) => fetchMediaClue(this.route.params.id, clue.id))
);
make a component that autonomously loads data from the API
<div v-for="clue in clues" :key="clue.id">
[{clue.name}}
<media-component :clue-id="clue.id" :other-prop="route.params.id" />
</div>

Using counter flag in v-for loop

I want to use a counter flag in v-for inside another v-for loop for counting total run of inside loop.
Here is my template:
<a :href="'#/product/'+list.id" :id="ikeyCounter" v-for="item,ikey in section.list" class="movie item fcosuable">
{{ ikeyCounterPlus() }}
<div class="verticalImage">
<div class="loader hideloading"></div>
<img :src="item.thumb" alt="">
</div>
</a>
data() {
return {
loading: true,
status: null,
list: [],
sections: null,
ikeyCounter: 3
}
},
And method:
ikeyCounterPlus() {
this.ikeyCounter++;
},
but I'm getting wrong result on ikeyCounter variable. Id of a tag started from "15003" to "15150", if I don't call ikeyCounterPlus() inside v-for tag, for loop will run correctly (150 run)
If you want to count your objects, then just count your data. No need to involve DOM.
section.list is an array, so section.list.length should give you desired count.
Also, as mentioned in the answer before, use some unique property of item (for example some sort of id) as the value for :key attribute.
You can't do it like this, Vue.js is reactive framework and you should learn a little bit before asking these kind of questions - https://v2.vuejs.org/v2/guide/reactivity.html
Use your key as id instead

Returning $key in AngularFire 5

I have the following code that works with AngularFire 5:
export class NavigationComponent {
items: Observable<any[]>;
constructor(db: AngularFireDatabase) {
this.items = db.list('/pages', ref => {
let query = ref.limitToLast(100).orderByChild('sortOrder');
return query;
}).valueChanges();
}
}
But now need to return item.$key which apparently is no longer returned by default in AngularFire 5. I see mention in the migration guide of needing to "map" this, but can't seem to get the right syntax working on the above code.
Update: followed the advice below, and it seemed to have worked but there appears to be some difference still between the behavior between old and new.
<nav class="nav-standard">
<app-logo></app-logo>
<div class="nav-dropdown">
<ul *ngFor="let item of items | async | filter : 'parent' : '00000000-0000-0000-0000-000000000000'" class="nav-dropdown">
<li>
<a mat-button class="mat-button" href="{{item.path}}" data-id="{{item.key}}" target="{{item.target}}" title="{{item.tooltip}}" [attr.data-sort]="item.sortOrder" *ngIf="item.content; else elseBlock">{{item.menuText}}</a>
<ng-template #elseBlock>
<a mat-button class="mat-button" data-id="{{item.key}}" target="{{item.target}}" title="{{item.tooltip}}">{{item.menuText}}</a>
</ng-template>
<ul class="nav-dropdown-content">
<li *ngFor="let childItem of items | async | filter : 'parent' : item.key" class="">
<a class="mat-button" href="{{childItem.path}}" data-id="{{childItem.key}}" target="{{childItem.target}}" title="{{childItem.tooltip}}" [attr.data-sort]="childItem.sortOrder">{{childItem.menuText}}</a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
The nested *ngFor never seems to fire, whereas before it did. It appears that items in the nested *ngFor is null ?? I found if I create another Observable in my component called childItems and assign duplicate logic to that, it works okay -- but to me that feels dirty and wrong. How can I get the data in the observable to persist long enough to use it in the nested *ngFor ?
item key information isn't 'free' anymore upon the new AngularFire API changes. Instead of use '.valueChanges()' to turn reference into Observable, you can use '.snapshotChanges()':
.snapshotChanges()
.map(changes => {
return changes.map(change => ({key: change.payload.key, ...change.payload.val()}));
})
from then on you can reference item key by using 'item.key' (notice that you cannot use '$key').

Is there a way to disconnect an event added by dojoAttachEvent

I have a dojo widget which uses a a custom-library code having a link like this in its template.
Go Back
I need to find a way to disconnect this event from my widget. The only way i know how an event can be disconnected is, using a
dojo.disconnect(handle)
I could use this if I had the event connected using dojo,connect() which returns me the handle.
However with dojoAttachEvent i don't have the event handle hence no way to disconnect it.
Note :
Changing this html is not an option for me, since this an external library i am using.
Also, I am not looking for a solution to disconnect all events.
CODE:
otherWidget.js:
dojo.provide("otherWidget");
dojo.declare("otherWidget", [], {
templateString : dojo.cache("otherWidget","templates/otherWidget.html"),
_goBack: function(){
this.destroyWidgetAndRedirect();
},
destroyWidgetAndRedirect: function(){
//Code to destory and redirect.
},
});
otherWidget.html:
<div>
Go Back
<!-- Other Widget related code -->
...
</div>
myWidget.js:
dojo.provide("myWidget");
dojo.require("otherWidget");
dojo.declare("myWidget", [], {
templateString : dojo.cache("myWidget","templates/myWidget.html"),
this.otherWidget = new otherWidget({}, dojo.byId('otherWidgetContainer'));
});
myWidget.html:
<div>
<div id="otherWidgetContainer"></div>
<!-- My Widget related code -->
...
</div>
Any thoughts..
Thanks.
Extension points can be used directly on your html, or in javascript. Suppose the widget you are using is called 'my.custom.dojowidget', and that it has an onClick extension point. I will show here the declarative way, in your html. Try this :
<div data-dojo-type="my.custom.widget">
<script type="dojo/method" data-dojo-event="onClick" data-dojo-args"evt">
dojo.stopEvent(evt);
console.debug("did this work ?");
</script>
</div>
Now this depends on the existence of the extension point... if you can't still do what you want, please post the relevant parts of your widget's code.
So... based on the sample code you posted in your edit, I think you should do the following :
<div data-dojo-type="otherWidget">
<script type="dojo/method" data-dojo-event="destroyWidgetAndRedirect" data-dojo-args="evt">
dojo.stopEvent(evt);
// do whatever custom code you want here...
</script>
</div>