Aurelia and Select2 change event - javascript

How can I utilize Aurelia's data binding with Select2? It seems to work just fine with a standard select.
Ideally I would like to use change.delegate in the select element to call a method within my View Model so I would have access to the data storage we are injecting.
The only way I can get an event to fire is to wire up a change event in the attached() handler, but then the data storage falls out of scope.
View:
<template>
<label>Company:</label>
<!-- i would like to use change.delegate="change()" below -->
<select value.bind="selectedCompanies" multiple>
<option repeat.for="company of companies" model.bind="company.CompanyId">${company.CompanyName}</option>
</select>
</template>
View Model:
import {inject, bindable} from 'aurelia-framework';
import {FilterCompanyData} from 'data/custom elements/filters/filterCompanyData';
import {UserStorage} from 'storage/userStorage';
#inject(Element, FilterCompanyData, UserStorage)
export class FilterCompanyCustomElement {
#bindable selectedCompanies;
constructor(element, filterCompanyData, userStorage) {
this.element = element;
this.filterCompanyData = filterCompanyData;
this.userStorage = userStorage;
this.companies = [];
}
bind(bindingContext, overrideContext) {
let userId = this.userStorage.userId;
return this.filterCompanyData
.getCompanies(userId)
.then(response => this.companies = response);
}
attached() {
var el = $(this.element).find('select');
var sel = el.select2({
closeOnSelect: false
});
// Is it possible to get rid of this?
sel.on('change', function (event) {
if (event.originalEvent) { return; }
// TODO add changed data to user storage
var IE = ((navigator.userAgent.indexOf("MSIE") != -1) || (!!document.documentMode == true));
var notice = IE ? new CustomEvent('change', { bubble: false }) : new Event('change', { bubble: false });
$(el)[0].dispatchEvent(notice);
});
}
detached() {
$(this.element).find('select').select2('destroy');
}
change() {
// I'd like to use this method from change.delegate
// TODO add changed data to user storage
}
}
On a side note doesn't Aurelia have a built in polyfill for New Event? IE doesn't seem to like that.

I see that this question is getting old, but i have tried to do the same that you are doing here, and i ended up with the following:
https://github.com/Kla3mus/select24aurelia
I don't use the polyfill, or the original events outside of the component. I use two-way binding for the option and selected value instead. Maybe that could work for you too?
It's written in TypeScript, but it's almost the same as JavaScript, so i think you will be able modify it :)

Related

Can't access to a 'this' value in a function outside of constructor

I'm trying to learn how to make an app like reactjs but not really using it. I'm following a tutorial but I have some challenges. I have a function called 'update' which fires when there is a change in the state. I have a 'menu' object that I import as a child. The thing is, I can't seem to access to this child object in the 'update' function. Please have a look at the following:
import onChange from 'on-change';
import Menu from './menu';
class App {
constructor(){
const state = {
showMenu: false
}
this.state = onChange(state, this.update);
this.el = document.createElement('div');
this.el.className = 'todo';
// create an instance of the Menu
this.menu = new Menu(this.state);
// create a button to show or hide the menu
this.toggle = document.createElement('button');
this.toggle.innerText = 'show or hide the menu';
this.el.appendChild(this.menu.el);
this.el.appendChild(this.toggle);
// change the showMenu property of our state object when clicked
this.toggle.addEventListener('click', () => { this.state.showMenu = !this.state.showMenu; })
}
update(path, current, previous) {
if(path === 'showMenu') {
> // show or hide menu depending on state
> console.log (app.menu); // undefined
> this.menu[current ? 'show' : 'hide'](); // not working cause 'this.menu' is undefined
}
}
}
const app = new App();
> console.log (app.menu); // here it console logs correctly the object
document.body.appendChild(app.el);
Can someone help me to figure out what is going on here? thank you!
In the update() method in your App class, you have to use this.menu instead of app.menu.
It should look like this:
update(path, current, previous) {
if(path === 'showMenu') {
console.log (this.menu);
this.menu[current ? 'show' : 'hide']();
}
}
You can't use app within the App class because app is not defined there. You have to use the this keyword to access the members in the class itself.
Hope this helps.

How do you handle click-outside of element properly in vuejs?

