Ion RangeSlider not reloading correctly in Angular 9 project - javascript

I am trying to integrate Ion.RangeSlider into an Angular 9 project to select a range of dates. I'm having an issue where the slider works when it initially loads on a page; however it doesn't load right when I navigate back to the same page.
(Edited to add routes)
For example, here is a view when I load a page in my project (/#/category?id=13&data_list_id=15), and the slider is loading as expected:
The next tab over navigates to (/#/category?id=13&data_list_id=16) also generates the slider as expected:
Returning to the initial summary view (/#/category?id=13&data_list_id=15) results in the slider appearing as an input box and not working properly.
Below are sections of code from my component that I think are the relevant parts to generating the slider.
date-slider.component.html
<label [for]="'dateFrom'" class="visuallyhidden">Date From:</label>
<input type="text" [name]="'dateFrom'" [id]="'dateFrom'" [value]="sliderDates[start]" class="date-input js-from" />
<div class="slider-container">
<input class="sliders" type="text" [id]="'slider'" value="" aria-label="date-range-slider" />
</div>
<label [for]="'dateTo'" class="visuallyhidden">Date To:</label>
<input type="text" [name]="'dateTo'" [id]="'dateTo'" [value]="sliderDates[end]" class="date-input js-to" />
date-slider.component.ts
ngOnInit() {
if (this.dates && this.dates.length) {
const defaultRanges = this.findDefaultRange(this.dates, this.freq, this.defaultRange, this.dateFrom, this.dateTo);
// Start and end used for 'from' and 'to' inputs in slider
// If start/end exist in values array, position handles at start/end; otherwise, use default range
this.start = defaultRanges.start;
this.end = defaultRanges.end;
this.sliderDates = defaultRanges.sliderDates; // Array of dates for the slider i.e. ['2000', '2001', '2002', '2003', '2004', '2005']
}
}
ngAfterViewInit() {
const $fromInput = $(`#dateFrom`);
const $toInput = $(`#dateTo`);
const $range = $(`#slider`).data('ionRangeSlider');
this.initRangeSlider(this.sliderDates, this.start, this.end, this.freq);
// Set change functions for 'from' and 'to' date inputs
this.setInputChangeFunction($fromInput, this.sliderDates, $range, 'from', this.portalSettings, this.freq);
this.setInputChangeFunction($toInput, this.sliderDates, $range, 'to', this.portalSettings, this.freq);
this.updateChartsAndTables($fromInput.prop('value'), $toInput.prop('value'), this.freq)
this.cd.detectChanges(); // cd refers to ChangeDetectorRef
}
initRangeSlider(sliderDates: Array<any>, start: number, end: number, freq: string) {
const updateChartsAndTables = (from, to, freq) => this.updateChartsAndTables(from, to, freq);
const $fromInput = $(`#dateFrom`);
const $toInput = $(`#dateTo`);
$(`#slider`).ionRangeSlider({
min: 0,
from: start,
to: end,
values: sliderDates,
prettify_enabled: false,
hide_min_max: true,
hide_from_to: true,
keyboard: true,
keyboard_step: 1,
skin: 'round',
type: 'double',
onChange: function (data) {
$fromInput.prop('value', data.from_value);
$toInput.prop('value', data.to_value);
},
onFinish: function (data) {
updateChartsAndTables(data.from_value, data.to_value, freq);
}
});
}
I have tried using the ng2-ion-range-slider wrapper, but I end up with the following error when I load it into my module:
This likely means that the library (ng2-ion-range-slider) which declares IonRangeSliderModule has not been processed correctly by ngcc, or is not compatible with Angular Ivy. Check if a newer version of the library is available, and update if so. Also consider checking with the library's authors to see if the library is expected to be compatible with Ivy.
(Weirdly the slider works fine on an existing project that is basically the same application. The existing application was developed back when Angular was at version 2. It became a multiple app project where all the components, services, and individual app modules were all in the same src folder. But now that Angular-cli supports generating multiple apps in the same project, I'm trying to split things up and keep reusable/common components in their own custom library.)

Related

What element should userEvent.type(...) target to be used with MUI's DesktopDatePicker TextField?

How can I type into the TextField input in the MUI datepicker with react-testing-library's user-event? I can see there is a mask being applied
I've tried userEvent.type(inputEl, '1') and userEvent.keyboard('1'). In both cases, the input remains empty.
My current assumption is that a rerender replaces my update with the empty state of a higher order component (which makes sense, based on the application the date format mask to the input).
The documentation example works as expected, so my issue is specific to the MUI datepicker.
A watered down version of the component (and test) can be found in this sandbox.
Below is a copy of the rendered HTML to prove I'm targeting the input element with a data-testid, which does not work:
screen.getByTestId('date-selector-text-field').outerHTML:
<input
aria-invalid="false"
placeholder="dd / mm / yyyy"
readonly=""
type="text"
aria-readonly="true"
aria-label="Choose date"
data-testid="date-selector-text-field"
class="MuiOutlinedInput-input MuiInputBase-input css-1t8l2tu-MuiInputBase-input-MuiOutlinedInput-input"
value=""
aria-describedby="mui-1-helper-text"
id="mui-1"
>
I added the following, to make sure the DatePicker is rendered for a Desktop in the test. More info on that you can find here: https://github.com/mui/material-ui-pickers/issues/2073#issuecomment-671834179
beforeAll(() => {
// add window.matchMedia
// this is necessary for the date picker to be rendered in desktop mode.
// if this is not provided, the mobile mode is rendered, which might lead to unexpected behavior
Object.defineProperty(window, "matchMedia", {
writable: true,
value: (query: any) => ({
media: query,
// this is the media query that #material-ui/pickers uses to determine if a device is a desktop device
matches: query === "(pointer: fine)",
onchange: () => null,
addEventListener: () => null,
removeEventListener: () => null,
addListener: () => null,
removeListener: () => null,
dispatchEvent: () => false,
}),
});
});
afterAll(() => {
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
delete window.matchMedia;
});
In case you have a value already in the Datepicker, when calling userEvent.type(), add an await userEvent.clear() before the await userEvent.type(). The clear is needed, because we are simulating actual user behaviour, and you are not able to type in something, if you already have a value (which I do have in my test case).

CKEditor 5 : Unable to add multiple attributes to 'img' tag

I have implemented a custom math plugin and integrated it into ck5. this plugin will convert math latex to image equation and I'm able to render the converted math equation image into a ck5 editor using the below code.
editor.model.change(writer => {
const imageElement = writer.createElement('image', {
src: data.detail.imgURL
});
editor.model.insertContent(imageElement, selection);
});
Still here everything is working fine. when i'm trying to store original latex equation value in image tag as custom attribute (attribute name is data-mathml ). It strips out custom attributes.
So I have gone through the documentation and tried but was not able to add the custom attribute.
Below is my code :
class InsertImage extends Plugin {
init() {
const editor = this.editor;
const view = editor.editing.view;
const viewDocument = view.document;
// Somewhere in your plugin's init() callback:
view.addObserver( ClickObserver );
editor.ui.componentFactory.add('insertImage', locale => {
const view = new ButtonView(locale);
view.set({
label: 'Add Equations',
withText: true,
tooltip: true
});
// Callback executed once the image is clicked.
this.listenTo(view, 'execute', () => {
openModel();
});
return view;
});
window.addEventListener('setDatatoCK', function(data){
const selection = editor.model.document.selection;
editor.model.change( writer => {
const imageElement = writer.createElement( 'image', {
src: data.detail.imgURL,
'data-mthml': data.detail.latexFrmla,
} );
editor.model.insertContent( imageElement, selection );
} );
})
this.listenTo( editor.editing.view.document, 'click', ( evt, data ) => {
if ( data.domEvent.detail == 2 ) {
editorToPopupdoubleClickHandler( data.domTarget, data.domEvent );
evt.stop();
}
}, { priority: 'highest' } );
}
};
I want to add multiple attributes to the image tag. What should I do to add multiple attributes?
(Note: I'm able to create a new custom tag (tag named "math") with multiple attributes but not for image tag)
Please help me with this. this blocker for me.
Methods tried:
In order to achieve this, I have created one custom tag with multiple attributes and append image tags under this custom tag. It's working fine as expected but I want to add multiple attributes to image tag not with the custom tag.
โœ”๏ธ Expected result
โŒ Actual result
๐Ÿ“ƒ Other details
Browser: Google Chrome Version 78.0.3904.108 (Official Build) (64-bit)
OS: macOS Mojave 10.14.6
CKEditor version: CKEditor 5
Installed CKEditor plugins: ckeditor5-editor-classic,ckeditor5-image,ckeditor5-essentials,ckeditor5-basic-styles,ckeditor5-core,ckeditor5-ui
Hope you've already found a solution to this answer. After spending several hours on searching a solution to a similar problem, I've made it working. See below:
// you have to import the insertImage fn to be able to override default imageinsert fn
import { insertImage } from '#ckeditor/ckeditor5-image/src/image/utils.js'
// this method HAVE to be automatically called when ckeditor is onready.
// You can add custom eventlistener or on the html tag just define listener:
// in Vue2 we used to do like this: <ckeditor #ready="someFnOnCkEditorReadyState()" />
someFnOnCkEditorReadyState() {
// as for img tag the options supported by ckeditor is very limited, we must define our properties to extend the used schema
editor.model.schema.extend('image', { allowAttributes: ['data-mathml'] })
// add conversion for downcast
editor.conversion.for('downcast').add(modelToViewAttributeConverter('data-mathml'))
// add conversion for upcast
editor.conversion.for('upcast').attributeToAttribute({
view: {
name: 'image',
key: 'data-mathml',
},
model: 'data-mathml',
})
}
// then you have to implement your custom image insert method
// from now on this will be your default insertImage fn
// this modification might require other modifications for example to add a custom image browser button to the toolbar
otherFnToInsertImg() {
insertImage(editor.model, {
src: image.url,
'data-mathml': 'here comes the magic',
})
}
Hope it helps someone to save some hours. ;)

Primeng paginator cannot reset page 1 after call API

I have 2 functions to load data: when init page and search page.
When init page, the data display with 5 pages. I click page 3, the data show with paging is Ok. After that, enter data search. The data table is reload, but the page number does not reset to 1, it's still page 3.
in html:
<p-paginator rows="5" totalRecords="{{totalRecords}}" (onLazyLoad)="paginate($event)"
(onPageChange)="paginate($event)" [rowsPerPageOptions]="[2,5,10,20]"></p-paginator>
ts:
defaultPage:0;
defaultSize:2
paginate(event) {
let currentPage: number;
this.defaultPage = event.first / event.rows;
this.defaultSize = event.rows;
this.listData = [];
if (this.isSearch == 1) {
this.getLoadPageSearch(this.defaultSize, this.defaultPage);
} else {
this.getLoadPage(this.defaultSize, this.defaultPage);
}
}
Please adivice me how to reset the paginator after call another API
The p-paginator component contains the changePageToFirst function. To access this function, we will need to obtain a ViewChild reference to the component. For example, in our template we define the component:
<p-paginator rows="10" totalRecords="100" #p></p-paginator>
<button (click)="reset($event)">Reset</button>
And in our component, we can handle the reset event as follows:
#ViewChild('p', {static: false}) paginator: Paginator;
reset($event) {
this.paginator.changePageToFirst($event);
}
Here is a demo: https://plnkr.co/edit/FxwTHK0W2WuTCjuqhLp6?p=preview
EDIT: The above demo is no longer working on plunkr. Here is an updated version:
https://stackblitz.com/edit/angular-u9nqf5
Another useful method is changePage.
In template:
<p-paginator #pp></p-paginator>
In the component:
#ViewChild('pp') paginator: Paginator;
this.paginator.changePage(0);
For those using the dataTable primeng component and using its lazy pagination feature,
we can reset the page to '1' by using
this.tableTemplateName.first = 0;
where setting first=0 means first page

Cannot use jquery plugin inside VueJS component

I have worked with VueJS for a while, and it is great. I have been able to integrate it with jQueryUI (for an old looking website) and I created a datepicker component, and a datetime picker component as well, both working correctly.
Now I am trying to create a simple phone number component, which simply provides an input with a mask that helps with the phone number format. The plugin for jquery that provides the masking, works correctly on it's own, but if I try to mask an input inside my component, it does not work.
Here is the example code in jsfiddle:
Simple Masked Phone input component for vuejs 2.4.0 - jsfiddle
Javascript:
Vue.component('phone', {
template: '#phone',
props: {
value : {
type : String,
default: ''
},
mask: {
type : String,
default: '(999) 999-9999'
}
},
data: function() {
return {
internalValue: ''
};
},
created: function() {
this.internalValue = $.trim(this.value);
},
mounted: function() {
$(this.$el).find('.phone:eq(0)').mask('(999) 999-9999');
},
methods: {
updateValue: function (value) {
this.$emit('input', value);
}
}
});
var vueapp = new Vue({
el: '#content',
data: {
myphone: ''
}
});
$('.phonex').mask('(999) 999-9999');
HTML:
<div id="content">
<script type="text/x-template" id="phone">
<input type="text" class="phone" v-model="internalValue" v-on:input="updateValue($event.target.value)" />
</script>
<label>Vue Phone</label>
<phone v-model="myphone"></phone>
<br />
{{ myphone }}
<br />
<label>Simple Phone</label>
<input type="text" class="phonex" />
</div>
This is what I see:
Dependencies:
jquery-2.2.4.min.js
jquery.maskedinput.min.js (1.4.1)
vue.js (2.4.0)
Is there anything I am doing wrong here? Thanks.
You don't need the .find('.phone:eq(0)') in your jquery, removing it seems to fix the masking (as shown here), though this does seem to mess with Vue's data binding.
After doing a bit more digging it looks like this is a known issue.
And is addressed here:
Vue is a jealous library in the sense that you must let it completely
own the patch of DOM that you give it (defined by what you pass to
el). If jQuery makes a change to an element that Vue is managing, say,
adds a class to something, Vue wonโ€™t be aware of the change and is
going to go right ahead and overwrite it in the next update cycle.
The way to fix this is to add the event handler when you call .mask on the element.
So for example:
mounted: function() {
var self = this;
$(this.$el).mask('(999) 999-9999').on('keydown change',function(){
self.$emit('input', $(this).val());
})
},
Here is the fiddle with the fix: https://jsfiddle.net/vo9orLx2/

CKEditor 4.7 - Justify Group Menu Button

i'm trying to create a dropdown menu in CKEditor to group some normal button tool just because i need to compress the toolsbar. For this i'm trying to create, for example, a justify group button menu, for this i have compile this plugin :
CKEDITOR.plugins.add('justifygroup', {
requires: ['justify'],
init: function (editor) {
var items = {
justifyleft: {
label: editor.lang.justify.left,
group: 'justify_group',
command: 'justifyleft',
// icon: CKEDITOR.getUrl(this.path + 'icons/icon.png'),
order: 1
},
justifycenter: {
label: editor.lang.justify.center,
group: 'justify_group',
command: 'justifycenter',
order: 2
},
justifyright: {
label: editor.lang.justify.right,
group: 'justify_group',
command: 'justifyright',
order: 3
},
justifyblock: {
label: editor.lang.justify.block,
group: 'justify_group',
command: 'justifyblock',
order: 4
}
};
editor.addMenuGroup('justify_group');
editor.addMenuItems(items);
editor.ui.add('JustifyGroup', CKEDITOR.UI_MENUBUTTON, {
label: 'Justify Group',
icon: 'JustifyLeft',
// Disable in source mode.
modes: {
wysiwyg: 1
},
onMenu: function () {
var activeItems = {};
// Make all items active.
for (var prop in items)
activeItems[prop] = CKEDITOR.TRISTATE_OFF;
return activeItems;
}
});
}
});
here it is a demo of this plugin : https://codepen.io/seltix/pen/dWxWbO
CKEDITOR.replace('textarea', {
extraPlugins: 'justifygroup',
toolbar:[{name: 'test', items: ['JustifyGroup']}]
});
the problems :
1 - if i dont call one of the align buttons on the toolbar ('JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock') the
button menu does not show any button
2 - i'm unable to control some
sort of visual mark to show what alignment is in use (like on the
toolbar buttons)
UPDATE : I found the solution for this problem on the "onMenu" function replacing activeItems[prop] = CKEDITOR.TRISTATE_OFF; with activeItems[prop] = editor.getCommand(items[prop].command).state;
3 - i dont know why but the first option is always
with "focus", how can I set the focus to match a specific item?
thanks all!
1 - The problem that is causing buttons not to display is ACF. Buttons that you're grouping in your dropdown requires certain tags/attrs to be available. In the simplest case it requires text-align to be applicable on p.
It looks that there's a bug in CKEditor that buttons added using editor.addMenuItems are not registering properly new ACF rules, while they do if added directly to the toolbar.
3 - I couldn't find a proper function for that, IMHO it should be doable in onMenu function, however it does not provide enough references to do that. Sounds like a feature request.
Generally you should look at language plugin, as it has many things you are looking for so it's a nice source of inspiration.
For future reference, please create separate StackOverflow question for each case. Though these issues were releated, they are a different cases.

Categories