items components in nativescript not populating the response from django api - django

I have simple django api which provide a list of movies title with their ids.
I have created a movies service in type script which performs the get operation and get the list of movies title and id.
In native script I have two files, items.component.ts
import { Component, OnInit } from "#angular/core";
import { MovieService } from "../services/movie.service";
#Component({
selector: "ns-items",
moduleId: module.id,
templateUrl: "./items.component.html",
providers: [MovieService]
})
export class ItemsComponent implements OnInit {
items;
constructor(private movieService: MovieService) { }
ngOnInit(): void {
this.movieService.getMovies().subscribe(
movies => {
this.items = movies;
console.log(movies);
},
error => {
console.log('error', error);
}
);
}
}
and items.component.html
<ActionBar title="Movie Rater App" class="action-bar">
</ActionBar>
<StackLayout class="page">
<ListView [items]="items" class="list-group">
<ng-template let-item="item">
<Label [nsRouterLink]="['/item', item.id]" [text]="item.title"
class="list-group-item"></Label>
</ng-template>
</ListView>
</StackLayout>
I am getting blank screen on the app in the emulator - just the action bar title. No exception in logs
I validated API is running fine and even I can see the response in console (i.e. output of console.log(movies)).
Any help will be appreciated.
django API response:
{
"count": 5,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"title": "Rocky (1976)",
"description": "A small-time boxer gets a supremely rare chance to fight a heavy-weight champion in a bout in which he strives to go the distance for his self-respect.",
"moviePoster": "http://127.0.0.1:8000/pic_folder/rocky-1976-poster2451370.jpg",
"avg_rating": 5,
"no_of_ratings": 1,
"maxRatings": 5
},
{
"id": 16,
"title": "Rocky II",
"description": "Rocky II",
"moviePoster": "http://127.0.0.1:8000/pic_folder/rocky2.jpg",
"avg_rating": 5,
"no_of_ratings": 1,
"maxRatings": 5
}
}

You must assign the result array to items so it suppose to be,
this.movieService.getMovies().subscribe(
movies => {
this.items = movies.results;
console.log(movies);
},
error => {
console.log('error', error);
}
);
Also you may not need the StackLayout wrapping the ListView, you could remove that.

Related

How to capture object props in Vue snapshot tests

I always get [object Object] in place of object props due to object to string coercion when I use snapshot testing. How can I fix it? I've tried wrapping element into JSON.stringify(), but it causes "Converting circular structure to JSON" Error.
The example of a resulting snapshot:
exports[`SalesList.vue Снапшот десктоп 1`] = `
<magic-grid-stub
class="sales-list"
cols="[object Object]"
gaps="[object Object]"
usemin="true"
>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-info-stub
class="item"
content="additionalInfo"
/>
</magic-grid-stub>
`;
I have the simple corresponding snapshot tests, like this one:
import { createLocalVue, shallowMount } from '#vue/test-utils'
import SalesList from '#/components/sales/SalesList.vue'
let localVue
const fakeSale = {
code: 'code',
description: 'description',
title: 'title',
image: 'image',
archive: false,
visible: true,
date_to: '2020/08/01',
short_description: 'short_description',
slug: 'slug',
date_from: '2020/06/01',
seo: {
seo_description: 'seo_description',
seo_title: 'seo_title',
seo_keywords: 'seo_keywords',
},
}
function createWrapper(component, options) {
return shallowMount(component, {
localVue,
...options,
})
}
beforeAll(() => {
localVue = createLocalVue()
})
describe('SalesList.vue', () => {
it('Снапшот десктоп', async () => {
expect.assertions(1)
const wrapper = createWrapper(SalesList, {
propsData: {
sales: Array.from({ length: 4 }, (_, index) => ({
...fakeSale,
slug: `slug-${index}`,
})),
additionalInfo: 'additionalInfo',
},
mocks: {
$device: { isDesktop: true },
},
})
expect(wrapper.element).toMatchSnapshot()
})
})
And the component in question itself:
<script lang="ts">
import SalesItem from '#/components/sales/SalesItem.vue'
import MagicGrid from '#/components/MagicGrid.vue'
import SalesInfo from '#/components/sales/SalesInfo.vue'
import Vue from 'vue'
export default Vue.extend({
name: 'SalesList',
components: {
SalesItem,
MagicGrid,
SalesInfo,
},
props: {
sales: {
type: Array,
required: true,
},
additionalInfo: {
type: String,
default: null,
},
},
computed: {
colsAndGaps(): {
cols: { 0: number }
gaps: { 0: number }
} {
return this.$device.isDesktopOrTablet
? {
cols: {0: 2},
gaps: {0: 30},
}
: {
cols: {0: 1},
gaps: {0: 16},
}
},
},
})
</script>
<template>
<magic-grid v-bind="colsAndGaps" class="sales-list">
<sales-item
v-for="sale in sales"
:key="sale.slug"
:sale="sale"
class="item"
/>
<sales-info v-if="additionalInfo" :content="additionalInfo" class="item"/>
</magic-grid>
</template>
You could use a custom jest snapshot serializer.
For VueJs 2 you could use https://github.com/tjw-lint/jest-serializer-vue-tjw - but it doesn't work for VueJs 3 (https://github.com/tjw-lint/jest-serializer-vue-tjw/pull/64).
Example configuration for VueJs 2:
npm install jest-serializer-vue-tjw
// package.json
{
...
"jest": {
"snapshotSerializers": ["jest-serializer-vue-tjw"]
}
}

