I'm tryin to recreate this photo below and I am so close. I have run into a problem and I cannot seem to fix it. In my Snippet the text is not vertical aligned with the other container divs like they are in the photo. How can I align my text so the words are aligned? I have tried using flex box but because some text are longer/shorter then others they are all out of alignment because their positions are fixed.
const storeItems = [
{
name: 'TV',
price: 800.00,
inStock: true,
details: '4K Ultra HD'
},
{
name: 'Phone',
price: 700.00,
inStock: false,
details: '5G'
},
{
name: 'Game Console',
price: 300.00,
inStock: true,
details: 'The latest and greatest'
},
{
name: 'Laptop',
price: 1200.00,
inStock: true,
details: '16GB RAM 1TB SSD'
},
{
name: 'Smart Watch',
price: 200.00,
inStock: false,
details: 'Counts your steps'
},
{
name: 'Headphones',
price: 100.00,
inStock: true,
details: 'Clearest music to be heard'
},
{
name: 'Keyboard',
price: 100.00,
inStock: true,
details: 'Types for you'
},
{
name: 'HDMI Cord',
price: 100.00,
inStock: true,
details: 'HDMI to USB type C'
},
{
name: 'Monitor',
price: 300.00,
inStock: true,
details: '4K Ultra HD'
},
{
name: 'Speaker',
price: 200.00,
inStock: true,
details: 'Clearest music to be heard'
},
{
name: 'Video Game',
price: 60.00,
inStock: true,
details: 'Enjoy for hours'
},
];
storeItems.forEach(function(n, i, a) {
if (n.inStock == true) {
$('.boxes').append('<div class="container">' + '<p>$' + n.price +'</p>' + '<p>' + n.name + '</p>' + '<p>' + n.details + '</p>'
+
'</div>');
}
if (n.inStock == false) {
$('.boxes').append('<p class="notInStock">' + n.name + ': $' +
n.price + ' Not in stock' + '</p>');
}
})
$('#clickMe').appendTo('.boxes');
$('#clickMe').click(function(){
$('#contentContainer').toggleClass('darkModeBackground');
$('.container').toggleClass('darkModeContainers');
$(this).toggleClass('darkModeClickMe');
})
body {
font-family: Helvetica;
background-color: #d3d3d3;
color: black;
}
.shrink-container {
width: 800px;
height: 50px;
margin: 0 auto;
}
.container {
display: flex;
align-content: center;
justify-content: space-between;
align-items: center;
background-color: white;
padding: 0px 10px;
margin: 10px 0;
border-radius: 5px;
}
#clickMe {
display: inline-block;
padding: 10px;
font-family: Helvetica;
border-radius: 3px;
border: 1px solid;
}
#clickMe:hover{
cursor: pointer;
}
.inStock{
}
.notInStock{
display:none;
}
.darkModeBackground{
background-color: black;
color:white;
}
.darkModeContainers{
background-color: #5A5A5A;
color: white;
}
.darkModeClickMe{
border-color: white;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>List of items</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body id="contentContainer">
<div class="shrink-container">
<div id="header">
<h1>Products</h1>
<p>______
<p>
</div>
<div id="appendToMe">
<div class="boxes">
</div>
</div>
</div>
<div id="clickMe">Toggle Dark Mode</div>
<script src="https://code.jquery.com/jquery-3.5.0.slim.min.js" integrity="sha256-MlusDLJIP1GRgLrOflUQtshyP0TwT/RHXsI1wWGnQhs=" crossorigin="anonymous"></script>
<script src="app.js"></script>
</body>
</html>
A solution using <table>.
const storeItems = [
{
name: 'TV',
price: 800.00,
inStock: true,
details: '4K Ultra HD'
},
{
name: 'Phone',
price: 700.00,
inStock: false,
details: '5G'
},
{
name: 'Game Console',
price: 300.00,
inStock: true,
details: 'The latest and greatest'
},
{
name: 'Laptop',
price: 1200.00,
inStock: true,
details: '16GB RAM 1TB SSD'
},
{
name: 'Smart Watch',
price: 200.00,
inStock: false,
details: 'Counts your steps'
},
{
name: 'Headphones',
price: 100.00,
inStock: true,
details: 'Clearest music to be heard'
},
{
name: 'Keyboard',
price: 100.00,
inStock: true,
details: 'Types for you'
},
{
name: 'HDMI Cord',
price: 100.00,
inStock: true,
details: 'HDMI to USB type C'
},
{
name: 'Monitor',
price: 300.00,
inStock: true,
details: '4K Ultra HD'
},
{
name: 'Speaker',
price: 200.00,
inStock: true,
details: 'Clearest music to be heard'
},
{
name: 'Video Game',
price: 60.00,
inStock: true,
details: 'Enjoy for hours'
},
];
storeItems.forEach(function(n, i, a) {
$('#appendToMe').append('<tr><td>$' + n.price +'</td><td>' + n.name + '</td><td>' + n.details + '</td></tr>');
})
$('#clickMe').click(function(){
$('body').toggleClass('darkModeBackground');
$('#appendToMe').toggleClass('dark');
$(this).toggleClass('darkModeClickMe');
})
body {
font-family: Helvetica;
background-color: #d3d3d3;
color: black;
}
#appendToMe {
border-spacing: 0 .5em;
margin-bottom: 2em;
}
tr {
background-color: white;
}
td {
padding: 10px 20px;
}
td:first-child {
border-radius: 5px 0 0 5px;
}
td:last-child {
border-radius: 0 5px 5px 0;
}
#clickMe button {
padding: 10px;
font-family: Helvetica;
border-radius: 3px;
border: 1px solid;
}
.darkModeBackground {
background-color: black;
color:white;
}
#appendToMe.dark tr {
background-color: #5A5A5A;
color: white;
}
.darkModeClickMe {
border-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<h1>Products</h1>
<table id="appendToMe"></table>
<div id="clickMe"><button>Toggle Dark Mode</button></div>
</body>
Related
How to align the width of the cells in the header and in the main part?
I marked the correct option in the picture with green checkmarks.
https://i.stack.imgur.com/PP1w2.png
My example and solution now: https://codepen.io/horus123/pen/YzVOGLQ
<div id="test">
<div class="table">
<div class="table__header">
<div v-for="(item,index) in headers" :key="index" class="table__head-el">
{{ item.title }}
</div>
</div>
<div class="table__body">
<div v-for="(el, indexx) in tableItems" :key="indexx" class="table__row">
<span v-for="(elem, indexxx) in el" :key="indexxx" class="table__field">
{{elem}}
</span>
</div>
</div>
</div>
One option would be to use the same grid for both the header and the main part. In other word the display: grid would be apply to the div.table element. In order to make div.table__head-el and div.table__field cells of this grid div.table__header, div.table__body and table__row must have display: contents. The rest of the CSS must be adapt as well but HTML and JS stay the same (I've added a blank property at the end of tableItems objects so the length of those items match the length of the header)
new Vue({
el: "#test",
data: {
headers: [
{
title: '#'
},
{
title: 'ID', icon: 'height'
},
{
title: 'Номер', icon: 'height'
},
{
title: 'Тип', icon: 'height'
},
{
title: 'Марка', icon: 'height'
},
{
title: 'Логист', icon: 'height'
},
{
title: 'Колонна', icon: 'height'
},
{
title: 'Трекер', icon: 'height'
},
{
title: 'Дата привязки трекера', icon: 'height'
},
{
title: 'Дата последних координат', icon: 'height'
},
{
title: 'Удалена'
},
{
title: 'Дата удаления'
}
],
tableItems: [
{
number: 1,
id: '42537370',
numberCar: 'В855АТ147',
type: 'Тягач',
brand: 'Mercedes-Benz',
logistician: 'Томсон Артём Александрович',
column: 'Андреев Евгений',
tracker: '86793',
dateStart: '29.03.2021 16:42:01',
dateEnd: '07.06.2021 13:49:39',
isDeleted: false,
blank: ''
},
{
number: 1,
id: '42537370',
numberCar: 'В855АТ147',
type: 'Тягач',
brand: 'Mercedes-Benz',
logistician: 'Имя Фамилия',
column: 'Андреев',
tracker: '48671111111193',
dateStart: '29.03.2021 16:42:01',
dateEnd: '07.06.2021 13:49:39',
isDeleted: false,
blank: ''
}
],
},
computed: {
},
methods: {
}
});
html {
--border: 1px solid black;
--border-radius: 8px;
}
.table {
max-width: 100%;
padding: 0 75px;
display: grid;
grid-template-columns: minmax(0, 60px) repeat(11, minmax(0, auto));
}
.table__header, .table__body, .table__row {
display: contents;
}
.table__head-el, .table__field {
padding: 12px 20px;
}
.table__head-el {
border-top: var(--border);
border-bottom: var(--border);
margin-bottom: 20px;
}
.table__head-el, .table__field {
display: grid;
place-items: center;
overflow: hidden;
}
.table__head-el:first-child {
border-left: var(--border);
border-radius: var(--border-radius) 0 0 var(--border-radius);
}
.table__head-el:last-child {
border-right: var(--border);
border-radius: 0 var(--border-radius) var(--border-radius) 0;
}
.table__row:first-child > .table__field {
border-top: var(--border);
}
.table__row:last-child > .table__field {
border-bottom: var(--border);
}
.table__field:first-child {
border-left: var(--border);
}
.table__field:last-child {
border-right: var(--border);
}
.table__row:first-child > .table__field:first-child {
border-top-left-radius: var(--border-radius);
}
.table__row:first-child > .table__field:last-child {
border-top-right-radius: var(--border-radius);
}
.table__row:last-child > .table__field:first-child {
border-bottom-left-radius: var(--border-radius);
}
.table__row:last-child > .table__field:last-child {
border-bottom-right-radius: var(--border-radius);
}
.table__row:hover > .table__field {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="test">
<div class="table">
<div class="table__header">
<div v-for="(item,index) in headers" :key="index" class="table__head-el">
{{ item.title }}
</div>
</div>
<div class="table__body">
<div v-for="(el, indexx) in tableItems" :key="indexx" class="table__row">
<span v-for="(elem, indexxx) in el" :key="indexxx" class="table__field">
{{elem}}
</span>
</div>
</div>
</div>
</div>
I'm trying to use a property of my data in a computed method like this:
data() {
return {
ToDoItems: [
{ id: uniqueId("todo-"), label: "Learn Vue", done: false },
{
id: uniqueId("todo-"),
label: "Create a Vue project with the CLI",
done: true,
},
{ id: uniqueId("todo-"), label: "Have fun", done: true },
{ id: uniqueId("todo-"), label: "Create a to-do list", done: false },
],
};
},
computed: {
listSummary() {
const numberFinishedItems = this.ToDoItems.filter((item) => item.done)
.length;
return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed`;
},
},
But the IDE (Visual Studio Code) and the compiler throw an error:
Property 'ToDoItems' does not exist on type 'ComponentPublicInstance<{}, {}, {}, {}, {}, EmitsOptions, {}, {}, false, ComponentOptionsBase<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions, string, {}>>'.
I'm following the vue.js tutorial of mozilla (https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties#adding_a_summary_counter) but using v3.
Has anything changed that this isn't possible anymore / differently?
Thanks in advance
complete code:
<template>
<div id="app">
<h1>To-Do List</h1>
<to-do-form #todo-added="addToDo"></to-do-form>
<h2 id="list-summary">{{ listSummary }}</h2>
<ul aria-labelledby="list-summary" class="stack-large">
<li v-for="item in ToDoItems" :key="item.id">
<to-do-item :label="item.label" :done="true" :id="item.id"></to-do-item>
</li>
</ul>
</div>
</template>
<script lang="ts">
import uniqueId from "lodash.uniqueid";
import { defineComponent } from "vue";
import ToDoItem from "./components/ToDoItem.vue";
import ToDoForm from "./components/ToDoForm.vue";
export default defineComponent({
name: "App",
components: {
ToDoItem,
ToDoForm,
},
data() {
return {
ToDoItems: [
{ id: uniqueId("todo-"), label: "Learn Vue", done: false },
{
id: uniqueId("todo-"),
label: "Create a Vue project with the CLI",
done: true,
},
{ id: uniqueId("todo-"), label: "Have fun", done: true },
{ id: uniqueId("todo-"), label: "Create a to-do list", done: false },
],
};
},
methods: {
addToDo(toDoLabel: string) {
this.ToDoItems.push({
id: uniqueId("todo-"),
label: toDoLabel,
done: false,
});
},
},
computed: {
listSummary() {
const numberFinishedItems = this.ToDoItems.filter((item) => item.done)
.length;
return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed`;
},
},
});
</script>
<style>
/* Global styles */
.btn {
padding: 0.8rem 1rem 0.7rem;
border: 0.2rem solid #4d4d4d;
cursor: pointer;
text-transform: capitalize;
}
.btn__danger {
color: #fff;
background-color: #ca3c3c;
border-color: #bd2130;
}
.btn__filter {
border-color: lightgrey;
}
.btn__danger:focus {
outline-color: #c82333;
}
.btn__primary {
color: #fff;
background-color: #000;
}
.btn-group {
display: flex;
justify-content: space-between;
}
.btn-group > * {
flex: 1 1 auto;
}
.btn-group > * + * {
margin-left: 0.8rem;
}
.label-wrapper {
margin: 0;
flex: 0 0 100%;
text-align: center;
}
[class*="__lg"] {
display: inline-block;
width: 100%;
font-size: 1.9rem;
}
[class*="__lg"]:not(:last-child) {
margin-bottom: 1rem;
}
#media screen and (min-width: 620px) {
[class*="__lg"] {
font-size: 2.4rem;
}
}
.visually-hidden {
position: absolute;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px);
clip: rect(1px, 1px, 1px, 1px);
clip-path: rect(1px, 1px, 1px, 1px);
white-space: nowrap;
}
[class*="stack"] > * {
margin-top: 0;
margin-bottom: 0;
}
.stack-small > * + * {
margin-top: 1.25rem;
}
.stack-large > * + * {
margin-top: 2.5rem;
}
#media screen and (min-width: 550px) {
.stack-small > * + * {
margin-top: 1.4rem;
}
.stack-large > * + * {
margin-top: 2.8rem;
}
}
/* End global styles */
#app {
background: #fff;
margin: 2rem 0 4rem 0;
padding: 1rem;
padding-top: 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
}
#media screen and (min-width: 550px) {
#app {
padding: 4rem;
}
}
#app > * {
max-width: 50rem;
margin-left: auto;
margin-right: auto;
}
#app > form {
max-width: 100%;
}
#app h1 {
display: block;
min-width: 100%;
width: 100%;
text-align: center;
margin: 0;
margin-bottom: 1rem;
}
</style>
Boussadjra Brahim's answer is "alright" but it doesn't actually address the issue. user16362509 is correct, but doesn't answer the question.. What you need to do is yes, annotate the return types of both your data properties and computed properties so TS knows what's going on. Not only that, but it provides stricter type checking if you annotate all types. I don't believe this issue actually occurs when properly using Vue3's composition API, but the problem does, as you can see, exist in options Api. Try: (Or consider using Composition Api). Weirdly enough, when you annotate the return type of a computed method, it understands the context, just quirks of Vue but it's good practice to have the types explicit.
<script lang="ts">
import uniqueId from "lodash.uniqueid";
import { defineComponent } from "vue";
import ToDoItem from "./components/ToDoItem.vue";
import ToDoForm from "./components/ToDoForm.vue";
export default defineComponent({
name: "App",
components: {
ToDoItem,
ToDoForm,
},
data(): { ToDoItems: Array<{id: *string*, label: string, done: boolean}> } {
return {
ToDoItems: [
{ id: uniqueId("todo-"), label: "Learn Vue", done: false },
{
id: uniqueId("todo-"),
label: "Create a Vue project with the CLI",
done: true,
},
{ id: uniqueId("todo-"), label: "Have fun", done: true },
{ id: uniqueId("todo-"), label: "Create a to-do list", done: false },
],
};
},
methods: {
addToDo(toDoLabel: string): void {
this.ToDoItems.push({
id: uniqueId("todo-"),
label: toDoLabel,
done: false,
});
},
},
computed: {
listSummary(): string {
const numberFinishedItems = this.ToDoItems.filter((item) => item.done)
.length;
return `${numberFinishedItems} out of ${this.ToDoItems.length} items completed`;
},
},
});
</script>
You are already using Vue 3. Why not use composition API with script setup for even better typescript support?
Live demo
<template>
<div id="app">
<h1>To-Do List</h1>
<form #submit.prevent="addToDo">
<input type="text" ref="label" />
<button type="submit">Add</button>
</form>
<h2 id="list-summary">{{ listSummary }}</h2>
<ul aria-labelledby="list-summary" class="stack-large">
<li v-for="item in ToDoItems" :key="item.id">
<input type="checkbox" v-model="item.done" />
{{ item.id }} {{ item.label }}
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
interface ToDoItem {
id: string;
label: string;
done: boolean;
}
const label = ref(null)
const ToDoItems = ref<ToDoItem[]>([
{ id: 1, label: 'Learn Vue', done: false },
{ id: 2, label: 'Create a Vue project with the CLI', done: true },
{ id: 3, label: 'Have fun', done: true },
{ id: 4, label: 'Create a to-do list', done: false },
]);
const addToDo = () => {
ToDoItems.value.push({
id: ToDoItems.value.length + 1,
label: label.value.value,
done: false,
});
label.value.value = '';
};
const listSummary = computed(() => {
return `${ToDoItems.value.filter((item) => item.done).length} out of ${ToDoItems.value.length} items completed`;
});
</script>
VueJS course Robot Builder: https://app.pluralsight.com/player?course=vuejs-fundamentals&author=jim-cooper&name=c8f8ef67-c67b-4a52-b109-9dbf2caae028&clip=3&mode=live
My VueJS-RobotBuilder repo:
https://github.com/leongaban/VueJS-RobotBuilder
I'm currently doing a simple tutorial on VueJS, however getting an error on an imported data object availableParts.
What I don't understand is that I have the json object from parts.js imported correctly in the data object. And I can log it out, however I get an error in the template area I assume?
Full code:
<template>
<div>
<div class="top-row">
<div class="top part">
<img v-bind:src="availableParts.heads[0].src" title="head"/>
<button class="prev-selector">◄</button>
<button class="next-selector">►</button>
</div>
</div>
<div class="middle-row">
<div class="left part">
<img v-bind:src="availableParts.arms[0].src" title="left arm"/>
<button class="prev-selector">▲</button>
<button class="next-selector">▼</button>
</div>
<div class="center part">
<img v-bind:src="availableParts.torso[0].src" title="torso"/>
<button class="prev-selector">◄</button>
<button class="next-selector">►</button>
</div>
<div class="right part">
<img v-bind:src="availableParts.arms[0].src" title="right arm"/>
<button class="prev-selector">▲</button>
<button class="next-selector">▼</button>
</div>
</div>
<div class="bottom-row">
<div class="bottom part">
<img v-bind:src="availableParts.bases[0].src" title="base"/>
<button class="prev-selector">◄</button>
<button class="next-selector">►</button>
</div>
</div>
</div>
</template>
<script>
import availableParts from '../../data/parts';
console.log('availableParts', availableParts);
export default {
name: 'RobotBuilder',
data() {
return availableParts;
},
};
</script>
<style>
.part {
position: relative;
width:165px;
height:165px;
border: 3px solid #aaa;
}
.part img {
width:165px;
}
.top-row {
display:flex;
justify-content: space-around;
}
.middle-row {
display:flex;
justify-content: center;
}
.bottom-row {
display:flex;
justify-content: space-around;
border-top: none;
}
.head {
border-bottom: none;
}
.left {
border-right: none;
}
.right {
border-left: none;
}
.left img {
transform: rotate(-90deg);
}
.right img {
transform: rotate(90deg);
}
.bottom {
border-top: none;
}
.prev-selector {
position: absolute;
z-index:1;
top: -3px;
left: -28px;
width: 25px;
height: 171px;
}
.next-selector {
position: absolute;
z-index:1;
top: -3px;
right: -28px;
width: 25px;
height: 171px;
}
.center .prev-selector, .center .next-selector {
opacity:0.8;
}
.left .prev-selector {
top: -28px;
left: -3px;
width: 144px;
height: 25px;
}
.left .next-selector {
top: auto;
bottom: -28px;
left: -3px;
width: 144px;
height: 25px;
}
.right .prev-selector {
top: -28px;
left: 24px;
width: 144px;
height: 25px;
}
.right .next-selector {
top: auto;
bottom: -28px;
left: 24px;
width: 144px;
height: 25px;
}
.right .next-selector {
right: -3px;
}
</style>
parts.js
const images = require.context('./images', true, /\.png$/);
const parts = {
heads: [
{
id: 1,
description:
'A robot head with an unusually large eye and teloscpic neck -- excellent for exploring high spaces.',
title: 'Large Cyclops',
src: images('./head-big-eye.png'),
type: 'heads',
cost: 1225.5,
},
{
id: 2,
description: 'A friendly robot head with two eyes and a smile -- great for domestic use.',
title: 'Friendly Bot',
src: images('./head-friendly.png'),
cost: 945.0,
type: 'heads',
onSale: true,
},
{
id: 3,
description:
'A large three-eyed head with a shredder for a mouth -- great for crushing light medals or shredding documents.',
title: 'Shredder',
src: images('./head-shredder.png'),
type: 'heads',
cost: 1275.5,
},
{
id: 4,
description:
'A simple single-eyed head -- simple and inexpensive.',
title: 'Small Cyclops',
src: images('./head-single-eye.png'),
type: 'heads',
cost: 750.0,
},
{
id: 5,
description:
'A robot head with three oscillating eyes -- excellent for surveillance.',
title: 'Surveillance Bot',
src: images('./head-surveillance.png'),
type: 'heads',
cost: 1255.5,
},
],
arms: [
{
id: 1,
description: 'An articulated arm with a claw -- great for reaching around corners or working in tight spaces.',
title: 'Articulated',
src: images('./arm-articulated-claw.png'),
type: 'arms',
cost: 275,
},
{
id: 2,
description: 'An arm with two independent claws -- great when you need an extra hand. Need four hands? Equip your bot with two of these arms.',
title: 'Two Clawed',
src: images('./arm-dual-claw.png'),
type: 'arms',
cost: 285,
},
{
id: 3,
description: 'A telescoping arm with a grabber.',
title: 'Grabber',
src: images('./arm-grabber.png'),
type: 'arms',
cost: 205.5,
},
{
id: 4,
description: 'An arm with a propeller -- good for propulsion or as a cooling fan.',
title: 'Propeller',
src: images('./arm-propeller.png'),
type: 'arms',
cost: 230,
onSale: true,
},
{
id: 5,
description: 'A short and stubby arm with a claw -- simple, but cheap.',
title: 'Stubby Claw',
src: images('./arm-stubby-claw.png'),
type: 'arms',
cost: 125,
},
],
torsos: [
{
id: 1,
description: 'A torso that can bend slightly at the waist and equiped with a heat guage.',
title: 'Flexible Gauged',
src: images('./torso-flexible-gauged.png'),
type: 'torsos',
cost: 1575,
},
{
id: 2,
description: 'A less flexible torso with a battery gauge.',
title: 'Gauged',
src: images('./torso-gauged.png'),
type: 'torsos',
cost: 1385,
},
{
id: 2,
description: 'A simple torso with a pouch for carrying items.',
title: 'Gauged',
src: images('./torso-pouch.png'),
type: 'torsos',
cost: 785,
onSale: true,
},
],
bases: [
{
id: 1,
description: 'A two wheeled base with an accelerometer for stability.',
title: 'Double Wheeled',
src: images('./base-double-wheel.png'),
type: 'bases',
cost: 895,
},
{
id: 2,
description: 'A rocket base capable of high speed, controlled flight.',
title: 'Rocket',
src: images('./base-rocket.png'),
type: 'bases',
cost: 1520.5,
},
{
id: 3,
description: 'A single-wheeled base with an accelerometer capable of higher speeds and navigating rougher terrain than the two-wheeled variety.',
title: 'Single Wheeled',
src: images('./base-single-wheel.png'),
type: 'bases',
cost: 1190.5,
},
{
id: 4,
description: 'A spring base - great for reaching high places.',
title: 'Spring',
src: images('./base-spring.png'),
type: 'bases',
cost: 1190.5,
},
{
id: 5,
description: 'An inexpensive three-wheeled base. only capable of slow speeds and can only function on smooth surfaces.',
title: 'Triple Wheeled',
src: images('./base-triple-wheel.png'),
type: 'bases',
cost: 700.5,
},
],
};
export default parts;
You are currently returning the whole availableParts object from data which does not have an availableParts property so vue.js gives you this error.
One way to fix it to return an object with an availableParts property which contains your data:
export default {
name: 'RobotBuilder',
data() {
return { availableParts: availableParts };
},
};
The other way to fix this to directly reference the arms, torsos, etc. arrays in your bindings, e.g:
v-bind:src="heads[0].src"
yea I changed the import name to allAvailableParts and then returned the availableParts pointing to that data. Very strange that it works for him but then maybe this is something newer introduced.
import allAvailableParts from '../../data/parts';
export default {
name: 'RobotBuilder',
data() {
return { availableParts: allAvailableParts };
},
};
I have this JSON coming in to typeahead:
[{"q": "#django", "count": 3}, {"q": "#hashtag", "count": 3}, {"q": "#hashtags", "count": 0}, {"q": "#google", "count": 1}]
And the code of my file to work with typeahead
var hashTags = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('q'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: '/hashtag.json?q=%QUERY',
remote: {
url: '/hashtag.json?q=%QUERY',
wildcard: '%QUERY'
}
});
$('.search-tag-query').typeahead({
hint:true,
highlight: true,
// autoselect: true,
minLength:1,
limit: 10,
},
{
name: 'hashTags',
display: 'q',
// displayKey: 'count',
source: hashTags.ttAdapter(),
templates: {
empty: 'No results...'
}
});
I easily render to my html dropdown suggestions the data that I get from the "q" or the "count".
The problem is that I cant send both of them, as you can see in the code.
How can I send both, so I can show the tags and the count of posts related to them?
Thanks.
Use a custom template
suggestion: function(data) {
return '<p><strong>' + data.q+ '</strong> – ' + data.count+ '</p>';
}
You can concatenate the arrays returned at Bloodhound using $.map() and Array.prototype.concat().
You can then filter suggestions at suggestion property of templates object passed to .typeahead(). At stacksnippets both q and count properties are appended to HTML as suggestions for each match of either q or count values.
$(function() {
var data = [{"q": "#django", "count": 3}, {"q": "#hashtag", "count": 3}, {"q": "#hashtags", "count": 0}, {"q": "#google", "count": 1}];
var suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: $.map(data, function(d) {
return {
value: d.q,
suggest: d
}
})
// here we concatenate the two arrays
.concat($.map(data, function(d) {
return {
value: d.count,
suggest: d
}
}))
});
suggestions.initialize();
$(".bs-example .typeahead").typeahead({
minLength: 1,
hint: true,
highlight: true
}, {
name: "suggestions",
displayKey: "value",
templates: {
suggestion: function(data) {
console.log(data);
var details = "<div class=resultContainer>"
+ data.value
+ "<br>"
+ (data.suggest.count == data.value
? data.suggest.q
: data.suggest.count)
+ "</div>";
return details
}
},
source: suggestions.ttAdapter()
});
})
.bs-example {
font-family: sans-serif;
position: relative;
margin: 100px;
}
.typeahead,
.tt-query,
.tt-hint {
border: 2px solid #CCCCCC;
border-radius: 8px;
font-size: 24px;
height: 30px;
line-height: 30px;
outline: medium none;
padding: 8px 12px;
width: 200px;
}
.typeahead {
background-color: #FFFFFF;
}
.typeahead:focus {
border: 2px solid #0097CF;
}
.tt-query {
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset;
}
.tt-hint {
color: #999999;
}
.tt-dropdown-menu {
background-color: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 8px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
margin-top: 12px;
padding: 8px 0;
width: 422px;
}
.tt-suggestion {
font-size: 24px;
line-height: 24px;
padding: 3px 20px;
}
.tt-suggestion.tt-is-under-cursor {
background-color: #0097CF;
color: #FFFFFF;
}
.tt-suggestion p {
margin: 0;
}
.resultDesc,
.resultLabel {
font-size: 14px;
font-style: italic;
}
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="https://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<div class="bs-example">
<input type="text" class="typeahead tt-query" placeholder="q and count" />
</div>
Thank's to everyone, now I understand how it works and specially to Madalin Ivascu. I improved my code and it worked by this way:
var hashTags = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('q'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: '/hashtag.json?q=%QUERY',
remote: {
url: '/hashtag.json?q=%QUERY',
wildcard: '%QUERY'
}
});
$('.search-tag-query').typeahead({
hint:true,
highlight: true,
// autoselect: true,
minLength:1,
limit: 10,
},
{
name: 'hashTags',
display: 'q',
// displayKey: 'count',
source: hashTags.ttAdapter(),
templates: {
// empty: 'No results...',
suggestion: function (data) {
return '<p><strong>' + data.q + '</strong> – ' + data.count + '</p>';
}
}
});
I am using given code to export html page to pdf by using drawDom method:
$(function(){
$('#ExportToPdf').on("click", function (e) {
var selectedTab = $('.selected-tab').attr("id");
selectedTab = selectedTab.replace("tab-", "#");
var fileName = $(selectedTab).find($('.report-title')).text().replace(' ', '_');
kendo.drawing.drawDOM($(selectedTab))
.then(function (group) {
// Render the result as a PDF file
return kendo.drawing.exportPDF(group, {
paperSize: "auto",
margin: { left: "1cm", top: "1cm", right: "1cm", bottom: "1cm" }
});
})
.done(function (data) {
// Save the PDF file
kendo.saveAs({
dataURI: data,
fileName: fileName + ".pdf"
});
});
});
});
But result is given below for Arabic characters
I want this result:
I tried every thing what I get on internet.
Adding different types of fonts for unicode and kendo builtin fonts but all in vein.
This question is 8 months old, so you might have found a solution by now.
I just wanted to share my own solution, which is a bit of a hack, but at least it works for me.
Basically, you want to flip the text in the html using the special command:
For example - grid.client.name (grid.client.name is just an example, replace with where you store the arabic names. Repeat for each cell that contains arabic text).
You will notice now that the text in the pdf is no longer shrinked - but it actually has the wrong direction now. How to fix this? - well,
you simply reverse the arabic text in the code (so basically we reverse the text twice). An example method to reverse a string:
function reverseString(str) {
var newString = "";
for (var i = str.length - 1; i >= 0; i--) {
newString += str[i];
}
return newString;
}
Apply this to all of your data that contains arabic text.
If you've done both of these things, it should now appear correctly after exporting to pdf.
Good Luck.
Here is KENDO UI tutorial and it works fine for me.Can you rewrite your code by analyze this code? If the problem is still continue then we try to find solution again.
<script>
// Import DejaVu Sans font for embedding
// NOTE: Only required if the Kendo UI stylesheets are loaded
// from a different origin, e.g. cdn.kendostatic.com
kendo.pdf.defineFont({
"DejaVu Sans" : "https://kendo.cdn.telerik.com/2016.2.607/styles/fonts/DejaVu/DejaVuSans.ttf",
"DejaVu Sans|Bold" : "https://kendo.cdn.telerik.com/2016.2.607/styles/fonts/DejaVu/DejaVuSans-Bold.ttf",
"DejaVu Sans|Bold|Italic" : "https://kendo.cdn.telerik.com/2016.2.607/styles/fonts/DejaVu/DejaVuSans-Oblique.ttf",
"DejaVu Sans|Italic" : "https://kendo.cdn.telerik.com/2016.2.607/styles/fonts/DejaVu/DejaVuSans-Oblique.ttf",
"WebComponentsIcons" : "https://kendo.cdn.telerik.com/2017.1.223/styles/fonts/glyphs/WebComponentsIcons.ttf"
});
</script>
<!-- Load Pako ZLIB library to enable PDF compression -->
<script src="https://kendo.cdn.telerik.com/2017.2.621/js/pako_deflate.min.js"></script>
<script>
$(document).ready(function() {
$(".export-pdf").click(function() {
// Convert the DOM element to a drawing using kendo.drawing.drawDOM
kendo.drawing.drawDOM($(".content-wrapper"))
.then(function(group) {
// Render the result as a PDF file
return kendo.drawing.exportPDF(group, {
paperSize: "auto",
margin: { left: "1cm", top: "1cm", right: "1cm", bottom: "1cm" }
});
})
.done(function(data) {
// Save the PDF file
kendo.saveAs({
dataURI: data,
fileName: "HR-Dashboard.pdf",
proxyURL: "https://demos.telerik.com/kendo-ui/service/export"
});
});
});
$(".export-img").click(function() {
// Convert the DOM element to a drawing using kendo.drawing.drawDOM
kendo.drawing.drawDOM($(".content-wrapper"))
.then(function(group) {
// Render the result as a PNG image
return kendo.drawing.exportImage(group);
})
.done(function(data) {
// Save the image file
kendo.saveAs({
dataURI: data,
fileName: "HR-Dashboard.png",
proxyURL: "https://demos.telerik.com/kendo-ui/service/export"
});
});
});
$(".export-svg").click(function() {
// Convert the DOM element to a drawing using kendo.drawing.drawDOM
kendo.drawing.drawDOM($(".content-wrapper"))
.then(function(group) {
// Render the result as a SVG document
return kendo.drawing.exportSVG(group);
})
.done(function(data) {
// Save the SVG document
kendo.saveAs({
dataURI: data,
fileName: "HR-Dashboard.svg",
proxyURL: "https://demos.telerik.com/kendo-ui/service/export"
});
});
});
var data = [{
"source": "Approved",
"percentage": 237
}, {
"source": "Rejected",
"percentage": 112
}];
var refs = [{
"source": "Dev",
"percentage": 42
}, {
"source": "Sales",
"percentage": 18
}, {
"source": "Finance",
"percentage": 29
}, {
"source": "Legal",
"percentage": 11
}];
$("#applications").kendoChart({
legend: {
position: "bottom"
},
dataSource: {
data: data
},
series: [{
type: "donut",
field: "percentage",
categoryField: "source"
}],
chartArea: {
background: "none"
},
tooltip: {
visible: true,
template: "${ category } - ${ value } applications"
}
});
$("#users").kendoChart({
legend: {
visible: false
},
seriesDefaults: {
type: "column"
},
series: [{
name: "Users Reached",
data: [340, 894, 1345, 1012, 3043, 2013, 2561, 2018, 2435, 3012]
}, {
name: "Applications",
data: [50, 80, 120, 203, 324, 297, 176, 354, 401, 349]
}],
valueAxis: {
labels: {
visible: false
},
line: {
visible: false
},
majorGridLines: {
visible: false
}
},
categoryAxis: {
categories: [2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011],
line: {
visible: false
},
majorGridLines: {
visible: false
}
},
chartArea: {
background: "none"
},
tooltip: {
visible: true,
format: "{0}",
template: "#= series.name #: #= value #"
}
});
$("#referrals").kendoChart({
legend: {
position: "bottom"
},
dataSource: {
data: refs
},
series: [{
type: "pie",
field: "percentage",
categoryField: "source"
}],
chartArea: {
background: "none"
},
tooltip: {
visible: true,
template: "${ category } - ${ value }%"
}
});
$("#grid").kendoGrid({
dataSource: {
type: "odata",
transport: {
read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Customers"
},
pageSize: 15,
group: { field: "ContactTitle" }
},
height: 450,
groupable: true,
sortable: true,
selectable: "multiple",
reorderable: true,
resizable: true,
filterable: true,
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
columns: [
{
field: "ContactName",
template: "<div class=\'customer-name\'>#: ContactName #</div>",
title: "Contact",
width: 200
},{
field: "ContactTitle",
title: "Contact Title",
width: 220
},{
field: "Phone",
title: "Phone",
width: 160
},{
field: "CompanyName",
title: "Company Name"
},{
field: "City",
title: "City",
width: 160
}
]
});
});
</script>
<style>
/*
Use the DejaVu Sans font for display and embedding in the PDF file.
The standard PDF fonts have no support for Unicode characters.
*/
.k-widget {
font-family: "DejaVu Sans", "Arial", sans-serif;
font-size: .9em;
}
</style>
<style>
.export-app {
display: table;
width: 100%;
height: 750px;
padding: 0;
}
.export-app .demo-section {
margin: 0 auto;
border: 0;
}
.content-wrapper {
display: table-cell;
vertical-align: top;
}
.charts-wrapper {
height: 250px;
padding: 0 0 20px;
}
#applications,
#users,
#referrals {
display: inline-block;
width: 50%;
height: 240px;
vertical-align: top;
}
#applications,
#referrals {
width: 24%;
height: 250px;
}
.customer-photo {
display: inline-block;
width: 40px;
height: 40px;
border-radius: 50%;
background-size: 40px 44px;
background-position: center center;
vertical-align: middle;
line-height: 41px;
box-shadow: inset 0 0 1px #999, inset 0 0 10px rgba(0,0,0,.2);
}
.customer-name {
display: inline-block;
vertical-align: middle;
line-height: 41px;
padding-left: 10px;
}
</style>
<!DOCTYPE html>
<html>
<head>
<base href="https://demos.telerik.com/kendo-ui/pdf-export/index">
<style>html { font-size: 14px; font-family: Arial, Helvetica, sans-serif; }</style>
<title></title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.2.621/styles/kendo.common-material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.2.621/styles/kendo.material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.2.621/styles/kendo.material.mobile.min.css" />
<script src="https://kendo.cdn.telerik.com/2017.2.621/js/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2017.2.621/js/jszip.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2017.2.621/js/kendo.all.min.js"></script>
</head>
<body>
<div id="example">
<div class="box wide hidden-on-narrow">
<h4>Export page content</h4>
<div class="box-col">
<button class='export-pdf k-button'>Export as PDF</button>
</div>
<div class="box-col">
<button class='export-img k-button'>Export as Image</button>
</div>
<div class="box-col">
<button class='export-svg k-button'>Export as SVG</button>
</div>
</div>
<div class="demo-section k-content export-app wide hidden-on-narrow">
<div class="demo-section content-wrapper wide">
<div class="demo-section charts-wrapper wide">
<div id="users"></div>
<div id="applications"></div>
<div id="referrals"></div>
</div>
<div id="grid"></div>
</div>
</div>
<div class="responsive-message"></div>
</div>
</body>
</html>
I made temporary solution that I convert the report into canvas then I exported to pdf. I html2canvas to export html in to canvas. if any one find another solution please let me know.
$('#ExportToPdf').on("click", function (e) {
html2canvas(document.querySelector("#widget-47")).then(canvas => {
$("#widget-47").hide();
$("#widget-47").parent().append(canvas);
});
setTimeout(function () {
kendo.drawing.drawDOM($('#kendo-wrapper'))
.then(function (group) {
// Render the result as a PDF file
return kendo.drawing.exportPDF(group, {
paperSize: "auto",
margin: { left: "1cm", top: "1cm", right: "1cm", bottom: "1cm" }
});
})
.done(function (data) {
// Save the PDF file
kendo.saveAs({
dataURI: data,
fileName: "report.pdf"
});
$("#widget-47").siblings().remove();
$("#widget-47").show();
});
}, 3000);
});