Is there any proper solution for handling click-outside of elements?
there are general solutions out there, like Handling clicks outside an element without jquery :
window.onload = function() {
// For clicks inside the element
document.getElementById('myElement').onclick = function(e) {
// Make sure the event doesn't bubble from your element
if (e) { e.stopPropagation(); }
else { window.event.cancelBubble = true; }
// Place the code for this element here
alert('this was a click inside');
};
// For clicks elsewhere on the page
document.onclick = function() {
alert('this was a click outside');
};
};
But the problem is almost all projects have multiple and different popups in different components which i should handle their click-outsides.
how should i handle click-outisde without using a global window.on?(I think it is not possible to put all components outside-case handler in window.on )
After struggling with this and searching about this, i found how to solve this problem using vuejs directive without bleeding:
1. using libraries:
v-click-outside is a good one,
https://www.npmjs.com/package/v-click-outside
2. without a library:
```
//main.js
import '#/directives';
......
// directives.js
import Vue from "vue";
Vue.directive('click-outside', {
bind: function (element, binding, vnode) {
element.clickOutsideEvent = function (event) { // check that click was outside the el and his children
if (!(element === event.target || element.contains(event.target))) { // and if it did, call method provided in attribute value
vnode.context[binding.expression](event);
// binding.value(); run the arg
}
};
document.body.addEventListener('click', element.clickOutsideEvent)
},
unbind: function (element) {
document.body.removeEventListener('click', element.clickOutsideEvent)
}
});
```
use it every-where you want with v-click-outside directive like below:
//header.vue
<div class="profileQuickAction col-lg-4 col-md-12" v-click-outside="hidePopUps">
...
</>
you can check this on
You can also directly use VueUse vOnClickOUtside directive.
<script setup lang="ts">
import { vOnClickOutside } from '#vueuse/components'
const modal = ref(true)
function closeModal() {
modal.value = false
}
</script>
<template>
<div v-if="modal" v-on-click-outside="closeModal">
Hello World
</div>
</template>
It might be little late but i ve found a clear solution
<button class="m-search-btn" #click="checkSearch" v-bind:class="{'m-nav-active':search === true}">
methods:{
checkSearch(e){
var clicked = e.target;
this.search = !this.search;
var that = this;
window.addEventListener('click',function check(e){
if (clicked === e.target || e.target.closest('.search-input')){ //I ve used target closest to protect the input that has search bar in it
that.search = true;
}else {
that.search = false;
removeEventListener('click',check)
}
})
}}

PrimeNg TabView with ConfirmDialog

I'm trying to use PrimeNg TabView component along with confirmDialog unsuccessfully, here is my code:
<p-tabView (onChange)="onTabChange($event)" [(activeIndex)]="index">...</p-tabView>
onTabChange(event){
this.confirmationService.confirm({
message: 'Do you confirm ?',
accept: () => {
this.index = event.index;
},
reject:() =>{ }
});
}
Do you have an idea on how to prevent or allow tab change using confirm dialog ?
Thanks
Based on similar solution for material design tabs, here is the solution for my issue:
in html Declare a local variable referencing TabView DOM object:
<p-tabView #onglets>...</p-tabView>
in component.ts, change default function called when click on tab with specific
function to match your case:
#ViewChild('onglets') onglets: TabView;
this.onglets.open = this.interceptOngletChange.bind(this);
...
interceptOngletChange(event: Event, tab: TabPanel){
const result = confirm(Do you really want to leave the tab?);
return result && TabView.prototype.open.apply(this.onglets, argumentsList);
});
}
I had similar problem. Needed show dialog before tab change.
My solution:
HTML
<p-tabView #tabView (onChange)="onChange($event)" />
TS
#ViewChild('tabView') tabView: TabView;
onChange(event: any) {
const previoustab = this.tabView.tabs[this.prevIndex]; //saved previous/initial index
previoustab.selected = true;
const selectedTab = this.tabView.tabs[event.index];
selectedTab.selected = false;
this.tabView.activeIndex = this.prevIndex;
this.nextIndex= event.index;
}
GoToNextTab() {
this.tabView.activeIndex = this.nextIndex;
this.prevIndex= this.nextIndex;
this.tabView.open(undefined, this.tabView.tabs[this.nextIndex]);
}
With this code you will stay on the selected tab without tab style changes.

How to hook on library function (Golden Layout) and call additional methods

