Ember.js: controller actions not triggered by 'sendAction' from a component - javascript

I'm facing some issues in an Ember.js application and some sendAction that do not work.
Basically, I have:
- an Input component (the one triggering the action)
- one controller/template (that contains the rendering of the component)
- one other controller / template, in which we render the previous controller.
Here's the code.
First, the text field component:
Tm.JiraCloudInputComponent = Ember.TextField.extend({
keyDown: function (event) {
if (event.keyCode === 27) {
this.sendAction('cancelJiraCloudUrl');
}
if (event.keyCode === 13) {
event.preventDefault();
this.sendAction('saveJiraCloudUrl');
}
},
blur: function () {
this.sendAction('saveJiraCloudUrl');
}
})
The controller that has the actions:
Tm.JiraCloudController = Ember.Controller.extend({
jiraCloudEnabled: Ember.computed.oneWay('content.jiraCloudEnabled'),
jiraCloudUrl: Ember.computed.oneWay('content.jiraCloudUrl'),
successMessage: '',
errorMessage: '',
actions: {
saveJiraCloudUrl: function () {
if (Tm.isEmpty(this.get('jiraCloudUrl'))) {
this.unlinkJiraCloud();
} else {
this.linkJiraCloud();
}
},
cancelJiraCloudUrl: function () {
this.set('jiraCloudUrl', this.get('model.jiraCloudUrl'));
this.set('jiraCloudEnabled', this.get('model.jiraCloudEnabled'));
this.clearMessages();
}
}
});
and its associated template:
{{#default-box id="jira-cloud-settings" class="box-full-width"}}
{{box-header title="settings.jira_cloud"}}
{{#box-content}}
{{jira-cloud-input class="form-control" value=jiraCloudUrl placeholder="Jira cloud url"}}
<button type="button" class="btn btn-default" title="Link Jira cloud" {{action "saveJiraCloudUrl"}}>Link Jira cloud</button>
{{/box-content}}
{{/default-box}}
Note: the boxes (default-box, box-content) are also components.
And last, the template in which we render the previous controller:
{{render "jiraCloud" content}}
I can't find anything obvious why it does not work. No error is raised by the 'sendAction' calls.
Note that the action on the button works like a charm.
Thanks for your help,
Vincent

Well, I've just been stupid in fact. I was too used to the old "send" so I forgot to do the mapping when rendering the component:
{{jira-cloud-input save="saveJiraCloudUrl" esc="cancelJiraCloudUrl" class="form-control" value=jiraCloudUrl placeholder="Jira cloud url"}}
And in the code:
Tm.JiraCloudInputComponent = Ember.TextField.extend({
keyDown: function (event) {
if (event.keyCode === 27) {
this.sendAction('esc');
}
if (event.keyCode === 13) {
this.sendAction('save');
event.preventDefault();
}
},
blur: function () {
this.sendAction('save');
}
})

Related

How to use two #click $emit function in vue.js with electron

I'm developing login page with connect with SSHFS drive.
And some of the functions are working but, I'm confusing with my codes. I analyze my code but I don't know why it's working, and why this is working.
My application looks like this.
It's just simple login web app with Electron. So When I click login and if the ID & and password is right, it will mount SSHFS drive and goes to system tray icon to hide. After that it should open the explorer that mounted folder.
So I thought When I click login button, and two #click with $emit functions should work like this.
<div class="form-item">
<button class="login" v-show="showConnectButton" :class="{ 'success': isConnected, 'connecting-disconnecting': isConnectingOrDisconnecting }" :disabled="isConnectingOrDisconnecting" #click="[$emit(isConnected ? 'disconnect' : 'connect', conn), $emit('open', conn.mountPoint)]">{{isConnected ? 'disconnect' : 'login'}}</button>
</div>
I use this code but it seems the code tries mount the drive early then $emit(isConnected) function so It shows the error that Cannot find 'E' Drive that I set in another code.
It's an error that 'System cannot find 'E' drive in my computer'
I tried to put mount function to another function but it doesn't work. Only the 'Hide to tray' and 'Mount Drive function' works well. So I think it should fix only a few code and it will work but I don't know where should I fix the code.
And I already searched in stackoverflow questions but it seems doesn't fit in my question.
Here's my part of my full code. Please check the code and give me some advise. Thanks.
My ConnectionItem.vue full Code
<template>
<div class="item">
<div class="form-item">
<button class="login" v-show="showConnectButton" :class="{ 'success': isConnected, 'connecting-disconnecting': isConnectingOrDisconnecting }" :disabled="isConnectingOrDisconnecting" #click="[$emit(isConnected ? 'disconnect' : 'connect', conn)]">{{isConnected ? 'disconnect' : 'login'}}</button>
</div>
</div>
</template>
<script>
import { remote } from 'electron'
import Icon from '#/components/Icon'
const window = remote.getCurrentWindow()
export default {
name: 'connection-item',
components: {
Icon
},
props: {
conn: {
type: Object,
required: true
},
mode: {
type: String,
required: false,
default: 'none'
}
},
computed: {
isConnected (conn) {
// this.isActive = !this.enable
this.$emit('open', conn.mountPoint)
window.hide()
return this.conn.status === 'connected'
},
isConnectingOrDisconnecting () {
return this.conn.status === 'connecting' || this.conn.status === 'disconnecting'
},
showConnectButton () {
// window.hide()
// openLocal(path);
// this.$emit('open', conn.mountPoint)
return this.mode === 'none' || this.isConnected || this.isConnectingOrDisconnecting
},
isEditing () {
return this.mode === 'edit' && !this.isConnected && !this.isConnectingOrDisconnecting
},
showDeleteButton () {
return this.mode === 'delete' && !this.isConnected && !this.isConnectingOrDisconnecting
},
showRunningInBackgroundNotification () {
if (!this.runningInBackgroundNotificationShowed) {
if (this.$store.state.Settings.settings.displayTrayMessageOnClose) {
this.notify('program runs in background (network connected)') // 1
this.notify('Click Quit button when you disconnect drive') // 2
this.runningInBackgroundNotificationShowed = true
}
}
}
}
}
</script>

Stripe js: don't let empty form be sent

I'm trying to avoid letting users submit stripe form when inputs are empty, I`m using stripe.js elements integration to render my form and handle form submition inside my vue component.
this.cardNumberElement.on('change', this.enableForm);
this.cardExpiryElement.on('change', this.enableForm);
this.cardCvcElement.on('change', this.enableForm);
After checking the docs I tried to use the change event on inputs but this is not working sice the user can just not type anything and click submit button.
This is my component:
mounted()
{
console.log(this.$options.name + ' component succesfully mounted');
this.stripe = Stripe(this.stripePK);
this.elements = this.stripe.elements();
this.cardNumberElement = this.elements.create('cardNumber', {style: this.stripeStyles});
this.cardNumberElement.mount('#card-number-element');
this.cardExpiryElement = this.elements.create('cardExpiry', {style: this.stripeStyles});
this.cardExpiryElement.mount('#card-expiry-element');
this.cardCvcElement = this.elements.create('cardCvc', {style: this.stripeStyles});
this.cardCvcElement.mount('#card-cvc-element');
let stripeElements = document.querySelectorAll("#card-number-element, #card-expiry-element, #card-cvc-element");
stripeElements.forEach(el => el.addEventListener('change', this.printStripeFormErrors));
this.cardNumberElement.on('change', this.enableForm);
this.cardExpiryElement.on('change', this.enableForm);
this.cardCvcElement.on('change', this.enableForm);
},
methods:
{
...mapActions('Stripe', ['addSource', 'createSourceAndCustomer']),
...mapMutations('Stripe', ['TOGGLE_PAYMENT_FORM']),
...mapMutations('Loader', ['SET_LOADER', 'SET_LOADER_ID']),
enableForm:function(event){
if(event.complete){
this.disabled = false;
}
else if(event.empty){
this.disabled = true;
}
},
submitStripeForm: function()
{
this.SET_LOADER({ status:1, message: 'Procesando...' });
var self = this;
this.stripe.createSource(this.cardNumberElement).then(function(result) {
if (result.error) {
self.cardErrors = result.error.message;
}
else {
self.stripeSourceHandler(result.source.id);
}
});
},
stripeSourceHandler: function(sourceId)
{
console.log('stripeSourceHandler');
this.cardNumberElement.clear();
this.cardExpiryElement.clear();
this.cardCvcElement.clear();
if(this.customerSources.length == 0)
{
console.log('createSourceAndCustomer');
this.createSourceAndCustomer({ id: sourceId });
}
else
{
console.log('addSource');
this.addSource({ id: sourceId });
}
},
printStripeFormErrors: function(event)
{
if(event.error)
{
self.cardErrors = event.error.message
}
else
{
self.cardErrors = '';
}
}
}
Given the stripe docs, the use of the event seems correct (though it can be improved a bit with using this.disabled = !event.complete to cover error case and not only empty case).
You may try to console.log in the event callback enableForm to check if event is well fired.
Anyway, it's more likely coming from the disabling logic of the submit button and it misses in your post. I've created below a fake secure-component that triggers a change event when value change.
The interesting part in on the container component :
Submit is disabled by default through data disabled,
Submit is enabled if event received has a property complete set to true. If false, it is disabled.
Hope it will help you to focus your trouble.
/**
Mock component to emulate stripes card element behavior with change event
*/
const SecureInput = {
template: '<input type="text" v-model="cardnumber"/>',
data: () => ({
cardnumber: null
}),
watch: {
cardnumber: function(val) {
if(!val) {
this.$emit('change', {empty: true, error: false, complete: false});
return;
}
if(val.length < 5) {
this.$emit('change', {empty: false, error: true, complete: false});
return;
}
this.$emit('change', {empty: false, error: false, complete: true});
}
}
}
/* Logic is here */
const app = new Vue({
el: '#app',
components: {
SecureInput
},
data: {
disabled: true
},
methods: {
updateDisable: function(event) {
this.disabled = !event.complete;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form #submit.prevent="$emit('submitted')">
<p><secure-input #change="updateDisable"/></p>
<p><input type="submit" :disabled="disabled"/></p>
</form>
</div>

events in fullcalendar/VUE application not updating

Inside of my cal.vue component I have a fullcalendar component. Within cal.vue I have a method called submit. Inside of submit I make a (successful) reference to the fullcalendar component with this.$refs.calendar. However when I do this.$refs.calendar.$emit('refetchEvents'); in my submit function the events are not fetched (my events are not updated on my calendar). Why are my events not being updated upon submit and how can I update them?
Here is the relevant code:
<template>
<div id="calendar" :navL="navLinks" :event-sources="eventSources" #event-selected="eventSelected" #event-created="eventCreated" :config="config" >
<button class="btn btn-secondary" v-on:click="submit()">
Submit
</button>
<full-calendar id="target" ref="calendar" :event-sources="eventSources" #event-selected="eventSelected" #day-click="click"#event-created="eventCreated" :config="config"></full-calendar>
</div>
</template>
<script>
const self = this;
export default {
name: 'calendar',
data() {
return {
eventSources: [
{
url: 'http://localhost:3000/getAppointments',
type: 'GET',
data: {
id: this.$store.getters.user
},
error: function() {
alert('there was an error while fetching events!');
},
}
],
};
},
methods: {
submit: function() {
console.log(this.$refs.calendar); //prints fullcalendar VUE component
this.$refs.calendar.$emit('refetchEvents'); //nothing appears to happen here
},
},
}
</script>
According to the documentation,
this.$refs.calendar.$emit('refetchEvents')
should be
this.$refs.calendar.$emit('refetch-events')
If using the vanilla FullCalendar via npm packages: "#fullcalendar/core", "#fullcalendar/daygrid", "#fullcalendar/timegrid", "#fullcalendar/vue"
html: <full-calendar ref="fullCalendar" :events="events" />
inside a method to update an event:
let calendar = this.$refs.fullCalendar.getApi()
const updatedEvent = calendar.getEventById(eventId)
updatedEvent.setProp('title', 'New Title')
updatedEvent.setProp('backgroundColor', 'yellow')
updatedEvent.setProp('borderColor', 'yellow')
updatedEvent.setProp('textColor', 'black')
The above will rerender the fullCalendar event but will not change your this.events array. There may be something that's a bit more all inclusive for ALL events in one shot otherwise you'd have to do:
this.events.forEach(event => {
...similar code the above
})

How can i detect multiple key press (up/down) on Vue.js

I am trying to implement common chat app on Vue.js.
window.onload = function () {
new Vue({
el: '#vue-chat',
data: {
body: ''
},
methods: {
fooMethod: function () {
alert('foo');
},
barMethod: function () {
alert('bar');
}
}
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.3/vue.js"></script>
<div id="vue-chat">
<ul class="comments">
<li></li>
</ul>
<input type="text" v-model="body" #keyup.enter="fooMethod">
</div>
and i want to call barMethod when users press enter key and shift key at the same time.
I read docs however I could not find the way.
Thank you for reading!
With the shift key and other modifier keys you can see if they were pressed through the event object.
I'd use a single method with #keyup.enter and then decide to which method to call based on the event's shiftKey value.
new Vue({
el: '#app',
data: {
message: 'Hi',
},
methods: {
action(event) {
if (event.shiftKey) {
this.shiftKeyPressed()
} else {
this.shiftKeyNotPressed()
}
},
shiftKeyPressed() {
console.log('Shift key was pressed.')
},
shiftKeyNotPressed() {
console.log('Shift key was NOT pressed.')
},
}
})
Here's a quick demo: https://jsfiddle.net/bj75cyd3/
There is no trivial way to do what you want.
You can't reach your goal through modifiers; you have to drop the .enter modifier and deal with the keyup event, as well as the keydown event.
<input type="text" v-model="body" #keyup="keyUp" #keydown="keyDown">
There are a short answer and a long answer suggesting how to track multiple keys pressed at once in JavaScript.
Based on the answers linked above, we can build the basis of our Vue solution:
data: {
shiftPressed: false
},
methods: {
keyDown: function (event) {
if (event.keyCode === 16) {
this.shiftPressed = true
}
},
keyUp: function(event) {
if (event.keyCode === 16) {
this.shiftPressed = false
}
if (this.shiftPressed && (event.keyCode === 13)) {
this.shiftPressed = false // avoid double trigger
this.fooMethod()
}
}
}

Angular.JS onclick function only called on first click

I am currently working on a small aplication using Angular.JS
In my view i have following button
<md-button class="md-primary" ng-click="editUser(user, $event)">Edit</md-button>
the editUser method looks something like this:
$scope.editUser = function (user, $event) {
$scope.userToEdit = user;
$mdDialog.show({
controller: DialogController,
targetEvent: $event,
templateUrl: '/js/modules/user/views/edit.tmpl.html',
parent: angular.element(document.body),
clickOutsideToClose: true,
scope: $scope
})
.
then(function (answer) {
if (answer == "save") {
for (right in $scope.allSystemRightsStatements) {
if ($scope.allSystemRightsStatements[right].selected) {
if( $scope.userToEdit.rights==null){
$scope.userToEdit.rights = [];
}
$scope.userToEdit.rights.push($scope.allSystemRightsStatements[right]);
}
}
$scope.updateUser($scope.userToEdit);
}
$scope.userToEdit = {};
}, function () {
$scope.userToEdit = {};
});
};
$scope.updateUser = function (user) {
//userService.updateUser makes a $http PUT request
var promise = userService.updateUser(user);
promise.then(function (result) {
$mdToast.show(
$mdToast.simple(result.message)
.position($scope.getToastPosition())
.hideDelay(3000)
);
}, function (reason) {
$mdToast.show(
$mdToast.simple(reason)
.position($scope.getToastPosition())
.hideDelay(3000)
);
}, function (update) {
});
};
Now the dialog is nicely shown and the answer function is also called, everything as expected.
However, when I click the button a second time the editUser funciton is not executed. As if the onClick event from the button had been removed at dialog close.
Any help on solving this problem is greatly appreciated,
Thanks
As said here
it is probably a good idea to explicitly mention that the scope will be destroyed upon hiding the dialog (so people shouldn't pass a controller's $scope directly).
(regarding the scope you are passing to mdDialog)
So, as the scope is destroyed, angular is not binding your button with any action

Categories