I create a mobile application with covid19 real time data. i have used api.but i can't access json data from it. when it runs object object error is showing.here is the current output
here is the data inside the api.here is the api data.
import { Component } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
import { LoadingController } from '#ionic/angular';
import { finalize } from 'rxjs/operators';
import { NULL_EXPR } from '#angular/compiler/src/output/output_ast';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
data : string;
error: string;
loading: any;
obj: string;
updatedDateSL: string;
constructor(private http: HttpClient,public loadingController: LoadingController) {
this.data='';
this.error='';
this.obj='';
this.updatedDateSL='';
}
async ionViewWillEnter() {
await this.presentLoading();
// Load the data
this.prepareDataRequest()
.pipe(
finalize(async () => {
// Hide the loading spinner on success or error
await this.loading.dismiss();
})
)
.subscribe(
data=> { //data is a java script object that is why it can stringfy.
//updatedDateSL = data.data.update_date_time; // Set the data to display in the template
this.data = JSON.stringify(data); //converts to string
this.obj=JSON.parse(this.data); //info is a javascript objct
//var totCasesSL = info.data.local_total_cases;
//var totHospitalSL = data.local_total_number_of_individuals_in_hospitals;
//var totRecoverSL = data.local_recovered;
//var totDeathSL = data.local_deaths;
//var newSL = data.local_new_cases;
//var newDeathSL = data.local_new_deaths;
//var totActiveSL = data.local_active_cases;
//alert(info.update_date_time);
},
err => {
// Set the error information to display in the template
this.error = `An error occurred, the data could not be retrieved: Status: ${err.status}, Message: ${err.statusText}`;
}
);
}
async presentLoading() {
// Prepare a loading controller
this.loading = await this.loadingController.create({
message: 'Loading...'
});
// Present the loading controller
await this.loading.present();
}
private prepareDataRequest(): Observable<object> {
// Define the data URL
//const dataUrl = 'https://api.nigelbpeck.com/';
const dataUrl = 'https://hpb.health.gov.lk/api/get-current-statistical/';
// Prepare the request
return this.http.get(dataUrl);
}
}
here is the html file.
/<ion-header>
<ion-toolbar>
<ion-title>
Ionic 4 Example App
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<div class="ion-padding">
<p>Data will be presented here...</p>
<p *ngIf="!error;else errorContent">{{ obj ? obj : '-' }}</p>
<ng-template #errorContent><p><span style="color: red;">{{error}}</span></p></ng-template>
</div>
</ion-content>
i need to get local_new_cases and local_total_cases.Api connction is working here is the event that i run application sucessfly.final sucessful output.
To show data in html you need to stringify the data so you should use data instead of obj in html . Update html code from
<ion-content>
<div class="ion-padding">
<p>Data will be presented here...</p>
<p *ngIf="!error;else errorContent">{{ obj ? obj : '-' }}</p>
<ng-template #errorContent><p><span style="color: red;">{{error}}</span></p></ng-template>
</div>
</ion-content>
to
<ion-content>
<div class="ion-padding">
<p>Data will be presented here...</p>
<p *ngIf="!error;else errorContent">{{ data ? data : '-' }}</p>
<ng-template #errorContent><p><span style="color: red;">{{error}}</span></p></ng-template>
</div>
</ion-content>
Related
I want to develop an interface that uploads a file with Vue js from my backend in django, I can't figure out the problem. but here is the error I got: "POST http://127.0.0.1:8000/upload/ 500 (Internal Server Error)"
here the class "upload" in view.py:
def upload(request):
obj = None
dict={}
if request.method == 'POST':
repo = Repository(username="fedoraAdmin",password="fedora2022")
obj = repo.get_object(type=FileObject)
obj.file.content = request.FILES['file']
obj.file.mimetype=request.FILES['file'].content_type
obj.file.label=request.FILES['file'].name
obj.label = request.POST['label']
obj.save()
dict=obj.index_data()
print(dict)
return JsonResponse(dict)
my form in forms.py:
class UploadForm(forms.Form):
label = forms.CharField(max_length=255, # fedora label maxes out at 255 characters
help_text='Preliminary title for the new object. 255 characters max.')
file = forms.FileField()
Finally, my vue js code:
<template>
<div class="container"> <h1 class="title mb-6">Uploader</h1> <div>
<form #submit.prevent="submitFile" enctype="multipart/form-data">
<input type="file" #change="uploadFile" ref="file">
<button>Upload!</button>
</form>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'archivage',
mounted() {
document.title = 'Archivage | fedora'
} ,
methods: {
uploadFile() {
this.file = this.$refs.file.files[0];
this.label=this.$refs.file.files[0].name
},
performUpload() {
const formData = {
label: this.label,
file: this.file}
axios
.post('/upload/',formData)
.then(response => {
console.log(response.data) ;
}) .catch(error => {
console.log(error)
})
},
submitFile: function(){
this.performUpload() },
}
}
</script>
I am developing an online store for a client using Angular/Spring Boot. To keep things simple and to the point, I am storing the users shopping cart data in localStorage which is working fine. However, for the shopping cart to be updated with the users products, I have to refresh the page. I would like the app to just update without having to refresh the page. Im sure this is probably very trivial, but I cant get it to work!
I have tried looking at a few different sources :
Refreshing Page with Angular
Angular Source
I have tried using the this.router.routeReuseStrategy.shouldReuseRoute = function () { return false; }; within the components constructor but again didnt have any luck.
The main bits of code are as follows:
CartService - On construction we fetch the data from localStorage:
constructor(private http: HttpServiceService) {
this.getCartDetailsByUser();
}
getCartDetailsByUser() {
let data = JSON.parse(localStorage.getItem("products"));
this.cartObj = data;
if (data !== null) {
this.cartQty = data.length;
console.log(data);
}
}
When the user clicks "Add to Cart", this function is called, which adds the data to the local storage, but i need to update the DOM with whats in the cart and the cart length!
addCart(product) {
let products = [];
console.log(product);
if (localStorage.getItem("products")) {
products = JSON.parse(localStorage.getItem("products"));
}
products.push({
productId: product.productId,
image: product.image,
price: product.price,
});
localStorage.setItem("products", JSON.stringify(products));
}
I dont want to manually refresh the whole page for something so simple, I would just like the DOM to update with no need to refresh.
The header.component.html is what displays the shopping cart with its quantity etc
<div class="cart cart box_1 checkout-count-wrap">
<form action="#" method="post" class="last">
<button
class="w3view-cart"
type="submit"
(click)="openCheckoutModel()"
name="submit"
value=""
>
<p class="total_count_checkout">{{cart_qty}}</p>
<i class="fa fa-cart-arrow-down" aria-hidden="true"></i>
</button>
</form>
</div>
With the linked .ts class (header.component.ts). In the constructor, I get the qty which gets passed to the html above.
constructor(
private router: Router,
private cartService: CartServiceService,
private http: HttpServiceService
) {
this.cartService.cartServiceEvent.subscribe((data) => {
this.cart_qty = this.cartService.getQty();
});
}
When the user clicks on the shopping cart on the DOM, the following code is executed which gives a popup with the products etc in the cart:
openCheckoutModel() {
this.cartObj = this.cartService.getCartOBj();
this.cartTotalPrice = this.cartService.cartTotalPrice;
this.mainDialogType = "checkout";
}
Which in turn then displays the data in the shopping cart.
ALL of this works correctly apart from the cart not updating on the fly!!
Any help would be greatly appreciated!! :D
**** EDITED ANSWER ****
OK, so from the answer on the question, I have made a few small changes with the Observable pattern.. This half works - It updates the quantity of the cart on the fly (incrementing the number + 1 when a user clicks 'Add to Cart'). It also stores the product in the json object in localStorage as it did before. However, the actual items in the cart now do not show on the DOM, where as they did before. The data definitely exists with the correct products added being stored in localStorage, but now there seems to be some issues with the DOM displaying what's in it (productName, price etc)
Will this be due to another Observable being needed to track the item data in the cart? This may become clearer when I share my code (full classes)..
So here is the checkout-component.ts with the method being highlighted with *****
import { Component, OnInit } from "#angular/core";
import { CartServiceService } from "../service/cart-service.service";
import { HttpServiceService } from "../http-service.service";
import { Router } from "#angular/router";
#Component({
selector: "app-checkout",
templateUrl: "./checkout.component.html",
styleUrls: ["./checkout.component.css"],
})
export class CheckoutComponent implements OnInit {
cartObj = [];
cartTotalPrice: any;
pay_type = "cash_on_delivery";
delivery_address = "";
constructor(
private router: Router,
private cartService: CartServiceService,
private http: HttpServiceService
) {}
ngOnInit() {
this.getCartDetailsByUser();
//below function will be triggerd from when removing and qty is changing..
this.cartService.cartServiceEvent.subscribe((data) => {
this.cartObj = this.cartService.getCartOBj();
this.cartTotalPrice = this.cartService.cartTotalPrice;
});
}
qtyChange(qty, cartObj) {
var request = {
cartId: cartObj.id,
quantity: qty,
price: cartObj.price * qty,
};
this.http
.postRequestWithToken("api/addtocart/updateQtyForCart", request)
.subscribe(
(data: any) => {
this.cartService.getCartDetailsByUser(); //for updating in the application..
},
(error) => {
alert("Error while fetching the cart Details");
}
);
}
getCartDetailsByUser() {
let data = JSON.parse(localStorage.getItem("products"));
this.cartObj = data;
this.cartTotalPrice = this.getTotalAmounOfTheCart();
console.log("Cart Obj", this.cartObj);
console.log("Total", this.cartTotalPrice);
}
// getCartDetailsByUser(){
// this.http.postRequestWithToken("api/addtocart/getCartsByUserId",{}).subscribe((data:any)=>{
// this.cartObj = data;
// this.cartTotalPrice = this.getTotalAmounOfTheCart();
// },error=>{
// alert("Error while fetching the cart Details");
// })
// }
getTotalAmounOfTheCart() {
let obj = this.cartObj;
let totalPrice = 0;
for (var o in obj) {
totalPrice = totalPrice + parseFloat(obj[o].price);
}
return totalPrice.toFixed(2);
}
removeCartById(cartObj) {
if (confirm("Are you sure want to delete..?")) {
let id = cartObj.id;
this.cartService.removeCart(id);
}
}
checkoutCart() {
if (this.delivery_address == "") {
alert("Delivery address should not be empty");
return;
}
if (this.pay_type == "cash_on_delivery") {
let request = {
total_price: this.cartTotalPrice,
pay_type: "COD",
deliveryAddress: this.delivery_address,
};
this.http
.postRequestWithToken("api/order/checkout_order", request)
.subscribe(
(data: any) => {
alert("checkout process completed.Your Order is processed..");
this.cartService.getCartDetailsByUser();
this.router.navigate([""]);
},
(error) => {
alert("Error while fetching the cart Details");
}
);
} else {
alert("Payment Integration is not yet completed.");
}
}
}
Then the corresponding checkout-component.html
<div style="display: block;" id="w3lssbmincart">
<ul>
<li *ngFor="let cart of cartObj" class="sbmincart-item sbmincart-item-changed">
<div class="sbmincart-details-name">
<a class="sbmincart-name">{{cart.name}}</a>
</div>
<div class="sbmincart-details-quantity">
<select [(ngModel)]="cart.qty" (change)="qtyChange($event.target.value,cart)">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
</select>
</div>
<div class="sbmincart-details-remove">
<button (click)="removeCartById(cart)" type="button" class="sbmincart-remove" data-sbmincart-idx="0">×</button>
</div>
<div class="sbmincart-details-price">
<span class="sbmincart-price">{{cart.price}}</span>
</div>
</li>
</ul>
<div class="sbmincart-footer">
<div class="sbmincart-subtotal radio-wrap">
<span><input [(ngModel)]="pay_type" value="cash_on_delivery" type="radio" name="pay_type" /><span class="radio_text">Cash on Delivery</span></span>
<span><input [(ngModel)]="pay_type" value="online" name="pay_type" type="radio"/><span class="radio_text">Online</span></span>
</div>
<div class="sbmincart-subtotal">
<textarea placeholder="Enter the Delivery address" [(ngModel)]="delivery_address"></textarea>
</div>
<div class="sbmincart-subtotal">
Subtotal: <span class="price">${{cartTotalPrice}}</span>
</div>
<div>
<button (click)="checkoutCart()">Place Order</button>
</div>
</div>
<input type="hidden" name="bn" value="sbmincart_AddToCart_WPS_US">
</div>
</div>
</div>
</div>
Which simply loops over the cartObj stored within the service class.
I modified the header-component.ts (where the cart exists) to look like this:
import { Component, OnInit } from "#angular/core";
import { HttpServiceService } from "../http-service.service";
import { CartServiceService } from "../service/cart-service.service";
import { timingSafeEqual } from "crypto";
import { Router } from "#angular/router";
#Component({
selector: "app-header",
templateUrl: "./header.component.html",
styleUrls: ["./header.component.css"],
})
export class HeaderComponent implements OnInit {
isOpenLoginDialog = false;
currentDropDownMenu = "";
dialogType = "login";
mainDialogType = "";
isLogin = false;
mobile = "123456789";
password = "test";
cartObj = [];
cart_qty = 0;
cartTotalPrice = 0;
register = { name: "", email: "", mobile: "", password: "", re_password: "" };
welcomeUsername = "";
items$ = this.cartService.items$;
constructor(
private router: Router,
private cartService: CartServiceService,
private http: HttpServiceService
) {
let request = {};
this.http.postRequest("api/status", request).subscribe(
(data) => {
console.log("test", data);
},
(error) => {
alert("Server connection error " + error);
}
);
this.cartService.cartServiceEvent.subscribe((data) => {
this.cart_qty = this.cartService.getQty();
this.cartObj = this.cartService.getCartOBj();
});
}
logout() {
this.http.logout();
this.isLogin = false;
}
ngOnInit() {}
checkout_btn() {
this.router.navigate(["checkout"]);
}
openCheckoutModel() {
this.cartObj = this.cartService.getCartOBj();
this.cartTotalPrice = this.cartService.cartTotalPrice;
this.mainDialogType = "checkout";
}
openDialog() {
this.mainDialogType = "login";
}
dialogTypeInside(type) {
if (this.dialogType != type) this.dialogType = type;
}
closeDialog() {
this.mainDialogType = "";
}
curentDropDown(currentDropdownMenuName) {
if (this.currentDropDownMenu == currentDropdownMenuName) {
this.currentDropDownMenu = "";
} else {
this.currentDropDownMenu = currentDropdownMenuName;
}
}
}
Notice the openCheckoutModel() method which gets called which should pass the data down into the html component.
The data definitely exists as I am printing it out via console.log in the service class. The screen shot is below:
The front end DOM also shows there are 4 items in the cart:
However, the html does not display the actual data on the cart page:
From what I can see, i am looping over the data that definitely exists, however it does not show on the browser. I also get no errors :(
I hope this makes sense!
For peace of mind and for performance reasons, I would recommend you to go the RxJS way and turn things observable.
Short answer:
Here is a stackblitz example I could quickly make for you:
https://stackblitz.com/edit/angular-ivy-kgpicq
Long Answer:
I would recommend you structure your class as:
class CartService {
constructor() {
let cartItems = JSON.parse(localstorage.getItem('products'));
if (!cartItems) {
cartItems = []
}
this.itemsSubject.next(cartItems);
}
private itemsSubject = new BehaviorSubject<Product[]>([]);
items$ = itemsSubject.asObservable();
addToCart(item: Product) {
this.items$.pipe(
take(1),
map((products) => {
products.push(item);
localstorage.setItem('products', JSON.stringify(products));
},
).subscribe();
}
}
In your component class:
class ProductsPageComponent {
constructor(private cartService: CartService) {}
items$ = this.cartService.items$;
}
In your template:
<div class="cart cart box_1 checkout-count-wrap">
<form action="#" method="post" class="last">
<button class="w3view-cart" type="submit" (click)="openCheckoutModel()" name="submit" value="">
<p class="total_count_checkout">{{(items$ | async).length}}</p>
<i class="fa fa-cart-arrow-down" aria-hidden="true"></i>
</button>
</form>
</div>
AJAX
You can achieve this with ajax in plain javascript or with jQuery. My preference would go to jQuery.
Some code:
$.get( "/your_link", function( data ) {
$( ".result" ).html( data );
});
I would say you could write a another function in your controller that responds with the cart items. Then you could call the $.get whenever you would like to refresh your items. There's a lot of info on this so don't hesitate to look it up :)
https://api.jquery.com/jQuery.get/
Subscribe
Another solution might be to put a timeout on your subscribe. (this is some code from a project of mine which needed similar functionality to yours.)
In component.ts:
getUsers(): void {
// polling
timer(0, 2500)
.subscribe(() => {
this.userService.getUsers()
.subscribe(data => this.users = data);
});
}
In service.ts:
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.usersUrl);
}
I am trying to send data to my popover component but it doesn't send the data.
Code
HTML
<div class="message" id="conversation" *ngFor="let message of messages.notes">
<ion-row class="message-body">
<ion-col (click)="presentPopover($event)" size="12" class="message-main-sender" *ngIf="message.user.id === user.id; else receiverNote">
// rest of message detail...
</ion-col>
</ion-row>
</div>
component
async presentPopover(ev: any) {
const popover = await this.popoverController.create({
component: MessagesComponent,
cssClass: 'my-custom-class',
event: ev,
translucent: true
});
return await popover.present();
}
popover component (can't get sent data in here )
reply() {
console.log('my_data1 : ', this.navParams.get('data')); // undefined
console.log('my_data2 : ', this.activatedRoute.snapshot.paramMap.get('event')); // null
}
Any idea?
You can use componentProps to send data to your popover
async presentPopover(ev: any) {
const popover = await this.popoverController.create({
component: MessagesComponent,
cssClass: 'my-custom-class',
event: ev,
translucent: true,
componentProps: {
id: 1
}
});
return await popover.present();
}
Get the data in the MessagesComponent with navParams:
constructor(private navParams: NavParams) {}
ngOnInit() {
console.log(this.navParams.get('id')); // 1
}
Send data back to original page
Send data back in your component with dismiss.
this.popoverController.dismiss({ id: 1 });
Ready to receive it inside the constructed presentPopover function
async presentPopover(ev: any) {
const popover = await this.popoverController.create({
// ...
});
popover.onDidDismiss().then((result: object) => {
if (result['data']) {
console.log(result['data'].id) // 1
}
});
return await popover.present();
}
I am using ng2-file-upload to upload the image. Everything works well but after i choose image, it'll display selected image thumbnail.
Please review my stackblitz Link
Thanks in Advance
You can look out this demo may this helps you
Template file:
img element to show image preview
<img [src]="previewImg" *ngIf="previewImg"/>
Class file:
URL.createObjectURL() is a static method that creates a DOMString containing a URL representing the object given in the parameter.
bypassSecurityTrustUrl Bypass security and trust the given value to be a safe style URL, i.e. a value that can be used in hyperlinks or <img src>.
constructor(private sanitizer: DomSanitizer) {} // inject the DomSanitizer
previewImg: SafeUrl;
this.uploader.onAfterAddingFile = (file) => {
console.log('***** onAfterAddingFile ******')
this.previewImg = this.sanitizer.bypassSecurityTrustUrl((window.URL.createObjectURL(file._file)));;
}
Dont forget to include import { DomSanitizer, SafeUrl } from '#angular/platform-browser';
Here is the answer when your image is ready using FileReader show the thumbnail
import { Component, OnInit } from '#angular/core';
import {FileUploader} from 'ng2-file-upload';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
url = 'https://evening-anchorage-3159.herokuapp.com/api/';
ready = false;
thumb="";
uploader = new FileUploader({
url: this.url,
maxFileSize: 1024 * 1024 * 1
});
name = 'Angular 5';
ngOnInit() {
this.uploader.onAfterAddingFile = (file) => {
console.log('***** onAfterAddingFile ******')
var image_file=file._file
const reader = new FileReader();
reader.addEventListener('load', () => {
console.log(reader.result)
this.ready=true;
this.thumb=reader.result.toString();
});
reader.readAsDataURL(image_file);
}
this.uploader.onCompleteItem = (item:any, response:any, status:any, headers:any) => {
console.log('ImageUpload:uploaded:', item, status, response);
};
this.uploader.onCompleteAll = () => {
console.log('******* onCompleteAll *********')
}
this.uploader.onWhenAddingFileFailed = (item: any, filter: any, options: any) => {
console.log('***** onWhenAddingFileFailed ********')
}
}
}
Your HTML looks like this
<hello name="{{ name }}"></hello>
<p>Maximun allowed file size is 1MB</p>
<img [src]="thumb" *ngIf="ready"/>
<input type="file" ng2FileSelect [uploader]="uploader">
<button (click)="uploader.uploadAll()">Upload </button>
I got the solution ,
I have tested on your https://stackblitz.com/edit/angular-ng2-file-upload , try this it is working
export class AppComponent implements OnInit {
url = 'https://evening-anchorage-3159.herokuapp.com/api/';
uploader = new FileUploader({
url: this.url,
maxFileSize: 1024 * 1024 * 1
});
name = 'Angular 5';
//added this two variable here
imageUrlOfLogo:string='';
logoFileNameFile?: File;
ngOnInit() {
// your own code here
}
// added this code here
handleLogoFileInput(file: any) {
var item = file.item(0);
this.logoFileNameFile = file.item(0);
var reader = new FileReader();
reader.onload = (event: any) => {
this.imageUrlOfLogo = event.target.result;
}
reader.readAsDataURL(this.logoFileNameFile as File);
}
}
component.html
<p>Maximun allowed file size is 1MB</p>
<img [src]="imageUrlOfLogo" style="width:50%; height:90px;" *ngIf="logoFileNameFile">
<input type="file" ng2FileSelect [uploader]="uploader" (change)="handleLogoFileInput($event.target.files)">
<button (click)="uploader.uploadAll()">Upload </button>
I'm new to Vue.js but trying to get to grips with it. So far it has gone well and I've got quite far but I am stuck extending a parents template.
I am trying to make dashboard widgets that extend a default widget layout (in Boostrap). Please note that the below code is using Vue, Require, Underscore & Axios.
Parent file - _Global.vue
<template>
<div class="panel panel-default">
<div class="panel-heading">
<b>{{ widgetTitle }}</b>
<div class="pull-right">
<a href="#" v-on:click="toggleMinimized"
v-bind:title="(isMinimized ? 'Show widget' : 'Hide widget')">
<i class="fa fa-fw" v-bind:class="isMinimized ? 'fa-plus' : 'fa-minus'"></i>
</a>
</div>
</div>
<div class="panel-body" v-if="!isMinimized">
<div class="text-center text-muted" v-if="!isLoaded">
<i class="fa fa-spin fa-circle-o-notch"></i><br />
</div>
<parent v-if="isLoaded">
<!-- parent content should appear here when loaded -->
</parent>
</div>
</div>
</template>
<script>
export default {
// setup our widget props
props: {
'minimized': {
'default': false,
'required': false,
'type': Boolean
}
},
// define our data
data: function () {
return {
widgetTitle: 'Set widget title in data',
isLoaded: false,
isMinimized: this.$props.minimized
}
},
// when vue is mounted, open our widget
mounted: function () {
if(!this.isMinimized) {
this.opened();
}
},
// define our methods
methods: {
// store our widget state to database
storeWidgetState: function () {
// set our data to send
let data = {
'action' : 'toggleWidget',
'widget' : this.$options._componentTag,
'state' : !this.isMinimized
};
// post our data to our endpoint
axios.post(axios.endpoint, data);
},
// toggle our minimized data
toggleMinimized: function (e) {
// prevent default
e.preventDefault();
// toggle our minimized state
this.isMinimized = !this.isMinimized;
// trigger opened if we aren't minimized
if(!this.isMinimized) this.opened();
// save our widget state to database
this.storeWidgetState();
},
// triggered when opened from being minimized
opened: function () {
console.log('opened() method is where all widget logic should be placed');
}
}
}
</script>
Child file - Example.vue
Should extend _Global.vue using mixins and then display content within .panel-body
<template>
<div>
I want this content to appear inside the .panel-body div
{{ content }}
<img v-bind:src="image.src" v-bind:alt="image.alt"
v-if="image.src" class="img-responsive" style="margin: 0 auto" />
</div>
</template>
<script>
// import our widgets globals
import Global from './_Global.vue'
export default {
components: {
'parent': {
// what can I possibly put here??
}
},
// use our global mixin for all widgets
mixins: [Global],
// setup our methods for this widget
methods: {
opened: _.debounce(function () {
// make sure this can only be opened once
if(this.hasBeenOpened) return;
this.hasBeenOpened = true;
// temporarily allow axios to make external requests
let axiosHeaders = axios.defaults.headers.common;
let vm = this;
axios.defaults.headers.common = {};
axios.get('https://yesno.wtf/api')
.then(function (res) {
// set our content
vm.content = null;
// set our image content
vm.image.src = res.data.image;
vm.image.alt = res.data.answer;
})
.catch(function (err) {
// set our error text
vm.content = String(err);
})
.then(function () {
// this will always hit..
vm.isLoaded = true;
});
// restore our axios headers for security
axios.defaults.headers.common = axiosHeaders;
}, 300)
},
// additional data
data: function () {
return {
// set our widgets title
widgetTitle: 'Test title',
// logic for the specific widget
hasBeenOpened: false,
content: 'Loaded and ready to go...',
image: {
src: false,
alt: null
}
};
},
}
</script>
Currently my parent template is just completely overwriting my child view. The only way I can get it to work is by explicitly defining the template parameter inside components -> parent: {} but I don't want to have to do that...?
Ok, thanks to Gerardo Rosciano for pointing me the in right direction. I've used to slots to come up with an eventual solution. We then access parent methods and data attributes just to get everything working as it should.
Example.vue - our example widget
<template>
<div>
<widget-wrapper>
<span slot="header">Example widget</span>
<div slot="content">
<img v-bind:src="image.src" v-bind:alt="image.alt"
v-if="image.src" class="img-responsive" style="margin: 0 auto" />
{{ content }}
</div>
</widget-wrapper>
</div>
</template>
<script>
// import our widgets globals
import WidgetWrapper from './_Widget.vue'
export default {
// setup our components
components: {
'widget-wrapper': WidgetWrapper
},
// set our elements props
props: {
'minimized': {
'type': Boolean,
'default': false,
'required': false
}
},
// setup our methods for this widget
methods: {
loadContent: _.debounce(function () {
// make sure this can only be opened once
if(this.hasBeenOpened) return;
this.hasBeenOpened = true;
// temporarily allow axios to make external requests
let axiosHeaders = axios.defaults.headers.common;
let vm = this;
axios.defaults.headers.common = {};
axios.get('https://yesno.wtf/api')
.then(function (res) {
// set our content
vm.content = null;
// set our image content
vm.image.src = res.data.image;
vm.image.alt = res.data.answer;
})
.catch(function (err) {
// set our error text
vm.content = String(err);
})
.then(function () {
// this will always hit..
vm.isLoaded = true;
});
// restore our axios headers for security
axios.defaults.headers.common = axiosHeaders;
}, 300)
},
// additional data
data: function () {
return {
// global param for parent
isLoaded: false,
// logic for the specific widget
hasBeenOpened: false,
content: 'Loaded and ready to go...',
image: {
src: false,
alt: null
}
};
},
}
</script>
_Widget.vue - our base widget that gets extended
<template>
<div class="panel panel-default">
<div class="panel-heading">
<b><slot name="header">Slot header title</slot></b>
<div class="pull-right">
<a href="#" v-on:click="toggleMinimized"
v-bind:title="(minimized ? 'Show widget' : 'Hide widget')">
<i class="fa fa-fw" v-bind:class="minimized ? 'fa-plus' : 'fa-minus'"></i>
</a>
</div>
</div>
<div class="panel-body" v-if="!minimized">
<div class="text-center text-muted" v-if="!isLoaded">
<i class="fa fa-spin fa-circle-o-notch"></i><br />
Loading...
</div>
<div v-else>
<slot name="content"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
// get loaded state from our parent
computed: {
isLoaded: function () {
return this.$parent.isLoaded;
}
},
// set our data element
data: function () {
return {
minimized: false
}
},
// when the widget is mounted, trigger open state
mounted: function () {
this.minimized = this.$parent.minimized;
if(!this.minimized) this.opened();
},
// methods to manipulate our widget
methods: {
// save our widget state to database
storeWidgetState: function () {
// set our data to send
let data = {
'action' : 'toggleWidget',
'widget' : this.$parent.$options._componentTag,
'state' : !this.minimized
};
// post this data to our endpoint
axios.post(axios.endpoint, data);
},
// toggle our minimized state
toggleMinimized: function (e) {
// prevent default
e.preventDefault();
// toggle our minimized state
this.minimized = !this.minimized;
// trigger opened if we aren't minimized
if(!this.minimized) this.opened();
// save our widget state to database
this.storeWidgetState();
},
// when widget is opened, load content
opened: function () {
// make sure we have a valid loadContent method
if(typeof this.$parent.loadContent === "function") {
this.$parent.loadContent();
} else {
console.log('You need to define a loadContent() method on the widget');
}
}
}
}
</script>