How to programmatically collapse raddataform "Groups" in Nativescript-vue

I'm trying to make use of the RadDataForm using NativeScript-Vue to develop a relatively long form.
My form elements are being defined and grouped programmatically via json. I would like to have the groups start in a minimized / collapsed state for the user experience.
The NativeScript-Vue docs for this feature are very sparse. Angular documentation is deeper so I went there for help, but clicking thru to the "collapsed" API reference results in a 404 from this page.
I'm running it from the Playground to test functionality using the following code:
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar">
<ActionItem icon="font://" class="fa" />
<ActionItem icon="font://\uf07a" class="fa" />
</ActionBar>
<RadDataForm :source="person" :metadata="groupMetaData"
#groupUpdate="onGroupUpdate" />
</Page>
</template>
<script>
import Vue from "nativescript-vue";
import RadDataForm from "nativescript-ui-dataform/vue";
Vue.use(RadDataForm);
export default {
data() {
return {
person: {
name: "John",
age: 23,
email: "john#company.com",
city: "New York",
street: "5th Avenue",
streetNumber: 11
},
groupMetaData: {
// propertyGroup: [{
// "Address": {
// 'collapsed' = true,
// },
// // {
// // "Main Info": 'collapsed',
// }
// ],
propertyAnnotations: [{
name: "city",
index: 3,
groupName: "Address",
editor: "Picker",
valuesProvider: [
"New York",
"Washington",
"Los Angeles"
]
},
{
name: "street",
index: 4,
groupName: "Address"
},
{
name: "streetNumber",
index: 5,
editor: "Number",
groupName: "Address"
},
{
name: "age",
index: 1,
editor: "Number",
groupName: "Main Info"
},
{
name: "email",
index: 2,
editor: "Email",
groupName: "Main Info"
},
{
name: "name",
index: 0,
groupName: "Main Info"
}
]
}
};
},
methods: {
onGroupUpdate: function(args) {
let nativeGroup = args.group;
if (args.ios) {
nativeGroup.collapsible = true;
// nativeGroup.collapsed = true;
} else {
nativeGroup.setExpandable(true);
// nativeGroup.collapsed;
// nativeGroup.collapsed(true);
// nativeGroup.collapsed;
}
// console.log(JSON.stringify(nativeGroup));
}
}
};
</script>
<style scoped>
.home-panel {
vertical-align: center;
font-size: 20;
margin: 15;
}
.description-label {
margin-bottom: 15;
}
</style>
My assumption was to address this in the OnGroupUpdate method, but I'm not getting where I need to be (you can see a few attempts that are commented out).
The goal is to have this view load with minimized groups so that the user can expand the different form groups that s/he wishes to work on in sequence.
Playground link: https://play.nativescript.org/?id=XLKFoC&template=play-vue&v=14
Thanks for any help
I think what you actually need is nativeGroup.setIsExpanded(false);
exports.onGroupUpdate = (args) => {
if (app.ios) {
let nativeGroup = args.group;
nativeGroup.collapsible = true;
} else {
let nativeGroup = args.group;
nativeGroup.setExpandable(true);
//in this example i only expand one group.
if (args.groupName !== "SERVICE INFORMATION") {
nativeGroup.setIsExpanded(false)
}
}
}

