Trying to set creditCardExpMonth to the current month in the below Magento 2 JavaScript class (cc-form.js). The option month values are 1-12. When I manually add a month value like 3 creditCardExpMonth: 3, to the defaults:{ }, it works as expected. I just can't seem to figure out how to set it to the current month dynamically. I'm open to any solution that allows for the value to be overridden by the user's selection but I'd prefer it be inside this class or on the html page and not a JQuery update after the page loads.
I created a getCurrentMonth() function in this class but couldn't figure out how to access it correctly to set creditCardExpMonth to a default value.
define(
[
'underscore',
'Mageplaza_Osc/js/view/payment/default',
'Magento_Payment/js/model/credit-card-validation/credit-card-data',
'Magento_Payment/js/model/credit-card-validation/credit-card-number-validator',
'mage/translate'
],
function (_, Component, creditCardData, cardNumberValidator, $t) {
return Component.extend({
defaults: {
creditCardType: '',
creditCardExpYear: '',
creditCardExpMonth: '',
creditCardNumber: '',
creditCardSsStartMonth: '',
creditCardSsStartYear: '',
creditCardVerificationNumber: '',
selectedCardType: null
},
initObservable: function () {
this._super()
.observe([
'creditCardType',
'creditCardExpYear',
'creditCardExpMonth',
'creditCardNumber',
'creditCardVerificationNumber',
'creditCardSsStartMonth',
'creditCardSsStartYear',
'selectedCardType'
]);
return this;
},
initialize: function() {
var self = this;
this._super();
//Set credit card number to credit card data object
this.creditCardNumber.subscribe(function(value) {
var result;
self.selectedCardType(null);
if (value == '' || value == null) {
return false;
}
result = cardNumberValidator(value);
if (!result.isPotentiallyValid && !result.isValid) {
return false;
}
if (result.card !== null) {
self.selectedCardType(result.card.type);
creditCardData.creditCard = result.card;
}
if (result.isValid) {
creditCardData.creditCardNumber = value;
self.creditCardType(result.card.type);
}
});
//Set expiration year to credit card data object
this.creditCardExpYear.subscribe(function(value) {
creditCardData.expirationYear = value;
});
//Set expiration month to credit card data object
this.creditCardExpMonth.subscribe(function(value) {
creditCardData.expirationYear = value;
});
//Set cvv code to credit card data object
this.creditCardVerificationNumber.subscribe(function(value) {
creditCardData.cvvCode = value;
});
},
getCode: function() {
return 'cc';
},
getData: function() {
return {
'method': this.item.method,
'additional_data': {
'cc_cid': this.creditCardVerificationNumber(),
'cc_ss_start_month': this.creditCardSsStartMonth(),
'cc_ss_start_year': this.creditCardSsStartYear(),
'cc_type': this.creditCardType(),
'cc_exp_year': this.creditCardExpYear(),
'cc_exp_month': this.creditCardExpMonth(),
'cc_number': this.creditCardNumber()
}
};
},
getCcAvailableTypes: function() {
return window.checkoutConfig.payment.ccform.availableTypes[this.getCode()];
},
getIcons: function (type) {
return window.checkoutConfig.payment.ccform.icons.hasOwnProperty(type)
? window.checkoutConfig.payment.ccform.icons[type]
: false
},
getCcMonths: function() {
return window.checkoutConfig.payment.ccform.months[this.getCode()];
},
getCcYears: function() {
return window.checkoutConfig.payment.ccform.years[this.getCode()];
},
hasVerification: function() {
return window.checkoutConfig.payment.ccform.hasVerification[this.getCode()];
},
hasSsCardType: function() {
return window.checkoutConfig.payment.ccform.hasSsCardType[this.getCode()];
},
getCvvImageUrl: function() {
return window.checkoutConfig.payment.ccform.cvvImageUrl[this.getCode()];
},
getCvvImageHtml: function() {
return '<img src="' + this.getCvvImageUrl()
+ '" alt="' + $t('Card Verification Number Visual Reference')
+ '" title="' + $t('Card Verification Number Visual Reference')
+ '" />';
},
getSsStartYears: function() {
return window.checkoutConfig.payment.ccform.ssStartYears[this.getCode()];
},
getCcAvailableTypesValues: function() {
return _.map(this.getCcAvailableTypes(), function(value, key) {
return {
'value': key,
'type': value
}
});
},
getCcMonthsValues: function() {
return _.map(this.getCcMonths(), function(value, key) {
return {
'value': key,
'month': value.substring(0,2)
}
});
},
getCcYearsValues: function() {
return _.map(this.getCcYears(), function(value, key) {
return {
'value': key,
'year': value
}
});
},
getCurrentMonth: function() {
var d = new Date();
var n = d.getMonth() + 1;
return n;
},
getCurrentYear: function() {
var d = new Date();
var n = d.getYear();
return n;
},
getSsStartYearsValues: function() {
return _.map(this.getSsStartYears(), function(value, key) {
return {
'value': key,
'year': value
}
});
},
isShowLegend: function() {
return false;
},
getCcTypeTitleByCode: function(code) {
var title = '';
_.each(this.getCcAvailableTypesValues(), function (value) {
if (value['value'] == code) {
title = value['type'];
}
});
return title;
},
formatDisplayCcNumber: function(number) {
return 'xxxx-' + number.substr(-4);
},
getInfo: function() {
return [
{'name': 'Credit Card Type', value: this.getCcTypeTitleByCode(this.creditCardType())},
{'name': 'Credit Card Number', value: this.formatDisplayCcNumber(this.creditCardNumber())}
];
}
});
});
Here is the knockout HTML with select data-bind just in case it's needed (taken from Magento payment cc-form.html):
<select name="payment[cc_exp_month]"
class="select select-month"
data-bind="attr: {id: getCode() + '_expiration', 'data-container': getCode() + '-cc-month', 'data-validate': JSON.stringify({required:true, 'validate-cc-exp':'#' + getCode() + '_expiration_yr'})},
enable: isActive($parents),
options: getCcMonthsValues(),
optionsValue: 'value',
optionsText: 'month',
optionsCaption: $t('Month'),
value: creditCardExpMonth">
</select>
If this script is run every time the page loads, you could do something like this:
defaults: {
creditCardType: '',
creditCardExpYear: '',
creditCardExpMonth: (function(){
return ((new Date()).getMonth()+1);
})(),
creditCardNumber: '',
creditCardSsStartMonth: '',
creditCardSsStartYear: '',
creditCardVerificationNumber: '',
selectedCardType: null
}
or if you want something cleaner, you can refactor the code into a function that is defined prior to this object creation:
function (_, Component, creditCardData, cardNumberValidator, $t) {
function getCurrentMonth() {
return ((new Date()).getMonth()+1);
}
return Component.extend({
defaults: {
creditCardType: '',
creditCardExpYear: '',
creditCardExpMonth: getCurrentMonth(),
creditCardNumber: '',
creditCardSsStartMonth: '',
creditCardSsStartYear: '',
creditCardVerificationNumber: '',
selectedCardType: null
},
Related
I'm new in Vue.js and tried to convert some legacy code for pagination. I've created a pager component which accepts a function as one of its params. But it's causing an infinite UI render loop.
Could you help me to resolve or suggest some solution for such problem?
Here is my pager component js:
const PagerComponent = {
name: "pagerComponent",
template: "#pagerComponent",
props: {
pageSize: Number,
pageIndex: Number,
totalPages: Number,
totalRecords: Number,
pageSlide: Number,
hasNextPage: Boolean,
hasPrevPage: Boolean,
pages: Array,
loadFunc: Function
},
data() {
return {
pager: {
pageSize: 0,
pageIndex: 0,
totalPages: 0,
totalCount: 0,
pageSlide: 1,
hasNextPage: false,
hasPrevPage: false,
pages: [],
loadFunc: function () { }
}
}
},
methods: {
load(index) {
this.pager.pageIndex = index;
if (this.pager.loadFunc != null) {
this.pager.loadFunc();
}
},
isActivePage(page) {
return this.pager.pageIndex + 1 == page;
},
update(newPager) {
this.pager.pageSize = newPager.pageSize;
this.pager.pageIndex = newPager.pageIndex;
this.pager.totalPages = newPager.totalPages;
this.pager.totalCount = newPager.totalCount;
this.pager.hasNextPage = newPager.hasNextPage;
this.pager.hasPrevPage = newPager.hasPrevPage;
this.generatePages();
},
generatePages() {
this.pager.pages = [];
var pageNum = this.pager.pageIndex + 1;
var pageFrom = Math.max(1, pageNum - this.pager.pageSlide);
var pageTo = Math.min(this.pager.totalPages, pageNum + this.pager.pageSlide);
pageFrom = Math.max(1, Math.min(pageTo - this.pager.pageSlide, pageFrom));
pageTo = Math.min(this.pager.totalPages, Math.max(pageFrom + this.pager.pageSlide, pageNum == 1 ? pageTo + this.pager.pageSlide : pageTo));
for (var i = pageFrom; i <= pageTo; i++) {
this.pager.pages.push(i);
}
}
},
computed: {
hasPages() {
if (this.pager.pages == null)
return false;
return this.pager.pages.length > 0;
},
doNotHavePrevPage() {
return !this.pager.hasPrevPage;
},
doNotHaveNextPage() {
return !this.pager.hasNextPage;
}
},
beforeMount() {
this.pager.pageSize = this.pageSize;
this.pager.pageIndex = this.pageIndex;
this.pager.totalPages = this.totalPages;
this.pager.totalCount = this.totalRecords;
this.pager.hasNextPage = this.hasNextPage;
this.pager.hasPrevPage = this.hasPrevPage;
this.pager.loadFunc = this.loadFunc;
this.pager.pages = this.pages || [];
this.generatePages();
},
mounted() {
}
}
Here is how it's used in html:
<pager-Component v-bind="Pager" v-bind:load-Func="GetItems" ref="pager"></pager-Component>
And GetItems funciton:
function () {
var self = this;
const data = {
Pager: self.Pager,
Filter: []
};
$.ajax({
url: self.GetItemsUrl,
type: "POST",
dataType: "json",
busy: self.Loading,
data: data
}).done(function (result) {
if (result.isSuccess) {
self.$refs.pager.update(result.data.pager);
self.Items.splice(0);
result.data.items.map(function (value, key) {
self.Items.push(value);
});
}
else {
alert(result.data.errors[0]);
}
});
}
Finally after tones of tests, the solution was found and it's pretty easy.
I just needed to use v-on:click instead of :click. I just don't know why lot of tutorials suggest to use :click if it doesn't work
So for example use
<div v-on:click="load(pageIndex)">My button</div>
instead of
<div :click="load(pageIndex)">My button</div>
I'm struggling for a few days with importing my Vuex store into multiple different modules. For some reason it seems that my store is creating new instances for every import. In one Vue object i am setting a value to the store, but for an unknown reason to me it is not accessible in another Vue object. I have 3 files: store.js, addresses.js, and relation.js. In the store.js file the following code is present:
const Vue = require('vue').default;
const Vuex = require("vuex");
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
addresses: {
address: 'test'
}
},
mutations: {
setAddress(state, payload) {
state.addresses.address = payload;
}
},
actions: {
setAddress(state, payload) {
state.commit('setAddress', payload);
}
},
getters: {
getAddress(state) {
return state.addresses.address;
}
},
});
export default store;
My addresses.js contains:
import agent from '../agent.js';
const Vue = require("vue").default;
import __ from '../translations/translate';
import Swal from '../../../app-assets/vendors/js/extensions/sweetalert2.all.min'
import store from "../store";
var address = new Vue({
el: '#address-form',
data: {
address: {
id: null,
postal_code: '',
house_number: '',
house_number_addition: '',
street_name: '',
place: '',
country: defaultAddressCountry[0],
latitude: null,
longitude: null,
extra_address_line: null,
safety_instructions: null,
notes: null,
po_box_number: '',
is_po_box: false
},
address_countries: addressCountries,
inputErrors: {
postal_code: '',
house_number: '',
street_name: '',
place: '',
po_box_number: '',
}
},
methods: {
completeAddress: function(){
let self = this;
if(this.address.postal_code && this.address.house_number && this.address.postal_code.length > 3 && this.address.house_number.length > 0){
$('#complete-address i').addClass('rotate');
agent.Address.completeAddress(this.address.postal_code, this.address.house_number.toString() + this.address.house_number_addition, this.address.country)
.then(function(response){
self.address.street_name = response.data.address.street;
self.address.place = response.data.address.locality;
self.address.latitude = response.data.location.latitude;
self.address.longitude = response.data.location.longitude;
$('#complete-address i').removeClass('rotate');
self.validateFields();
}).catch(function(){
$('#complete-address').addClass('text-color-red');
setTimeout(function(){
$('#complete-address').removeClass('text-color-red');
}, 1000)
$('#complete-address i').removeClass('rotate');
self.address.street_name = null;
self.address.place = null;
self.address.latitude = null;
self.address.longitude = null;
});
}else{
if(!this.address.postal_code || this.address.postal_code.length <= 4){
$('#input-postal-code').addClass('has-error');
}
if(!this.address.house_number || this.address.house_number.length == 0){
$('#input-house-number').addClass('has-error');
}
setTimeout(function(){
$('#input-postal-code, #input-house-number').removeClass('has-error');
}, 500)
}
},
gotoMaps: function () {
open('https://maps.google.com/?q=' + this.address.latitude + ', ' + this.address.longitude);
},
sanitizeFields: function(){
this.address.postal_code = this.address.postal_code.replace(/[^0-9a-z]/gi, '').toUpperCase().substr(0, 6);
this.address.house_number = this.address.house_number.replace(/[^0-9]+/g, '').substr(0, 4);
this.address.house_number_addition = this.address.house_number_addition.replace(/[^0-9a-z]/gi, '').toUpperCase().substr(0, 2);
},
validateFields: function(){
let has_errors = false;
this.inputErrors.postal_code = '';
if(this.address.postal_code.length < 5){
this.inputErrors.postal_code = __('invalid input');
has_errors = true;
}
if(this.address.postal_code.length === 0){
this.inputErrors.postal_code = __('required');
has_errors = true;
}
this.inputErrors.house_number = '';
if(this.address.house_number.length === 0 && !this.address.is_po_box){
this.inputErrors.house_number = __('required');
has_errors = true;
}
this.inputErrors.street_name = '';
if(this.address.street_name.trim().length === 0 && !this.address.is_po_box){
this.inputErrors.street_name = __('required');
has_errors = true;
}
this.inputErrors.place = '';
if(this.address.place.trim().length === 0){
this.inputErrors.place = __('required');
has_errors = true;
}
this.inputErrors.po_box_number = '';
if(this.address.po_box_number.trim().length === 0 && this.address.is_po_box){
this.inputErrors.po_box_number = __('required');
has_errors = true;
}
return !has_errors;
},
saveAddress: function(){
if(this.address.is_po_box){
this.address.street_name = '';
this.address.house_number = '';
this.address.house_number_addition = '';
this.address.safety_instructions = null;
this.address.extra_address_line = null;
this.address.notes = null;
}else{
this.address.po_box_number = '';
}
if(this.validateFields()){
let self = this;
if(this.address.id) {
agent.Address.update(this.address);
}else{
agent.Address.create(this.address).then(function (result) {
if(result.status)
self.address = result.data;
}).catch(function(error){
if(error.response.status === 409){
let house_number = [error.response.data.house_number];
if(error.response.data.house_number_addition){
house_number.push(error.response.data.house_number_addition);
}
Swal.fire({
title: __('Existing address was found'),
icon: 'info',
html: '' +
'<table style="text-align:left;" class="table table-bordered">' +
'<tr>' +
'<td>'+__('Address')+'</td>' +
'<td>'+error.response.data.street_name+' ' + house_number.join('-') + '</td>' +
'</tr>' +
'<tr>' +
'<td>'+__('Postal code')+'</td>' +
'<td>'+error.response.data.postal_code+'</td>' +
'</tr>' +
'<tr>' +
'<td>'+__('Place')+'</td>' +
'<td>'+error.response.data.place+'</td>' +
'</tr>' +
'<tr>' +
'<td>'+__('Country')+'</td>' +
'<td>'+error.response.data.country+'</td>' +
'</tr>' +
'</table>',
customClass: 'swal-wide',
showCancelButton: true,
confirmButtonText: __('Use this address'),
cancelButtonText: __('No, create a new one'),
}).then(function(result){
if(result.value === true){
store.dispatch('setAddress', self.address)
console.log(store.state.addresses.address)
}
});
}
});
}
}
}
},
watch: {
address: {
handler(){
if(this.address.is_po_box){
$('.no-po-box').hide();
$('#extra-address-info').hide();
$('#show-address-extra').attr('data-expanded', 'false');
$('#show-address-extra').find('i').removeClass('icon-chevron-up').addClass('icon-chevron-down');
$('.is-po-box').show();
}else{
$('.no-po-box').show();
$('.is-po-box').hide();
}
this.sanitizeFields();
},
deep: true
}
},
computed: {
showMapsIcon: function(){
return this.address.latitude && this.address.longitude;
}
},
created(){
$(document).on('click', '.address-selector', function(){
$('#address-modal').modal();
});
$(document).on('click', '#show-address-extra', function(){
if($(this).attr('data-expanded') == 'false') {
$('#extra-address-info').slideDown();
$(this).attr('data-expanded', 'true');
$(this).find('i').removeClass('icon-chevron-down').addClass('icon-chevron-up');
}else{
$('#extra-address-info').slideUp();
$(this).attr('data-expanded', 'false');
$(this).find('i').removeClass('icon-chevron-up').addClass('icon-chevron-down');
}
});
$('.dataTable').DataTable();
},
delimiters: ['[[' , ']]']
});
export default address;
My relation.js file:
import agent from '../../agent.js'
const Vue = require("vue").default;
import store from "../../store";
var relation = new Vue({
el: '#upsert-relation-form',
data: {
relation: {
id: null,
name: null,
is_supplier: false,
is_customer: true,
is_prospect: false,
is_debtor: true,
general_email_address: null,
general_phone_number: defaultCountryCode,
communication_language: languages.find(x => x.code == defaultLanguage[0])['code'],
account_manager: null,
tags: [],
industries: [],
},
languages: languages,
account_managers: [
{
id: 1,
name: 'Piet de Vries'
},
{
id: 2,
name: 'Willem Aardappel'
}
]
},
computed: {
visiting_address(){
console.log(store.getters.getAddress);
return store.getters.getAddress;
}
},
methods: {
completeAddress: function(address){
console.log(address);
}
},
created(){
let self = this;
$(document).on('click', '.tags-container', function(){
$(this).find('input').focus();
});
$(document).on('click', '.tags-container li a.remove', function(){
let field = $(this).closest('.tags-container').attr('data-field');
let val = $(this).closest('li').text().trim();
let index = self.relation[field].findIndex(function(tag){
return tag.value == val;
});
self.relation[field].splice(index, 1);
});
$(document).on('keyup', '.tags-container input', function(e){
let field = $(this).closest('.tags-container').attr('data-field');
if(e.keyCode === 13){
let tagValue = $(this).val().trim().toLowerCase().replace(/[^a-zA-Z 0-9]+/g, '');
if(tagValue.length < 3){
return;
}
let index = self.relation[field].findIndex(function(tag){
return tag.value == tagValue;
});
if(index !== -1){
$(this).val('');
return;
}
self.relation[field].push({
id: null,
value: tagValue
});
$(this).val('');
}
});
$(document).on('keydown', '.tags-container input', function(e){
let field = $(this).closest('.tags-container').attr('data-field');
if(e.keyCode === 8 && $(this).val().trim().length === 0){
self.relation[field].pop();
}
});
},
delimiters: ['[[' , ']]']
});
export default relation;
In addresses.js I see that the value is correctly set to the store. But in the relation.js file it still gets the original data set on initiation of the store.
It's supposed to be done the other way around, you import the module in your vuex and not the vuex in your module.
So you should have a "Master module" that looks like this:
import Vue from 'vue';
///////////Vuex et store
import Vuex from 'vuex';
/////////////////Modules
import media from "./modules/media";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
}
,
actions: {
},
modules: {
media
}
})
and then your module should simply be like so:
const state = {
};
const getters = {
};
const mutations = {
};
const actions = {
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
};
The namespaced part is optional and here is the link to the doc:
https://vuex.vuejs.org/fr/guide/modules.html
In the doc it's organised in a single file, the way I presented it correspond to one file for the "Master Module" which is your store really, and then a file by module.
var options = {
url: function (phrase) {
return "location/autoComplete?key=" + phrase;
},
getValue: "cityName",
template: {
type: "custom",
method: function (value, item) {
return value + ", " + item.stateName;
}
},
list: {
onClickEvent: function () {
var selectedLongitude = $("#autoCompleteSearch").getSelectedItemData().longitude;
var selectedLatitude = $("#autoCompleteSearch").getSelectedItemData().latitude;
userPos = {
lat: Number(selectedLatitude),
lng: Number(selectedLongitude)
};
map.setCenter(userPos);
map.setZoom(17)
},
showAnimation: {
type: "fade", //normal|slide|fade
time: 400,
callback: function () {
}
},
hideAnimation: {
type: "slide", //normal|slide|fade
time: 400,
callback: function () {
}
}
}
};
This is the code I am using right now to fetch a remote webservice data. Now I need to get the data and remove the duplicate "city names". Also I need to show "Data not available" or some custom method when there is no data from the webservice. I am kind of stuck. Would be great if someone could point me on how to achieve this.
You can get rid of duplicates using a helper function:
function removeDuplicates(input) {
if (!input) { //Falsy input
return input;
}
var isArray = Array.isArray(input) ? [] : {};
var items = [];
var output = {};
for (var key in input) {
if (items.indexOf(input[key]) < 0) {
items.push(input[key]);
if (!isArray) {
output[key] = input[key];
}
}
}
return isArray ? items : output;
}
You can show your custom text if Array.isArray(input) ? (input.length === 0) : (Object.keys(input).length === 0 && input.constructor === Object).
I am trying to move the code vm.canGoForward from my controller to a service to hide the implementation details.
BEFORE CODE CHANGE
This worked fine.
View:
<button ng-disabled="!vm.canGoForward()" class="btn btn-primary" name="next" type="button" ng-click="vm.gotoStep(vm.currentStep + 1)">
Controller:
var vm = this;
vm.currentStep = 1;
vm.steps = WizardService.getWizardSteps(vm.formData);
vm.canGoForward = function() {
var res = true,
i,
nextStateIndex = vm.currentStep + 1;
if (nextStateIndex > vm.steps.length) {
return false;
}
for (i = 1; res && i <= nextStateIndex; i++) {
res = (res && vm.steps[i-1].isReady());
}
return !!res;
};
Service
var wizardService = {
getWizardSteps: getWizardSteps
};
return wizardService;
function getWizardSteps(formData) {
var wizardSteps = [
{
step: 1,
name: 'Name',
template: 'views/wizard/step1.html',
isReady: function() { return true; }
},
{
step: 2,
name: 'Email',
template: 'views/wizard/step2.html',
isReady: function() { return formData.firstName && formData.lastName; }
},
{
step: 3,
name: 'Job Category',
template: 'views/wizard/step3.html',
isReady: function() { return formData.email; }
}
];
return wizardSteps;
}
AFTER CODE CHANGE
View
Remains the same
Controller
var vm = this;
vm.currentStep = 1;
vm.steps = WizardService.getWizardSteps(vm.formData);
vm.canGoForward = WizardService.canGoForward(vm.currentStep, vm.steps);
Service
var wizardService = {
getWizardSteps: getWizardSteps,
canGoForward: canGoForward
};
return wizardService;
function getWizardSteps(formData) {
var wizardSteps = [
{
step: 1,
name: 'Name',
template: 'views/wizard/step1.html',
isReady: function() { return true; }
},
{
step: 2,
name: 'Email',
template: 'views/wizard/step2.html',
isReady: function() { return formData.firstName && formData.lastName; }
},
{
step: 3,
name: 'Job Category',
template: 'views/wizard/step3.html',
isReady: function() { return formData.email; }
}
];
return wizardSteps;
}
function canGoForward(currentStep, steps) {
console.log(steps);
var res = true,
i,
nextStateIndex = currentStep + 1;
if (nextStateIndex > steps.length) {
return false;
}
for (i = 1; res && i <= nextStateIndex; i++) {
res = (res && steps[i-1].isReady());
}
return !!res;
}
I now get the following error: TypeError: v2.canGoForward is not a function. How can I resolve it?
In your second version, the following line will actually call WizardService.canGoForward on the spot, not assign it:
vm.canGoForward = WizardService.canGoForward(vm.currentStep, vm.steps);
What gets assigned is the return value of that call, which obviously is not a function, hence the error message when a call is attempted later.
If you want to assign the function, and ensure the arguments get passed when it is called later, then use bind:
vm.canGoForward = WizardService.canGoForward.bind(WizardService, vm.currentStep, vm.steps);
I am having trouble where a property is returning undefined inside the vuejs instance, but returns the correct value in the HTML.
data: {
...
userFilter: 'all',
...
},
The alert is returning this.userFilter as undefined
filters: {
all: function(tasks) {
alert(this.userFilter); // This is undefined
if(this.userFilter == 'all'){
return tasks;
}else{
return tasks.filter(function(task){
return task.user_id == this.userFilter;
});
}
},
}
Dropdown to select user to filter by
<select class="form-control" v-if="visibility == 'all'" v-model="userFilter">
<option selected value="all">Showing all users tasks</option>
<option v-for="user in users"
value="#{{user.id}}">
#{{user.first_name}} #{{user.last_name}}
</option>
</select>
The below correctly displays the value of userFilter
#{{ userFilter }}
Entire code:
var tasks = new Vue({
el: '#tasks',
data: {
tasks: [],
users: [],
newTask: { description: '', due_at: '', user_id: '', completed: false },
editingTask: false,
showAlert: false,
loading: true,
visibility: 'active',
validation: [],
showUser: null,
authUser: {}
},
ready: function() {
this.getAuthUser();
this.getTasks();
this.getUsers();
},
computed: {
filteredTasks: function () {
return this.$options.filters[this.visibility](this.tasks);
},
remaining: function() {
return this.tasks.filter(function(task){
return !task.completed && task.user_id == this.authUser.id;
}).length;
}
},
filters: {
all: function(tasks) {
return tasks;
},
active: function(tasks) {
return tasks.filter(function(task){
return !task.completed;
});
},
completed: function(tasks) {
return tasks.filter(function(task){
return task.completed;
});
},
groupByDate: function(tasks) {
var result = {};
for (var i = 0; i < tasks.length; i++) {
var task = tasks[i];
// Convert due_at date to be used as array key
var due_at = moment(task.due_at,'YYYY-MM-DD').format('DD-MM-YYYY');
if (result[due_at]) {
result[due_at].push(task);
} else {
result[due_at] = [task];
}
}
return result;
},
newDate: function(date) {
return moment(date, 'DD-MM-YYYY').format('LL');
},
usersFilter: function(tasks, user_id) {
if(this.visibility == 'all' && user_id){
return tasks.filter(function(task){
return task.user_id == user_id;
});
}else{
return tasks;
}
}
},
methods: {
getTasks: function () {
this.loading = true;
this.$http.get('api/tasks/'+ this.visibility).success(function(tasks) {
this.$set('tasks', tasks);
this.loading = false;
}).error(function(error) {
console.log(error);
});
},
getUsers: function() {
this.$http.get('api/users/all',function(users){
this.$set('users',users);
});
},
getAuthUser: function() {
this.$http.get('api/users/current-user').success(function(authUser) {
this.$set('authUser',authUser);
});
},
toggleVisibility: function(filter) {
this.visibility = filter;
this.getTasks();
},
open: function(e) {
e.preventDefault();
$('#add-task-modal').slideDown();
},
close: function(e) {
e.preventDefault();
$('#add-task-modal').slideUp();
},
toggleAlert: function(message) {
this.showAlert = true;
$('.alert').text(message);
$('.alert').fadeIn().delay(1000).fadeOut();
this.showAlert = false;
},
addTask: function(e) {
e.preventDefault();
if ( ! this.newTask) return;
var task = this.newTask;
this.$http.post('api/tasks', task)
.success(function(data){
task.id = data.task_id;
task.due_at = moment(task.due_at,'DD-MM-YYYY').format('YYYY-MM-DD');
if(this.visibility == 'all'){
this.tasks.push(task);
}else if(this.authUser.id == task.user_id){
this.tasks.push(task);
}
$('.datepicker').datepicker('clearDates');
this.validation = [];
this.newTask = { description: '', due_at: '', user_id: '', completed: '' };
this.$options.methods.toggleAlert('Task was added.');
})
.error(function(validation){
this.$set('validation', validation);
});
},
toggleTaskCompletion: function(task) {
task.completed = ! task.completed;
this.$http.post('api/tasks/complete-task/'+ task.id, task);
},
editTask: function(task) {
if(task.completed) return;
this.editingTask = task;
},
cancelEdit: function (todo) {
this.editingTask = false;
},
updateTask: function(task) {
task.description = task.description.trim();
this.$http.patch('api/tasks/'+ task.id, task);
this.$options.methods.toggleAlert('Task was updated.');
return this.editingTask = false;
},
deleteTask: function(due_at,task) {
if(confirm('Are you sure you want to remove this task?')){
this.tasks.$remove(task);
this.$http.delete('api/tasks/'+ task.id, task);
this.$options.methods.toggleAlert('Task was removed.');
}
}
},
directives: {
'task-focus': function (value) {
if (!value) {
return;
}
var el = this.el;
Vue.nextTick(function () {
el.focus();
});
}
}
})
Try to use tasks.$data.visibility.