What is Vue way to access to data from methods? - javascript

I have the following code:
{
data: function () {
return {
questions: [],
sendButtonDisable: false,
}
},
methods: {
postQuestionsContent: function () {
var that = this;
that.sendButtonDisable = true;
},
},
},
I need to change sendButtonDisable to true when postQuestionsContent() is called. I found only one way to do this; with var that = this;.
Is there a better solution?

Inside methods if you don't have another scope defined inside, you can access your data like that:
this.sendButtonDisable = true;
but if you have a scope inside the function then in vue is a common usage of a variable called vm (stands for view model) at the beginning of the function, and then just use it everywhere like:
vm.sendButtonDisable = false;
An example of vm can be seen in the Vue official documentation as well.
complete example:
data: function () {
return {
questions: [],
sendButtonDisable : false
}
},
methods: {
postQuestionsContent : function() {
// This works here.
this.sendButtonDisable = true;
// The view model.
var vm = this;
setTimeout(function() {
// This does not work, you need the outside context view model.
//this.sendButtonDisable = true;
// This works, since wm refers to your view model.
vm.sendButtonDisable = true;
}, 1000);
}
}

It depends on how you call your postQuestionsContent method (if you call it asynchronously, you might need to bind the this context).
In most cases, you should be able to access it using this.$data.YOURPROPNAME, in your case this.$data.sendButtonDisable:
data: function () {
return {
questions: [],
sendButtonDisable : false
}
},
methods:
{
postQuestionsContent : function()
{
this.$data.sendButtonDisable = true;
}
}

Try this instead
...
methods:
{
postQuestionsContent ()
{
this.sendButtonDisable = true;
}
}
Registering your methods in the above manner should resolve the issue.

I tried both this.$data.sendButtonDisable and vm.sendButtonDisable and did not work for me.
But I got it working with outer_this = this, something like:
methods: {
sendForm(){
var outer_this;
outer_this = this;
$.ajax({
url: "email.php",
type: "POST",
dataType: "text",
data: {
abc: "..."
},
success: function(res){
if(res){
//...
outer_this.sendButtonDisable = false;
}else{
//...
}
}
});
}
},

Related

Global loaded data in VueJs is occasionally null

I'm new to VueJs and currently trying to load some data only once and make it globally available to all vue components. What would be the best way to achieve this?
I'm a little bit stuck because the global variables occasionally seem to become null and I can't figure out why.
In my main.js I make three global Vue instance variables:
let globalData = new Vue({
data: {
$serviceDiscoveryUrl: 'http://localhost:40000/api/v1',
$serviceCollection: null,
$clientConfiguration: null
}
});
Vue.mixin({
computed: {
$serviceDiscoveryUrl: {
get: function () { return globalData.$data.$serviceDiscoveryUrl },
set: function (newUrl) { globalData.$data.$serviceDiscoveryUrl = newUrl; }
},
$serviceCollection: {
get: function () { return globalData.$data.$serviceCollection },
set: function (newCollection) { globalData.$data.$serviceCollection = newCollection; }
},
$clientConfiguration: {
get: function () { return globalData.$data.$clientConfiguration },
set: function (newConfiguration) { globalData.$data.$clientConfiguration = newConfiguration; }
}
}
})
and in my App.vue component I load all the data:
<script>
export default {
name: 'app',
data: function () {
return {
isLoading: true,
isError: false
};
},
methods: {
loadAllData: function () {
this.$axios.get(this.$serviceDiscoveryUrl)
.then(
response => {
this.$serviceCollection = response.data;
let configurationService = this.$serviceCollection.services.find(obj => obj.key == "ProcessConfigurationService");
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
}
);
this.isLoading = false;
})
}
},
created: function m() {
this.loadAllData();
}
}
</script>
But when I try to access the $clientConfiguration it seems to be null from time to time and I can't figure out why. For example when I try to build the navigation sidebar:
beforeMount: function () {
let $ = JQuery;
let clients = [];
if (this.$clientConfiguration === null)
console.error("client config is <null>");
$.each(this.$clientConfiguration, function (key, clientValue) {
let processes = [];
$.each(clientValue.processConfigurations, function (k, processValue) {
processes.push(
{
name: processValue.name,
url: '/process/' + processValue.id,
icon: 'fal fa-project-diagram'
});
});
clients.push(
{
name: clientValue.name,
url: '/client/' + clientValue.id,
icon: 'fal fa-building',
children: processes
});
});
this.nav.find(obj => obj.name == 'Processes').children = clients;
The most likely cause is that the null is just the initial value. Loading the data is asynchronous so you'll need to wait for loading to finish before trying to create any components that rely on that data.
You have an isLoading flag, which I would guess is your attempt to wait for loading to complete before showing any components (maybe via a suitable v-if). However, it currently only waits for the first request and not the second. So this:
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
}
);
this.isLoading = false;
would need to be:
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
this.isLoading = false;
}
);
If it isn't that initial value that's the problem then you need to figure out what is setting it to null. That should be prety easy, just put a debugger statement in your setter:
$clientConfiguration: {
get: function () { return globalData.$data.$clientConfiguration },
set: function (newConfiguration) {
if (!newConfiguration) {
debugger;
}
globalData.$data.$clientConfiguration = newConfiguration;
}
}
Beyond the problem with the null, if you're using Vue 2.6+ I would suggest taking a look at Vue.observable, which is a simpler way of creating a reactive object than creating a new Vue instance.
Personally I would probably implement all of this by putting a reactive object on Vue.prototype rather than using a global mixin. That assumes that you even need the object to be reactive, if you don't then this is all somewhat more complicated than it needs to be.