How to use PUT to update something in Vue using Django REST framework

I am new to Vue but have experience with Django. I am using this boilerplate from Github: https://github.com/gtalarico/django-vue-template
I really like the structure of that boilerplate because it is not overwelming at all and not a lot of code is written to succesfully interact with the back-end API of Django.
It has GET, POST & DELETE already pre-installed and connected to Django REST. So far so good. However I try to add a PUT method to it so I can update models. I try to follow the same structure but I can't get it to work.
My productService.js:
import api from '#/services/api'
export default {
fetchProducts() {
return api.get(`products/`)
.then(response => response.data)
},
postProduct(payload) {
return api.post(`products/`, payload)
.then(response => response.data)
},
deleteProduct(proId) {
return api.delete(`products/${proId}`)
.then(response => response.data)
},
updateProduct(proId) {
return api.put(`products/${proId}`)
.then(response => response.data)
}
}
The updateProduct is the new code I added.
Then in store --> products.js:
const actions = {
getProducts ({ commit }) {
productService.fetchProducts()
.then(products => {
commit('setProducts', products)
})
},
addProduct({ commit }, product) {
productService.postProduct(product)
.then(() => {
commit('addProduct', product)
})
},
deleteProduct( { commit }, proId) {
productService.deleteProduct(proId)
commit('deleteProduct', proId)
},
updateProduct( { commit }, proId) {
productService.updateProduct(proId)
commit('updateProduct', proId)
}
}
const mutations = {
setProducts (state, products) {
state.products = products
},
addProduct(state, product) {
state.products.push(product)
},
deleteProduct(state, proId) {
state.products = state.products.filter(obj => obj.pk !== proId)
},
updateProduct(state, proId) {
state.products = state.products.filter(obj => obj.pk !== proId)
}
}
Here again I added updateProduct.
Then in my Products.vue:
......
<b-tbody>
<b-tr v-for="(pro, index) in products" :key="index">
<b-td>{{ index }}</b-td>
<b-td variant="success">{{ pro.name }}</b-td>
<b-td>{{ pro.price }}</b-td>
<b-td>
<b-button variant="outline-primary" v-b-modal="'myModal' + index">Edit</b-button>
<b-modal v-bind:id="'myModal' + index" title="BootstrapVue">
<input type="text" :placeholder="pro.name" v-model="name">
<input type="number" :placeholder="pro.price" v-model="price">
<b-button type="submit" #click="updateProduct(pro.pk)" variant="outline-primary">Update</b-button>
</b-modal>
</b-td>
<b-td><b-button #click="deleteProduct(pro.pk)" variant="outline-primary">Delete</b-button></b-td>
</b-tr>
.....
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: "Products",
data() {
return {
name: "",
price: "",
};
},
computed: mapState({
products: state => state.products.products
}),
methods: mapActions('products', [
'addProduct',
'deleteProduct',
'updateProduct'
]),
created() {
this.$store.dispatch('products/getProducts')
}
};
</script>
Everything works fine except the PUT action to update a product. I figured that you have to use the ID of a product to be able to edit it with PUT. So that's why I used the same snippet as DELETE. But right now I am still deleting it instead of editing.
I also used now placeholder to display the text of a product entry, which is also not the correct way.. I want to use the modal to edit a product entry and then update it.
Can someone point me in the right direction?

