custom element error in jointjs when use vue-cli - vue-cli

I got an error when use my element in vue-cli.
I do not why joint.shapes do not contain my element!
test.vue:
// Cannot read property 'Component' of undefined"
<script>
const joint = require('jointjs');
joint.dia.Element.define('example.Component', {
attrs: {
...
},
}, {
markup: ...,
}, {
createComponent: function() {
return new this({
attrs:{
...
}
});
}
});
export default {
name: "FlowCanvas",
mounted() {
var graph=new joint.dia.Graph;
var paper = new joint.dia.Paper({
...
});
var el1=joint.shapes.example.Component.createComponent();
el1.position(100,100);
},
}
</script>

Related

In component data is fetched only when i make some changes in it

I am trying to add markers on map, using a view and component
In view where i call an api and then i pass that data to component using v:bind but console.log in that component shows an empty array, when i make some changes in that component the page reloads and data is fetched following is my code in view and then component.
//Script for View
<script>
import Maps from '../components/Maps.vue';
import LoadingOverlay from '../components/loading.vue';
import MessageHelper from '../helpers/messageHelper';
import RepositoryFactory from '../repositories/RepositoryFactory';
const Catalogs = RepositoryFactory.get('catalogs');
export default {
components: {
Maps,
LoadingOverlay,
},
data() {
return {
zones_and_locations: [],
loadingConfig: {
isLoading: true,
cancellable: true,
onCancelMessage: this.onCancelMessage(),
isFullPage: true,
},
};
},
created() {
this.fetchPlacesData();
},
methods: {
async fetchPlacesData() {
const { data } = await Catalogs.getZoneLocations();
if (data.output === null) {
this.nullDataException();
} else {
const places = [];
data.output.forEach((value, index) => {
places.push(value);
});
this.zones_and_locations = places;
this.loadingConfig.isLoading = false;
}
},
onCancelMessage() {
return MessageHelper.getLoadingCancelMessage();
},
nullDataException() {
this.exceptionMessage = 'Data from API is not available.';
console.log(this.exceptionMessage);
},
},
};
</script>
//Script For Map.vue
<script>
import MarkerClusterer from '#google/markerclusterer';
import GoogleMapsHelper from '../helpers/GoogleMapsHelper';
export default {
name: 'GoogleMap',
props: ['zones_and_locations'],
data() {
return {
country: '',
markers: [],
map: '',
};
},
created() {
this.loadGoogleMaps();
},
methods: {
markerClusterer(map, markers) {
return new MarkerClusterer(
map,
markers,
{ imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m' },
);
},
async loadGoogleMaps() {
try {
const google = await GoogleMapsHelper();
const geoCoder = new google.maps.Geocoder();
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 7,
mapTypeControl: false,
fullscreenControl: false,
});
geoCoder.geocode({ address: 'Singapore' }, (results, status) => {
if (status !== 'OK' || !results[0]) {
throw new Error(status);
}
// set Center of Map
map.setCenter(results[0].geometry.location);
map.fitBounds(results[0].geometry.viewport);
});
this.map = map;
let zones = [];
Array.prototype.forEach.call(this.zones_and_locations, child => {
var obj = {
lat: parseFloat(child.lat),
lng: parseFloat(child.lng),
}
var position = {
position: obj,
};
zones.push(position);
});
if (zones.length > 0) {
const markers = zones.map(x => new google.maps.Marker({ ...x, map }));
this.markerClusterer(map, markers);
}
} catch (error) {
console.error(error);
}
},
},
};
</script>
//Template for View
<template>
<div>
<!-- START::Loading Overlay -->
<LoadingOverlay
v-bind:loadingConfig="loadingConfig">
</LoadingOverlay><!-- END::Loading Overlay -->
<Maps v-bind:zones_and_locations="zones_and_locations"
></Maps>
</div>
</template>
//Template for Component
<template>
<div>
<div id="map" class="google-map"></div>
</div>
</template>
Data from the parent component are loaded asynchronously, so the created lifecycle hook inside the component is executed before the actual data comes, when they're still set as an empty array and are not reactive to change.
You can fix this by setting a watch inside the component, like this:
methods: {
...
},
watch: {
zones_and_locations (newVal, oldVal) {
this.loadGoogleMaps();
}
}
orelse you can set a reference to the child component and invoke its method when data comes:
<!-- main view -->
<Maps
v-bind:zones_and_locations="zones_and_locations"
ref="myMap"
></Maps>
async fetchPlacesData() {
const { data } = await Catalogs.getZoneLocations();
if (data.output === null) {
this.nullDataException();
} else {
const places = [];
data.output.forEach((value, index) => {
places.push(value);
});
this.zones_and_locations = places;
// using $refs
this.$refs.myMap.loadGoogleMaps();
this.loadingConfig.isLoading = false;
}
},

