When I prepare DataTable in stimulus controller, it fires twice what is behaviour of turbo (for caching purposes) and therefore I get DataTables error of initializing it twice
When I tried fix as I use in rails6 without stimulus, it does not work
document.addEventListener('turbo:before-cache', function() {
if ($('#invoices_wrapper').length ==1) {invoicetable.destroy() ;} });
If I put console.log inside that function, I see it is not even executed.
How can I properly setup DataTables with stimulus?
This is my code
import { Controller } from "#hotwired/stimulus"
import $ from 'jquery';
import 'jszip';
import datatable from 'datatables.net-bs5';
import 'datatables.net-buttons-bs5';
window.datatable = datatable();
export default class extends Controller {
connect() {
var invoicetable = $('#invoices').DataTable({
'order': [1, 'asc'],
'serverSide': true,
"processing": true,
'ajax' : '/invoices.json',
columns: [
//{data: 'id', searchable: false, orderable: false},
{data: 'name' },
{data: 'value' }
],
initComplete: function ()
{
console.log("done")
}
});
}
}
Related
In my React-Admin app, I'd like to leverage react-hook-form's useFormContext for various things, such as, for example, setting the default pre-selected choice in this custom input field:
...
import {
Create, SimpleForm, SelectInput
} from 'react-admin';
import { useFormContext } from 'react-hook-form';
const MyInput = () => {
const formContext = useFormContext();
formContext.setValue('category', 'tech');
return (
<SelectInput source="category" choices={[
{ id: 'tech', name: 'Tech' },
{ id: 'people', name: 'People' },
]}
/>
);
};
...
const ItemCreate = () => {
return (
<Create>
<SimpleForm>
<MyInput />
</SimpleForm>
</Create>
);
};
...
This sets the pre-selected value of the field, just as intended. But it throws a warning: Cannot update a component ("Form") while rendering a different component ("MyInput")...
Is there some way to achieve this without getting the warning?
Note: The only reason I'm using a custom input field here is because when I put useFormContext() directly into the component that contains SimpleForm it returns null (similarly described here).
The warning is related to the fact that the entire body of the MyInput() function is executed during each render, you need to call the setValue() function inside the useEffect hook.
Got this working by moving formContext.setValue into a useEffect hook:
...
import {
Create, SimpleForm, SelectInput
} from 'react-admin';
import { useFormContext } from 'react-hook-form';
const MyInput = () => {
const formContext = useFormContext();
// moved the setValue into a useEffect
useEffect(() => {
formContext.setValue('category', 'tech');
});
return (
<SelectInput source="category" choices={[
{ id: 'tech', name: 'Tech' },
{ id: 'people', name: 'People' },
]}
/>
);
};
...
const ItemCreate = () => {
return (
<Create>
<SimpleForm>
<MyInput />
</SimpleForm>
</Create>
);
};
...
When wrapping an antd table column render function with observer, I get an error of render is not a function. I'm trying to avoid the need to every time create a separate observer wrapped functional component that I call from a column render function. Anybody had any luck or knows how to directly wrap a column render function with an observer (or an observer like implementation)?
Here is the typescript code:
import { observer } from "mobx-react-lite";
import { Button } from "antd";
import { ColumnsType } from "antd/lib/table";
import { TableDataSourceRecord } from "../models/TableDataSourceRecord";
const columns: ColumnsType<TableDataSourceRecord> = [
{
title: "View",
dataIndex: "url",
key: "url",
render: observer((url: any, _record: TableDataSourceRecord) => {
return (
<Button type="link" href={url.value}>
View
</Button>
);
}),
},
];
Thanks
Since render expects a function you can't pass React.Component to it, I guess (observer creates component).
Have you tried <Observer> wrapper component?
import { Observer } from "mobx-react-lite";
import { Button } from "antd";
import { ColumnsType } from "antd/lib/table";
import { TableDataSourceRecord } from "../models/TableDataSourceRecord";
const columns: ColumnsType<TableDataSourceRecord> = [
{
title: "View",
dataIndex: "url",
key: "url",
render: (url: string, _record: TableDataSourceRecord) => {
return (
<Observer>
{() => (
<Button type="link" href={url}>
View
</Button>
)}
</Observer>
);
},
},
];
Anyway, there is no point in having Observer in your example since you have no observable values there, url is just string primitive.
This may be self-evident but I'm not finding any example that informs what I'm trying to do (maybe I'm just doing it wrong). I'm adding Vue to an existing ASP.NET Core MVC application and adding the JavaScript statements directly to the page works but when I try to migrate to a TypeScript file nothing happens.
The JavaScript is:
new Vue({
el: "#productPage",
data: {
isLoading: true
},
methods: {
},
mounted () {
console.log("mounted()");
this.isLoading = false;
}
});
This works as expected. Migrating the code to a TypeScript file productPage.ts:
import Vue from 'vue';
new Vue({
el: "#productPage",
data: {
isLoading: true
},
methods: {
},
mounted () {
console.log("mounted()");
this.isLoading = false;
}
});
Which generates:
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "vue"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var vue_1 = require("vue");
var HonestyBox;
(function (HonestyBox) {
new vue_1.default({
el: "#productPage",
data: {
isLoading: true
},
methods: {},
mounted: function () {
console.log("Mounted !!!!");
this.isLoading = false;
}
});
})(HonestyBox || (HonestyBox = {}));
});
//# sourceMappingURL=productPage.js.map
And including the generated javascript file productPage.js:
<script src="~/js/productPage.js"></script>
This does nothing. Stepping through the debugger neither of the conditions in function(factory) are satisfied. The console tells me You are running Vue in development mode. but the included JavaScript fails to run. The tsconfig.json used to generate the JavaScript file:
{
"compileOnSave": true,
"compilerOptions": {
"module": "umd",
"moduleResolution": "node",
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": true,
"sourceMap": true,
"target": "es5",
"outDir": "wwwroot/js"
},
"include": [
"Typescript/**/*"
],
"exclude": [
"node_modules",
"wwwroot"
]
}
Using "module": "commonjs" results in ReferenceError: exports is not defined. I was hoping to avoid having to use Browserify or Webpack.
If I understand you correctly you add Vue in a separate script tag before your productPage.js.
This means that you can't import Vue in your TypeScript file, but you need to declare Vue so the module just assumes that Vue has been loaded already (outside of your TS module).
declare const Vue; // this replaces your Vue import statement
If you want to add a bundler later on, you need to remove your script tag which loads Vue and only go the modular approach:
Vue needs to be imported with an import statement so the bundler knows that he has to include all of Vue.
You will then have one single JS file (or if your bundler splits it: multiple JS files).
Running webpacker 3.5.5 (both the gem and package). This is mostly working, but in IE11 the app is broken because arrow functions do not appear to be transformed. However, inspecting the minified code it seems like the only place arrow functions aren't transformed are inside my vue components.
I think this is because my babel class properties plugin is not applying to my Vue loader somehow, but I haven't been able to come up with a solution.
Here's my .babelrc
{
"presets": [
[
"env",
{
"modules": false,
"targets": {
"browsers": [
"> 1%",
"IE 11"
],
"uglify": true
},
"useBuiltIns": true
}
]
],
"plugins": [
"syntax-dynamic-import",
"transform-object-rest-spread",
[
"transform-class-properties",
{
"spec": true
}
]
],
"env": {
"test": {
"presets": ["es2015"]
}
}
}
And here's the entirety of my environment.js file that modifies the webpack environment that webpacker ships with (vue loader is at the bottom).
const { environment } = require('#rails/webpacker');
environment.loaders.append('expose', {
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: 'jQuery'
}]
});
const webpack = require('webpack');
// append some global plugins
environment.plugins.append('Provide', new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
axios: 'axios',
moment: 'moment-timezone',
_: 'lodash'
}));
// Necesary configuration for vue-loader v15
const VueLoaderPlugin = require('vue-loader/lib/plugin');
environment.plugins.append(
'VueLoaderPlugin',
new VueLoaderPlugin()
);
environment.loaders.append('vue', {
test: /\.vue$/,
loader: 'vue-loader'
});
module.exports = environment;
Edit for more info: Here is the entry point to my pack called 'wrestling'
import 'babel-polyfill';
import 'wrestling';
Then in wrestling.js...
import './styles/wrestling'
import Rails from 'rails-ujs'
Rails.start();
import wrestlingSetup from './wrestlingSetup'
wrestlingSetup();
WrestlingSetup contains the actual references to the vue files. I've cut down the file to show what a single vue import looks like within the file. All the rest are essentially the same.
import Vue from 'vue/dist/vue.esm'
// Redacted a bunch of imports, but they all look like this oen
import WrestlerCreate from './vue/wrestler_create.vue'
export default function() {
document.addEventListener('DOMContentLoaded', () => {
axiosSetup();
const app = new Vue({
el: '#app',
components: {
// Other vue components here that I've removed for simplicity
WrestlerCreate,
}
})
});
}
Here's an actual example of the Vue component
<template>
<div role="form">
<!-- other form elements -->
</div>
</template>
<script>
export default {
name: 'wrestler-create',
props: [
],
// This does not get transformed by babel
data() {
return {
loading: false,
error: false,
errorMessage: "Error, please try again later or contact support.",
first_name: '',
last_name: '',
weight_class: '',
academic_class: ''
}
},
methods: {
// removed for simplicity
}
}
</script>
For clarify sake:
Please use function() for data. I find function() gives me less trouble than arrow functions.
export default {
data: function() {
return {
message: "Hello something!",
secondMessage: "Hello world!"
};
}
}
If you really wish to use arrow function, you can write:
export default {
data: () => {
return {
message: "Hello something!",
secondMessage: "Hello world!"
};
}
}
I am trying to use ng2-highcharts in my application. But after importing the necessary classes using the statement
import {Ng2Highcharts, Ng2Highmaps, Ng2Highstocks} from 'ng2-highcharts/ng2-highcharts';**
if I try to specify these classes in the array of directives in my component (directives: [Ng2Highcharts]), I am getting the following error in my console.
[TypeError: require is not a function][1]
My System.config looks as follows
System.config
Can someone tell me what I am missing here ?
You need to define 'ng2-highchart' within your SystemJS configuration:
<script>
System.config({
map: {
'ng2-highchart': 'node_modules/ng2-highchart'
},
(...)
});
</script>
See this question for more details:
How to use highcharts with angular 2?
Here is my candlestick sample... see if this helps.
import { Component } from '#angular/core';
import {JSONP_PROVIDERS, Jsonp} from '#angular/http';
import { CHART_DIRECTIVES } from 'angular2-highcharts';
#Component({
selector: 'high-chart',
directives: [CHART_DIRECTIVES],
providers: [JSONP_PROVIDERS],
template: `
<h2> This is HighChart CandleStick component </h2>
<chart type="StockChart" [options]="options3"></chart>
`
})
export class HighChartsComponent {
options3: Object;
constructor(jsonp : Jsonp) {
jsonp.request('https://www.highcharts.com/samples/data/jsonp.php?a=e&filename=aapl-ohlc.json&callback=JSONP_CALLBACK').subscribe(res => {
this.options3 = {
title : { text : 'CandleSticks' },
rangeSelector : {
selected : 1
},
series : [{
type : 'candlestick',
name : 'CandleSticks',
data : res.json(),
dataGrouping : {
units : [
[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]
]
},
tooltip: {
valueDecimals: 2
}
}]
};
});
}
Remove format: 'register' from you packages. This will help it detect the right format. Or you can add the format as follows:
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
},
"node_modules/ng2-highcharts": {
format: 'cjs',
defaultExtension: 'js'
}
},
map: {
"ng2-highcharts/ng2-highcharts" : "node_modules/ng2-highcharts/ng2-highcharts"
},
paths: {
"ng2-highcharts/ng2-highcharts" : "node_modules/ng2-highcharts/ng2-highcharts"
}
});