React - how to render nested object with renderItem

I'm a bit struggling with rendering nested object in my front end. I know there is a map function might be helpful, but I'm not sure how I can get it to work in my case (with renderItem). The back end I'm using are Rest API framework and Django.
This is what JSON structure looks like.
{
"id": 1,
"name": "Job 1",
"address": [
{
"id": 4,
"street": "65 Karamea Street",
"suburb": "Beautiful Surburb",
"town": "Christchurch",
"postcode": "8001",
"address_type": "h"
}
],
"franchise": {
"id": 2,
"name": "Company ABC",
"person": 2,
"legal_name": "Company ABC Ltd.",
"created": "2019-08-09T09:40:35.697582Z",
"modified": "2019-09-23T03:21:43.258983Z",
"region": {
"id": 4,
"region": "QueensTown"
}
},
"customer1": {
"id": 1,
"last_name": "Tom",
"first_name": "Wilson",
"address": [
{
"id": 11,
"street": "1 Sunset Road",
"suburb": "Auckland",
"town": "Auckland",
"postcode": "1234"
}
]
}
This is the React code:
import React from 'react';
import { List, Card } from 'antd';
const Job = props => {
return (
<React.Fragment>
<List
grid={{
gutter: 16,
xs: 1,
sm: 2,
md: 4,
lg: 4,
xl: 6,
xxl: 3,
}}
dataSource={props.data}
renderItem={item => (
<List.Item>
<Card title={<a href={`/job/${item.id}`}>{item.customer1.first_name}</a>}>{item.franchise.legan_name}
<span> | Based on </span>
{item.name}
</Card>
</List.Item>
)}
/>
</React.Fragment>
);
};
export default Job;
Within the card object, I wish to render the first_name of customer1 and the legal_name of franchise. Apparently item.customer1.first_name and item.franchise.legal_name aren't working.
I'm NEW to React, any help would be much appreciated! It would be great if you could provide some actual code just to be specific.
Many thanks in advance.
You are not sending array of jobs but an object to List.
Changes done:-
1) Converted the passed object into array.
2) Corrected name of attribute item.franchise.legal_name
Below is the working code:-
import React from "react";
import ReactDOM from "react-dom";
import { List, Card } from "antd";
class App extends React.Component {
render() {
return (
<React.Fragment>
<List
grid={{
gutter: 16,
xs: 1,
sm: 2,
md: 4,
lg: 4,
xl: 6,
xxl: 3
}}
dataSource={new Array(this.props.data)}
renderItem={item => (
<List.Item>
<Card
title={
<a href={`/job/${item.id}`}>{item.customer1.first_name}</a>
}
>
{item.franchise.legal_name}
<span> | Based on </span>
{item.name}
</Card>
</List.Item>
)}
/>
</React.Fragment>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
In order to show your JSON data you should use map in your render()
This is how map works
render(){return(
< div>{this.state.mydata.map(m=><div>{m. name < /div> })
< /div>
);
Now in order to use your nested data
In the map you can map m again (like m.map(...) )
Hope you get the idea and works for you

Ember data, how can i use nested(two-depth) json-format for hasMany Relationship

Generally, Ember HasMany relationship json format is follow,
{ "post" : { id:1, title:"this is title", comments:[1,2], writer: ...} }
But, i want to use next json format (because, my server return like this)
{ "post" : { id:1, title:"this is title",
comments:[
{id:1, bodytext:"blarblar...."},
{id:2, bodytext:"second blarblar...."},
], writer: ...} }
How can I use this?
Isn't there any problem in ember store relationship?
This is a job for EmbeddedRecordsMixin.
If you're implementing the server yourself and it's purpose-built for your ember application, you should consider switching to side-loading instead:
{ "post" : { id: 1,
title: "this is title",
comments: [1,2],
writer: ...
},
"comments": [ { id: 1,
bodytext: "blarblar...."
},
{ id: 2,
bodytext: "second blarblar...."
}
]
}
That way, it would be still just one request, but would work for more complicated structures (other than trees) as well.