How do I trigger a v-autocomplete "input" event with vue test utils?

I'm trying to trigger the "input" event of a Vuetify v-autocomplete component inside of a unit test. From what I can tell, the unit test is quite similar to the one the Vuetify guys are using, but for some reason the mock method is never called.
The factory method simply returns a component instance made via mount and adds a local store, vue, and vueLoading instance.
Would love a bit of insight here, already lost several hours to this and I'm starting to go a bit mad...
Vuetify version: 1.4.3
Component
<template>
<v-autocomplete
:items="options"
:value="selectedId"
:label="label || $t('user.filter')"
:dark="dark"
:class="fieldClass"
:menu-props="{ contentClass }"
:loading="$loading.isLoading('fetch users')"
item-text="email"
item-value="id"
name="search_by_user"
hide-details
single-line
clearable
#input="updateValue($event)"
#click.native="fetchUsers"
/>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: {
value: {
type: String,
required: false,
default: null
},
label: {
type: String,
required: false,
default: null
},
dark: {
type: Boolean,
required: false,
default: true
},
fieldClass: {
type: String,
required: false,
default: 'select-user-search'
},
contentClass: {
type: String,
required: false,
default: 'user-search'
},
blankItem: {
type: Object,
required: false,
default: null
}
},
data () {
return {
selectedId: this.value
}
},
computed: {
...mapGetters(['users']),
options () { return this.blankItem ? [this.blankItem].concat(this.users) : this.users }
},
created () {
if (this.value) this.fetchUsers()
},
methods: {
fetchUsers () {
if (this.users.length) return
this.$store.dispatch('FETCH_USERS', {
fields: ['id', 'email'],
filter: { active: 'true' }
})
},
updateValue (value) {
this.selectedId = value
this.$emit('input', value)
}
}
}
</script>
<style>
.select-user-search {
overflow: hidden;
padding-bottom: 1px;
}
.select-user-search .v-select__selections {
overflow: hidden;
}
.select-user-search .v-select__selection {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
Unit Test
import { mount, shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import VueLoading from 'vuex-loading'
import Vuetify from 'vuetify'
import UserSearch from 'manager/components/user/search.vue'
const factory = (values, shallow = true) => {
if (!shallow) {
return mount(UserSearch, { ...values })
}
return shallowMount(UserSearch, { ...values })
}
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueLoading)
localVue.use(Vuetify)
describe('UserSearch', () => {
let actions, getters, store, vueLoading
beforeEach(() => {
actions = {
FETCH_USERS: jest.fn()
}
getters = {
users: () => []
}
vueLoading = new VueLoading({ useVuex: true })
store = new Vuex.Store({
actions,
getters
})
})
it('calls "updateValue" method when input triggered', async () => {
const methods = {
updateValue: jest.fn()
}
const wrapper = factory({ methods, store, localVue, vueLoading }, false)
const input = wrapper.find('input')
input.element.value = 'value'
input.trigger('input')
await wrapper.vm.$nextTick()
expect(methods['updateValue']).toBeCalledWith('value')
})
})
I use the following variant
Component
<template>
<span> <!-- span (or the other HTML element) around <v-autocomplete> is important -->
<v-autocomplete
:items="options"
:value="selectedId"
:label="$t('user.filter')"
:menu-props="{ contentClass }"
:loading="false"
item-text="email"
item-value="id"
name="search_by_user"
hide-details
single-line
clearable
#input="updateValue($event)"
#click.native="fetchUsers"
/>
</span>
</template>
<script lang="ts">
import { Component } from 'vue-property-decorator';
import Vue from 'vue';
#Component
export default class AppAutocomplete extends Vue {
private options: any[] = [{email: 'box1#gmail.com', id: 1}];
private selectedId: string|undefined = undefined;
private contentClass = 'user-search';
private fetchUsers() {
}
public updateValue(event: any) {
}
}
</script>
Unit test
import { Wrapper, createLocalVue, mount } from '#vue/test-utils';
import Vue from 'vue';
import Vuetify from 'vuetify';
// #ts-ignore
import AppAutocomplete from '#/components/AppAutocomplete.vue';
describe('AppAutocomplete', () => {
Vue.use(Vuetify);
let wrapper: Wrapper<Vue>;
let vm: any;
beforeEach(() => {
wrapper = mount(AppAutocomplete, {
localVue: createLocalVue(),
vuetify: new Vuetify({
mocks: { $vuetify: { lang: { t: (val: string) => val } } },
}),
mocks: { $t: () => { /** */ } },
});
vm = wrapper.vm;
});
afterEach(() => wrapper.destroy());
it(`calls "updateValue" method when input triggered`, () => {
jest.spyOn(vm, 'updateValue');
const autocompleteElem = wrapper.find('.v-autocomplete');
autocompleteElem.vm.$emit('input', 'box1');
expect(vm.updateValue).toBeCalledTimes(1);
expect(vm.updateValue).toHaveBeenCalledWith('box1');
});
});
To surround v-autocomplete by some other HTML element is important. Without it a test fails.
Vuetify version: 2.1.12

Exporting custom properties from fabricjs objects to SVG

I have an image object where I have added a custom property.
When stringify the object, I can see that the property is there, but when I export the SVG by using toSVG, the custom property is not in the SVG source.
I would like the name property in my example to be exported as a custom property in the SVG image xml data (with the possibility to add multiple properties)
Is this possible?
Please se my fiddle: https://jsfiddle.net/Jonah/ujexg46s/
var svgImage = fabric.util.createClass(fabric.Image, {
initialize: function(element, options) {
this.callSuper("initialize", element, options);
options && this.set("name", options.name);
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
name: this.name
});
}
});
var canvas = new fabric.Canvas('container');
function loadImage() {
var img = new Image();
img.src = imageSrc;
img.onload = function() {
var image = new svgImage(img, {
name: "test",
left: 0,
top: 0
});
canvas.add(image);
}
}
document.getElementById('load-btn').onclick = function() {
loadImage();
};
document.getElementById('export-btn').onclick = function() {
canvas.deactivateAll().renderAll();
console.log(JSON.stringify(canvas));
window.open('data:image/svg+xml;utf8,' + encodeURIComponent(canvas.toSVG()));
};
You can find answer here.
fabric.util.object.extend(fabric.Image.prototype, {
toObject: function(propertiesToInclude) {
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
src: this._originalElement.src || this._originalElement._src,
filters: this.filters.map(function(filterObj) {
return filterObj && filterObj.toObject();
}),
crossOrigin: this.crossOrigin,
alignX: this.alignX,
alignY: this.alignY,
meetOrSlice: this.meetOrSlice,
name: this.name
});
},
});
https://jsfiddle.net/mullainathan/ujexg46s/1/
You can override the existing toSVG function like this.
var circle = new fabric.Circle ({
radius: 40,
left: 50,
top: 50,
fill: 'rgb(0,255,0)',
opacity: 0.5,
id: 'hello'
});
circle.toSVG = (function(toSVG) {
return function(){
var svgString = toSVG.call(this);
var domParser = new DOMParser();
var doc = domParser.parseFromString(svgString, 'image/svg+xml');
var parentG = doc.querySelector('path')
parentG.setAttribute('id', this.id);
return doc.documentElement.outerHTML;
}
})(circle.toSVG)

