Can i add conditional events on svelte components? - events

is it possible for an event to be fired on a condition defined in the parent component ?
I have an input component where i want to capture the input.. sometimes, but most of the time i dont want the event to fire
//App.html
<Input on:inputData="doStuff(event)" fireEvent=true />
//Input.html
<input bind:value=value (fireEvent ? on:keyup='fire("inputData", { value })' : false )/>
What happens currently is that fireEvent is ignored and on:keyup allways fires
UPDATE
I changed the keyup into a function call where i checked the parameter before firing the event, it works but its kinda wacky
//App.html
<Input on:inputData='doStuff(event.value)' fireEvent={true} />
...
methods: {
doStuff(text) {
console.log('here is the stuff', text);
}
//Input.html
<input bind:value=value on:keyup='setData({ value })'/>
...
methods: {
setData(text) {
if(this.get().fireEvent) {
this.fire("inputData", text)
}
}
}
Anyone got a prettier solution to this problem ?

Very late to the party, but I had a similar problem and ended up solving it with an action. For mine, I needed to attach a click listener to a button only if a certain condition was truthy. A more generic example could be:
<button use:conditionalEvent({condition: foo, event: 'click', callback: bar})>...</button>
...
function conditionalEvent(node, {condition, event, callback}) {
if (condition) {
node.addEventListener(event, callback);
}
return {
destroy() {
node.removeEventListener(event, callback)
}
}
}
The markup could be less verbose if it was only for a single callback or event type, but you get the point.
If the condition can change with user input, you can also return an update function:
function changingCondition(node, {condition, event, callback}) {
function update(condition) {
if (condition) {
node.addEventListener(event, callback);
} else {
node.removeEventListener(event, callback);
}
}
update(condition);
return {
update,
destroy() {
node.removeEventListener(event, callback)
}
}
}
And the update will run whenever condition changes.

I dont know why i didnt think of this but this is excatly what onupdate is for
//App.html
<Input on:inputData='doStuff(event.value)' fireEvent={true} />
...
methods: {
doStuff(text) {
console.log('here is the stuff', text);
}
//Input.html
<input bind:value=value />
...
onupdate({ changed, current, previous }) {
if(this.get().fireEvent) {
this.fire("inputData", current)
}

I tried the following and it worked. note the lower case 'fireevent'. The capital letter threw it off. Naturally you'd have to change it to on:inputData etc.
methods: {
test(evt) {
console.log('evt.fireEvent', evt.target.getAttributeNode("fireEvent").value);
if (evt.target.getAttributeNode("fireevent").value && evt.target.getAttributeNode("fireevent").value=="true")
console.log('do the happy dance');
else
console.log('do NOTHING');
}
}
<button on:click="test(event)" fireevent="false">Test</button>

Related

Conditional class binding using computed prop Vue 2

I wanted to give some form visual validation cues, so I tried using class binding to do just that. If I use the ternary inline, it doesn't really meet my requirements of what should happen, but when I tried using computed prop, it made all the other components disappear.
If i tried doing it like this: v-bind:class="[(!validation.hasError('form.fullName'))?'has-success':'has-danger noValid']"
It just triggers the animation and the classes once and they stays there. I want to trigger the noValid animation everytime the user clicks my submit button if there's errors in validation.
I'm using simple-vue-validator btw.
Here's the godforsaken component, I use vue now-ui-kit template from Creative Tim as a base and customize my way from there. This is their Form Group Input component, docs here
<fg-input
class="input-lg"
:label="validation.firstError('form.fullName')"
placeholder="Full Name..."
v-model="form.fullName"
addon-left-icon="now-ui-icons users_circle-08"
v-bind:class="{ visualValidation }"
></fg-input>
Button Component: bootstrap-vue, cause their customized button doesn't really serve my purpose
<b-button type="submit" block pill variant="info" #click="submit">Submit Form</b-button>
My computation: noValid is the shaking animation class, has-success and has-danger just changes their appearances.
computed: {
visualValidation: function() {
const successClass = "has-success";
const errorBoi = "has-danger";
const shakeBoi = "noValid";
if (validation.firstError("form.fullName")) {
return errorBoi + " " + shakeBoi;
} else if (!validation.hasError("form.fullName")) {
return successClass;
}
}
}
I thought the variables that i returned would be binded as classes to the form.fullName Model but it's not doing anything, better yet, it made all my other components not rendering.
What should i do here? i'm just starting to learn Vue.js so i don't really understand what's going on here.
Edit from here:
I rewrote the logic to my class binding, and just use method to remove the class on click the components.
here is the updated component:
<fg-input
class="input-lg"
:label="validation.firstError('form.email')"
placeholder="Email Here..."
v-model="form.email"
addon-left-icon="now-ui-icons ui-1_email-85"
v-bind:class=" {'has-success' : isSuccEmail, 'has-danger' : isFailEmail, 'noValid': validNoEmail} "
#click="removeShake()"
></fg-input>
data:
isSuccEmail: false,
isFailEmail: false,
validNoEmail: false
method:
removeShake: function() {
let vm = this;
vm.validNoName = false;
vm.validNoEmail = false;
console.log(vm.validNoEmail);
console.log(vm.validNoName);
},
However, there's currently a bug, where the validator don't validate separately. It gave the class has-success to email even though it was full-name that was successful.
valEmail: function() {
let vm = this;
vm.$validate("form.email").then(function(success) {
if (success) {
vm.isFailEmail = false;
vm.isSuccEmail = true;
} else if (!success) {
vm.isSuccEmail = false;
vm.isFailEmail = true;
vm.validNoEmail = true;
} else {
alert("Fatal Error");
}
});
},
valName: function() {
let vm = this;
vm.$validate("form.fullName").then(function(success) {
if (success) {
vm.isFailName = false;
vm.isSuccName = true;
} else if (!success) {
vm.isSuccName = false;
vm.isFailName = true;
vm.validNoName = true;
console.log(vm);
} else {
alert("Fatal Error");
}
});
}
The $validate is a function of simple-vue-validator, the docs.
Submit function is just calling those two functions above.
I think this because of the promise call, is there a way to call the $validate() without promise?
There are a few problems here.
Firstly, while templates don't require you to use this. you still need to use it within your JS code. You should be seeing an error in the console, or maybe even at compile time depending on how you have linting configured.
if (validation.firstError("form.fullName")) {
What is validation? I assume that should be this.validation. Likewise a couple of lines further down.
Your next problem is here:
v-bind:class="{ visualValidation }"
The braces here are creating an object literal, so it's equivalent to this:
v-bind:class="{ visualValidation: visualValidation }"
This will be conditionally adding the string 'visualValidation' as a class , which isn't what you want. Get rid of the braces:
v-bind:class="visualValidation"

how to make a dismissible event in fullcalendar?

Is it possible in fullcalendar to have the same behavior than in bootstrap dismissible alerts for removing an event?
I would like to have the same behavior than in Bootstrap alert in the following example:
https://www.w3schools.com/bootstrap/tryit.asp?filename=trybs_alerts_fade&stacked=h
element.find('div.fc-title').append("<span id='eventTimes'><i class='fas fa-times'/></span>");
element.find("#eventTimes").click(function () {
deleteBookingById(event._id);
$('.tooltip').remove();
});
function deleteBookingById(id) {
if (confirm('Do you want to remove this booking?')) {
// ToDo: C# delete part
$('#calendar').fullCalendar('removeEvents', id);
setNormalBehavior();
}
}

FIre event on toggle of v-show

I am using Laravel with Vuejs. i want to fire an event on component show or hide. how to achieve this?
<album-images v-show ="!gallery"
:album = "album"
:image-arr = "imageArr"></album-images>
You can use a watcher:
watch: {
gallery: {
handler: value => {
this.$emit('gallery-toggled', value)
}
}
}
or a computed:
computed: {
toggle () {
this.$emit('gallery-toggled', this.gallery)
}
}
If you want to do something after the view has updated (i.e the component is actually visible or hidden), you may have to wrap the $emit in nextTick(), see Vue.nextTick( [callback, context] ).
It depends on the exact order of updating the watch or computed, but if you are getting unexpected results in the receiver of the event, this is an option to try.
See the discussion here.
this.$nextTick(function() {
this.$emit('galleryVisible', this.gallery)
});

emberjs: how to trigger a custom event in a View

I would like to turn a primitive event (a click) into a semantic event, like "deleteTodo"
This is described here, but not how to implement :(
I have the following code:
App.TodoView = Em.View.extend({
click: function(e) {
this.trigger("deleteTodo");
}
});
App.Router.map(function(match) {
match('/').to('index');
});
App.IndexRoute = Ember.Route.extend({
deleteTodo: function(e) {
// this code is never executed :(
}
}) ;
After I perform the 'click', I see that the TodoView click function is called, but not the deleteTodo function from the IndexRoute. Any suggestions what might go wrong here ?
CHeers
You can use this.get("controller").send("deleteTodo"). This will send a message to the controller, if the controller doesn't handle deleteTodo it will bubble to the router and be handled there.
click: function(e) {
this.get('controller').send("deleteTodo");
}
In your router you will also need to define the event:
events: {
doStuff: function(e) {
alert("Do stuff") ;
}
}
http://jsfiddle.net/9Xasr/7/
I would typically do record deletion in the controller. Seems like putting that in a router event would not be ideal.

Slickgrid - One-click checkboxes?

When I create a checkbox column (through use of formatters/editors) in Slickgrid, I've noticed that it takes two clicks to interact with it (one to focus the cell, and one to interact with the checkbox). (Which makes perfect sense)
However, I've noticed that I am able to interact with the checkbox selectors plugin (for selecting multiple rows) with one click. Is there any way I can make ALL of my checkboxes behave this way?
For futher readers I solved this problem by modifing the grid data itself on click event. Setting boolean value to opposite and then the formatter will display clicked or unclicked checkbox.
grid.onClick.subscribe (function (e, args)
{
if ($(e.target).is(':checkbox') && options['editable'])
{
var column = args.grid.getColumns()[args.cell];
if (column['editable'] == false || column['autoEdit'] == false)
return;
data[args.row][column.field] = !data[args.row][column.field];
}
});
function CheckboxFormatter (row, cell, value, columnDef, dataContext)
{
if (value)
return '<input type="checkbox" name="" value="'+ value +'" checked />';
else
return '<input type="checkbox" name="" value="' + value + '" />';
}
Hope it helps.
The way I have done it is pretty straight forward.
First step is you have to disable the editor handler for your checkbox.
In my project it looks something like this. I have a slickgridhelper.js to register plugins and work with them.
function attachPluginsToColumns(columns) {
$.each(columns, function (index, column) {
if (column.mandatory) {
column.validator = requiredFieldValidator;
}
if (column.editable) {
if (column.type == "text" && column.autocomplete) {
column.editor = Slick.Editors.Auto;
}
else if (column.type == "checkbox") {
//Editor has been diasbled.
//column.editor = Slick.Editors.Checkbox;
column.formatter = Slick.Formatters.Checkmark;
}
}
});
Next step is to register an onClick event handler in your custom js page which you are developing.
grid.onClick.subscribe(function (e, args) {
var row = args.grid.getData().getItems()[args.row];
var column = args.grid.getColumns()[args.cell];
if (column.editable && column.type == "checkbox") {
row[column.field] = !row[column.field];
refreshGrid(grid);
}
});
Now a single click is suffice to change the value of your checkbox and persist it.
Register a handler for the "onClick" event and make the changes to the data there.
See http://mleibman.github.com/SlickGrid/examples/example7-events.html
grid.onClick.subscribe(function(e, args) {
var checkbox = $(e.target);
// do stuff
});
The only way I found solving it is by editing the slick.checkboxselectcolumn.js plugin. I liked the subscribe method, but it haven't attached to me any listener to the radio buttons.
So what I did is to edit the functions handleClick(e, args) & handleHeaderClick(e, args).
I added function calls, and in my js file I just did what I wanted with it.
function handleClick(e, args) {
if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) {
......
//my custom line
callCustonCheckboxListener();
......
}
}
function handleHeaderClick(e, args) {
if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) {
...
var isETargetChecked = $(e.target).is(":checked");
if (isETargetChecked) {
...
callCustonHeaderToggler(isETargetChecked);
} else {
...
callCustonHeaderToggler(isETargetChecked);
}
...
}
}
Code
pastebin.com/22snHdrw
Search for my username in the comments
I used the onBeforeEditCell event to achieve this for my boolean field 'can_transmit'
Basically capture an edit cell click on the column you want, make the change yourself, then return false to stop the cell edit event.
grid.onBeforeEditCell.subscribe(function(row, cell) {
if (grid.getColumns()[cell.cell].id == 'can_transmit') {
if (data[cell.row].can_transmit) {
data[cell.row].can_transmit = false;
}
else {
data[cell.row].can_transmit = true;
}
grid.updateRow(cell.row);
grid.invalidate();
return false;
}
This works for me. However, if you're using the DataView feature (e.g. filtering), there's additional work to update the dataview with this change. I haven't figured out how to do that yet...
I managed to get a single click editor working rather hackishly with DataView by calling
setTimeout(function(){ $("theCheckBox").click(); },0);
in my CheckBoxCellEditor function, and calling Slick.GlobalEditorLock.commitCurrentEdit(); when the CheckBoxCellEditor created checkbox is clicked (by that setTimeout).
The problem is that the CheckBoxCellFormatter checkbox is clicked, then that event spawns the CheckBoxCellEditor code, which replaces the checkbox with a new one. If you simply call jquery's .click() on that selector, you'll fire the CheckBoxCellEditor event again due because slickgrid hasn't unbound the handler that got you there in the first place. The setTimeout fires the click after that handler is removed (I was worried about timing issues, but I was unable to produce any in any browser).
Sorry I couldn't provide any example code, the code I have is to implementation specific to be useful as a general solution.

Resources