I'm using a library called Golden Layout, it has a function called destroy which will close all the application window, on window close or refesh
I need to add additional method to the destroy function. I need to removeall the localstorage aswell.
How do i do it ? Please help
Below is the plugin code.
lm.LayoutManager = function( config, container ) {
....
destroy: function() {
if( this.isInitialised === false ) {
return;
}
this._onUnload();
$( window ).off( 'resize', this._resizeFunction );
$( window ).off( 'unload beforeunload', this._unloadFunction );
this.root.callDownwards( '_$destroy', [], true );
this.root.contentItems = [];
this.tabDropPlaceholder.remove();
this.dropTargetIndicator.destroy();
this.transitionIndicator.destroy();
this.eventHub.destroy();
this._dragSources.forEach( function( dragSource ) {
dragSource._dragListener.destroy();
dragSource._element = null;
dragSource._itemConfig = null;
dragSource._dragListener = null;
} );
this._dragSources = [];
},
I can access the destroy method in the component like this
this.layout = new GoldenLayout(this.config, this.layoutElement.nativeElement);
this.layout.destroy();`
My code
#HostListener('window:beforeunload', ['$event'])
beforeunloadHandler(event) {
var originalDestroy = this.layout.destroy;
this.layout.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
localStorage.clear();
};
}
Looking at the documentation, GoldenLayout offers an itemDestroyed event you could hook to do your custom cleanup. The description is:
Fired whenever an item gets destroyed.
If for some reason you can't, the general answer is that you can easily wrap the function:
var originalDestroy = this.layout.destroy;
this.layout.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
};
You may be able to do this for all instances if necessary by modifying GoldenLayout.prototype:
var originalDestroy = GoldenLayout.prototype.destroy;
GoldenLayout.prototype.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
};
Example:
// Stand-in for golden laout
function GoldenLayout() {
}
GoldenLayout.prototype.destroy = function() {
console.log("Standard functionality");
};
// Your override:
var originalDestroy = GoldenLayout.prototype.destroy;
GoldenLayout.prototype.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
console.log("Custom functionality");
};
// Use
var layout = new GoldenLayout();
layout.destroy();
Hooking into golden layout is the intended purpose for the events.
As briefly touched on by #T.J. Crowder, there is the itemDestroyed event which is called when an item in the layout is destroyed.
You can just listen for this event like such:
this.layout.on('itemDestroyed', function() {
localStorage.clear();
})
However, this event is called every time anything is destroyed, and propagates down the tree, even just by closing a tab. This means that if you call destroy on the layout root, you will get an event for every RowOrColumn, Stack and Component
I would recommend to check the item passed into the event and ignore if not the main window (root item)
this.layout.on('itemDestroyed', function(item) {
if (item.type === "root") {
localStorage.clear();
}
})

Detect element style change in chrome

I'm trying to find a way to detect changes to the element style but I haven't had much luck. The code below works on a new property I define like tempBgColor but I cannot override/shadow an existing property like color. I know jquery has a watch function, but it only detects changes from the jquery api but not directly changing the value of a style something like elem.style.color.
var e = document.getElementById('element');
e.style.__defineGetter__("color", function() {
return "A property";
});
e.style.__defineSetter__("color", function(val) {
alert("Setting " + val + "!");
});
Any pointers?
You should be able to do this with a MutationObserver - see demo (Webkit only), which is the new, shiny way of getting notified about changes in the DOM. The older, now deprecated, way was Mutation events.
Demo simply logs in the console the old and new values when the paragraph is clicked. Note that the old value will not be available if it was set via a non-inline CSS rule, but the change will still be detected.
HTML
<p id="observable" style="color: red">Lorem ipsum</p>​
JavaScript
var MutationObserver = window.WebKitMutationObserver;
var target = document.querySelector('#observable');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log('old', mutation.oldValue);
console.log('new', mutation.target.style.cssText);
});
});
var config = { attributes: true, attributeOldValue: true }
observer.observe(target, config);
// click event to change colour of the thing we are observing
target.addEventListener('click', function(ev) {
observable.style.color = 'green';
return false;
}, false);
Credit to this blog post, for some of the code above.
With Chrome's Developer Tools open, you can find the element whose style's change you're interested in, right click it, select "Break on..." and "Attributes modifications".
here is a naive implementation using setTimeout with undescorejs.
The only way to find out which change was made is to iterate through the style object properties.
Here is the live example
$( function () {
var ele = document.getElementById('ele'),
oldStyle = {};
function checkEquality() {
style = _.clone(ele.style);
if (!_.isEqual(style, oldStyle)) {
console.log('Not equal');
oldStyle = _.clone(style);
} else {
console.log('Equal');
}
_.delay(checkEquality, 2000);
}
checkEquality();
$('a#add_prop').on('click', function () {
var props = $('#prop').val().replace(/ /g, '').split(':');
console.log(props);
$(ele).css(props[0], props[1]);
});
$('#prop').on('keydown', function (e) {
if (e.keyCode == 13) {
$('a#add_prop').trigger('click');
}
});
});

Categories

Resources