Calling a private/nested javascript function from an outer scope

I have my javascript code like this . Inside that I have an init() function and in that function I have an options JSON object and in that object I have a function defined as objectselected(). How I call that function in a button click event
I have tried like this WorkFlow.init().options.Objectselected() but it is not working,
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
elementId: "playAreaContainer",
TextStoreList: ['One', 'Two', 'Three'],
LinkTextStoreList: $('#drpLinkType option').map(function () {
return this.text;
}).get(),
shapeList: ['RoundedRectangle', 'Circle', 'Rectangle', 'Ellipse', 'Square', 'Diamond', 'Card', 'Database'],
diagramUpdate: function (e) {
},
objectSelected: function (e) {
},
linkUpdate: function (e) {
},
initialize: function () {
}
myGraph = new Graph(options);
options.initialize();
},
}
How to call that function.
One way around is you can return options and than call it.
init: function () {
var options = {
...your code..}
return options;
},
and call it than
var options = WorkFlow.init();
options.Objectselected();
As it stands, you have no access to options because it's a local variable - that is, local to its scope.
To access its contents, you'll need to return it from init().
Think about it:
WorkFlow.init()
Currently this returns undefined, because your init() returns nothing. You're trying to chain like in jQuery, but that relies on the API always returning the instance. Your path finds a dead-end at init().
To fix this, have init() return options - or at least the part of it you want to access from outside - an "export".
So (basic example)
init: function() {
var options {
my_func: function() { }, //<-- we want outside access to this
private: 'blah' //<-- this can stay private - leave it out of the export
}
//return an export, exposing only what we need to
return {
my_func: options.my_func
}
}
You need to return options as it is inside init function's scope
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
elementId: "playAreaContainer",
TextStoreList: ['One', 'Two', 'Three'],
LinkTextStoreList: $('#drpLinkType option').map(function () {
return this.text;
}).get(),
shapeList: ['RoundedRectangle', 'Circle', 'Rectangle', 'Ellipse', 'Square', 'Diamond', 'Card', 'Database'],
diagramUpdate: function (e) {
},
objectSelected: function (e) {
},
linkUpdate: function (e) {
},
initialize: function () {
}
myGraph = new Graph(options);
options.initialize();
return options;
},
}
And call it as WorkFlow.init().objectSelected();
Building on Patrick's comment, you'd need to return options from the init function:
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
...
options.initialize();
return options;
},
}

angular-ui-router function inside controller is not a function

I created a controller inside a state. We usually use this kind of notation for our angular (1.5) components and services with an angular.extend(self, {}).
My problem here is when self.criteria is being initialized, the browser call self.getAgencies() and return an exception :
Error: self.getAgencies is not a function
(function (app) {
'use strict';
app.config(function ($stateProvider) {
$stateProvider.state('app.invoice', {
url: '/invoice'
abstract: true,
template: '<ui-view></ui-view>'
})
.state('app.invoice.list', {
url: '/list?allMyParam',
template: '<invoices criteria="$ctrl.criteria"></invoices>',
controllerAs: '$ctrl',
controller: function ($location) {
var self = this;
angular.extend(self,{
criteria: {
agencies: self.getAgencies()
},
getAgencies: function () {
if ($location.search().agencies) {
return undefined;
} else {
return ['foo', 'blah'];
}
}
});
}
});
});
})(angular.module('module', []));
I put getAgencies() function over the criteria prototype initialization but it did not change anything.
I got out of it by moving getAgencies() outside of angular.extend(self, {}) like this :
var self = this;
var getAgencies = function () {
if ($location.search().agencies) {
return undefined;
} else {
return ['foo', 'blah'];
}
}
angular.extend(self, {
criteria: {
agencies: getAgencies()
}
});
My code is working so it is ok for me but I would like to understand why my self.getAgencies() is not working when this call inside a controller component works well, and make it better if I can.
I'm using angular-ui-router 0.2.18 with angular 1.5.0.
Thank you for your help.
Because when this code is reached
criteria: {
agencies: self.getAgencies()
},
the angular.extend function has not been called yet, and there is no reason why self should contain the getAgencies function.
Why not initialize the agencies afterwards?
angular.extend(self,{
criteria: { },
getAgencies: function () {
if ($location.search().agencies) {
return undefined;
} else {
return ['foo', 'blah'];
}
}
});
self.criteria.agencies = self.getAgencies();
Alternatively, you could use a getter and post-pone calling the function:
angular.extend(self,{
criteria: {
get agencies() {
if (!self._agencies) {
self._agencies = self.getAgencies();
}
return self._agencies;
}
},
getAgencies: ...
});