How to create Ractive's subcomponents dynamically and change them programmatically

I can instantiate (sub)components manually using tags, but I don't know how to do it dynamically, or, how to insert and remove different components in the same area using tags.
Today I instantiate each (sub)component this way:
Ractive.load( '/templates/global/example.html' ).then( function ( Example )
{
ractive.components.example = new Example( { el : 'aside' } );
});
But the new (sub)component can't see the data of it's parent instance in mustache, only his own data.
Here's a dynamic component:
Ractive.components.dynamic = Ractive.extend({
template: '<component/>',
components: {
component: function() {
return this.get('name');
}
},
oninit: function(){
this.observe('name', function(){
this.reset();
}, { init: false});
}
});
Just pass in the name of the component it should implement:
<dynamic name='{{name}}'/>
See it in action below
Ractive.components.a = Ractive.extend({ template: 'I am A {{foo}}' });
Ractive.components.b = Ractive.extend({ template: 'I am B {{foo}}' });
Ractive.components.c = Ractive.extend({ template: 'I am C {{foo}}' });
Ractive.components.dynamic = Ractive.extend({
template: '<component/>',
components: {
component: function() {
return this.get('name');
}
},
oninit: function(){
this.observe('name', function(){
this.reset();
}, { init: false});
}
});
var r = new Ractive({
el: document.body,
template: '#template',
data: {
foo: 'foo',
list: ['a', 'b', 'c'],
name: 'a'
}
});
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<script id='template' type='text/ractive'>
{{#each list}}
<input type='radio' name='{{name}}' value='{{.}}'>{{.}}
{{/each}}
<br>
<dynamic name='{{name}}'/>
</script>
Would like to upvote #martypdx's answer more than once. Inspired by his answer, I came up with something I'd like to share. It adds the following things:
Create multiple instances of a type of Component (Ractive.extend)
Component instances are alive and can receive messages from external listeners even if they have been unrendered.
Here is my fiddle: https://jsfiddle.net/vikikamath/z5otgzpL/9/
// Inspired by http://stackoverflow.com/a/31080919/405117
var mapper = {
'A': function(instanceId){
var handleAData;
return Ractive.extend({
template: 'I am A this is my data: <ul>{{#datas}}<li>{{.}}</li>{{/datas}}</ul>',
data: {
datas:[]
},
onrender: function() {
handleAData = function(txt){
this.push('datas', txt);
}.bind(this);
r.on(instanceId, handleAData);
},
onunrender: function() {
r.off(instanceId, handleAData);
}
});
}
,'B': function(instanceId){
var handleBData;
return Ractive.extend({
template: 'I am B this is my data: <ul>{{#datas}}<li>{{.}}</li>{{/datas}}</ul>',
data: {
datas:[]
},
onrender: function() {
handleBData = function(txt){
this.push('datas', txt);
}.bind(this);
r.on(instanceId, handleBData);
},
onunrender: function() {
r.off(instanceId, handleBData);
}
});
}
,'C': function(instanceId){
var handleCData;
return Ractive.extend({
template: 'I am C this is my data: <ul>{{#datas}}<li>{{.}}</li>{{/datas}}</ul>',
data: {
datas:[]
},
onrender: function() {
handleCData = function(txt){
this.push('datas', txt);
}.bind(this);
r.on(instanceId, handleCData);
},
onunrender: function() {
r.off(instanceId, handleCData);
}
});
}
,'D': function(instanceId){
var handleDData;
return Ractive.extend({
template: 'I am D this is my data: <ul>{{#datas}}<li>{{.}}</li>{{/datas}}</ul>',
data: {
datas:[]
},
onrender: function() {
handleDData = function(txt){
this.push('datas', txt);
}.bind(this);
r.on(instanceId, handleDData);
},
onunrender: function() {
r.off(instanceId, handleDData);
}
});
}
}
/* arbitrarily select a component */
function pickRandomComponent() {
return String.fromCharCode(Math.floor(Math.random() * Object.keys(mapper).length) + 65);
}
var DynamicComponent = Ractive.extend({
template: '<component/>',
components: {
component: function() {
return this.get('name');
}
},
oninit: function(){
this.observe('name', function(){
this.reset();
}, { init: false});
}
});
var r = new Ractive({
el: 'main',
template: '#template',
components: {
dummy: Ractive.extend({ template: 'Welcome message' }),
dynamic: DynamicComponent
},
data: {
foo: 'foo',
list: ['dummy'],
name: 'dummy',
textdata: ''
},
oninit: function() {
this.on("sendDataToCurrentComponent", function() {
r.fire(this.get('name'), this.get('textdata'))
}.bind(this));
this.on('addComponent', function(){
var rndComponent = pickRandomComponent();
var now = Date.now();
var componentInstanceName = rndComponent + '-' + now
this.components[componentInstanceName] = mapper[rndComponent](componentInstanceName)
this.push('list', componentInstanceName);
}.bind(this));
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/ractive/0.7.3/ractive.min.js"></script>
<script id='template' type='text/ractive'>
<button on-click="addComponent">Add</button>
<div>
<input type="text" value="{{textdata}}" placeholder="Send Text to current component" width="900" on-blur="sendDataToCurrentComponent"/>
</div>
{{#each list}}
<input type='radio' name='{{name}}' value='{{.}}'>{{.}}
{{/each}}
<br>
<dynamic name='{{name}}'/>
</script>
<main></main>

Error when trying material-ui SelectField component

I'm trying out material-ui right now. I am new to react.js and material-ui and somehow manage to go through some stuff, but cannot use the SelectField. Whenever I use the SelectField component, I get a
Uncaught TypeError: Cannot read property 'toUpperCase' of undefinederror.
I have followed how it is written in:material-ui.com
This is my selectField.jsx file:
var React = require('react');
var mui = require('material-ui');
var SelectField = mui.SelectField;
var ThemeManager = new mui.Styles.ThemeManager();
var Colors = mui.Styles.Colors;
var StyleResizable = mui.Mixins.StyleResizable;
var menuItems = [
{ payload: '1', text: 'Never' },
{ payload: '2', text: 'Every Night' },
{ payload: '3', text: 'Weeknights' },
{ payload: '4', text: 'Weekends' },
{ payload: '5', text: 'Weekly' },
];
var Main = React.createClass({
getInitialState: function() {
return { selectValue: undefined};
},
childContextTypes: {
muiTheme: React.PropTypes.object
},
getChildContext: function() {
return {
muiTheme: ThemeManager.getCurrentTheme()
};
},
render: function() {
return (
<SelectField
value={this.state.selectValue}
onChange={this._handleSelectValueChange}
floatingLabelText="Select Field"
menuItems={menuItems} />
);
},
_handleSelectValueChange: function(e) {
this.setState({
selectValue: e.target.value
});
}
});
module.exports = Main;
Any ideas why it outputs an error? Other material-ui components works fine.
EDIT:
This is my parent component in app.jsx file:
(function () {
var React = require('react');
var injectTapEventPlugin = require('react-tap-event-plugin');
var Main = require('./components/selectField.jsx');
window.React = React;
injectTapEventPlugin();
React.render(<Main />, document.body);
})();

Resources