Set state after observe()

What's the best way to set state based on the data received from observe()?
It seems setting state via componentWillMount() won't work as observe() runs after this and the data isn't available to set state.
I'm using the observe function as advised when using Parse
E.g.:
var DragApp = React.createClass({
getInitialState: function () {
return {
activeCollection : ''
};
},
observe: function() {
return {
collections: (collectionsQuery.equalTo("createdBy", currentUser))
};
},
_setactiveCollection: function(collection) {
this.setState({
activeCollection : collection
});
},
componentWillMount: function () {
var collection = this.data.collections[0];
this._setActiveCollection(collection);
},
)}
I went the wrong way about this.
I shouldn't be storing this.data into state. I can pass it into components via render.
To get round this.data not being ready before rendering, I make use of the ParseReact function pendingQueries() inside render. E.g.
if (this.pendingQueries().length > 0) {
content = 'loading...'
} else {
content = 'hello world I am' + this.data.name
}
Try:
var DragApp = React.createClass({
observe: function() {
var collections = collectionsQuery.equalTo("createdBy", currentUser);
return {
collections: collections,
activeCollection: collections[0]
};
},
render: function () {
// do something with this.data.collections and/or this.data.activeCollection
},
)}

Set a knockout.js observable without subscriptions firing

How can I set an observable property without any subscriptions firing for it?
I have a scenario were the page loads, an ajax call is made to get some data, the data is looped over and the currently selected item is then set to an observable. I want to be able to set this observable without any subscriptions for it firing because the first time this observable is set is considered its initial sate and the subscriptions should not execute on initial state.
function PlanViewModel() {
var self = this;
self.plans = ko.observableArray();
self.selectedItem = ko.observable();
self.getAllPlans = function () {
$.ajax({
url: "/Backoffice/Home/GetAllPlans",
type: "POST",
data: {},
context: this,
success: function (result) {
var planList = this.plans;
// clear the plan list
planList.removeAll();
$.each(result.plans, function () {
var planDetail = new PlanDetail(this, self);
if (this.IsSelected) {
self.selectedItem(planDetail); // how do I set this without the subscriptions firing?
}
planList.push(planDetail);
});
},
error: function (result) {
alert("An error occured getting plans.");
}
});
}
self.selectedItem.subscribe(function (newItem) {
newItem.repositoryUpdateSelectedPlan();
} .bind(self));
}
You could restructure your code like this:
function PlanViewModel() {
var self = this;
self.plans = ko.observableArray();
self.getAllPlans = function () {
$.ajax({
// …
success: function (result) {
// …
$.each(result.plans, function () {
var planDetail = new PlanDetail(this, self);
if (this.IsSelected) {
self.selectedItem = ko.observable(planDetail);
}
planList.push(planDetail);
});
if (self.selectedItem === undefined) {
self.selectedItem = ko.observable();
}
self.selectedItem.subscribe(function (newItem) {
newItem.repositoryUpdateSelectedPlan();
}.bind(self));
},
// …
});
}
}
That is, only start Knockout after your desired initial state is achieved.
Thanks, I went down that route and its working with some modifications. The selectedItem observable must be defined on the model from the get go because its used in bindings all over the place but I did move the subscription portion like you've suggested and that's working out great.
function PlanViewModel() {
var self = this;
var selectedItemSubscription = null;
self.plans = ko.observableArray();
self.selectedItem = ko.observable();
self.getAllPlans = function () {
$.ajax({
url: "/Backoffice/Home/GetAllPlans",
type: "POST",
data: {},
context: this,
success: function (result) {
var planList = this.plans;
// clear the plan list
planList.removeAll();
$.each(result.plans, function () {
var planDetail = new PlanDetail(this, self);
if (this.IsSelected) {
if (selectedItemSubscription != null)
selectedItemSubscription.dispose();
self.selectedItem(planDetail);
}
planList.push(planDetail);
});
selectedItemSubscription = self.selectedItem.subscribe(function (newItem) {
newItem.repositoryUpdateSelectedPlan();
}.bind(self));
},
error: function (result) {
alert("An error occured getting plans.");
}
});
}
}

Categories