I have a Vue block that I need to bind to a boolean property:
<div class="row" v-if.sync="isThisAllowed">
To calculate that property I need to make an API call using Axios, which has to be asynchronous. I've written the necessary code to get the value:
public async checkAllowed(): Promise<boolean> {
var allowed = false;
await Axios.get(`/api/isItAllowed`).then((result) => {
var myObjects = <MyObject[]>result.data.results;
myObjects.forEach(function (object) {
if (object.isAllowed == true) {
hasValuedGia = true;
}
})
});
return allowed;
}
What I did then - I'm not very experienced with Vue - is to add a property to the Vue model and assign a value to it in created:
public isThisAllowed: boolean = false;
async created() {
this.checkAllowed().then(result => {
this.isThisAllowed = result;
});
}
This works in the sense that the value I'm expecting is assigned to the property. But Vue doesn't like it and complains
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.
Most of the values on the model are exposed via getters:
get isSomethingElseAllowed(): boolean {
return this.productCode === ProductTypes.AcmeWidgets;
}
But I need to "await" the value of the async function, which would mean making the getter async which then, of course, makes it a Promise and I can't bind that to my model?
What's the right way to go about this?
You can't define a property that way, instead define isThisAllowed in the data object
as
data: function(){
return {
isThisAllowed: false
}
}
And make checkAllowed into a normal function and set this.isThisAllowed = allowed inside it
Related
For an Ember app, is it possible to use a constant as part of a computed property key ?
So, essentially, I have a constant as below;
MY_SECTION {
MY_FIELD: "my-field-id"
}
What I want is a computed property on "my-field-id" i.e.
myCP: function() {
console.log('Inside CP...');
}.property('section.field.my-field-id.value')
However, I want to be able to use constant for my-field-id instead of using it directly. Is that possible ?
Ola #testndtv, thanks for your question! Yes it is entirely possible to use a constant in the key for a computed property, but to make use of it you will need to use the more modern syntax that #jelhan was mentioning because .poperty() is deprecated.
Here is a working example of a controller that I have tested locally and is working as you would expect:
import Controller from '#ember/controller';
import { defineProperty, computed } from '#ember/object';
const PROPERTY_ID = 'some-random-string-that-is-too-long-to-write';
export default Controller.extend({
// this is just for the example so we can show the value in the template
// it is not needed to get this to work
PROPERTY_ID: PROPERTY_ID,
init() {
this._super(...arguments);
defineProperty(this, 'myCP', computed(PROPERTY_ID, function() {
return this.get(PROPERTY_ID);
}));
},
actions: {
addOne() {
// this is just for the example to stop the result always being NaN because
// null + 1 = NaN
let value = this.get(PROPERTY_ID) || 0;
this.set(PROPERTY_ID, value + 1);
}
}
});
As you can see we are making use of defineProperty which is being imported from '#ember/object'. You can read more about it in the API documentation
The key insight here is that you need to define the property dynamically in the init() for this Ember object.
The corresponding template for this Controller is as follows:
Property ID is: {{PROPERTY_ID}}
<br>
And the value is: {{get this PROPERTY_ID}}
<br>
<button {{action 'addOne'}}>Add One</button>
With JavaScript, when creating a class, while instantiating that class, I want to populate a public property. I can do this using a setter, however, the value I want comes from an external website which I retrieve via an ajax get call. The issue becomes that the new class object does not have the appropriate property value when I create it.
Here's some sample code:
class MyTestClass {
constructor() {
this._ipaddress = "";
}
get ip() {
return this._ipaddress;
}
set ip(value) {
createIpAddr();
}
createIpAddr() {
var myIpAddr = "";
var strUrl = "https://api.ipify.org/?format=json";
$.ajax({
url: strUrl,
success: function(data) {
this._ip = data.ip;
},
//async:false //Performing this in sync mode to make sure I get an IP before the rest of the page loads.
});
return myIpAddr;
}
}
var testobj = new MyTestClass();
console.log(testobj.ip);
The problem here is that I can't be sure the IP will be populated in time to use after creating the new instance of the class. I've tried promises and deffered, but they have the same problem, I can't populate the variable before I need it. I'm trying to adjust the way I am looking at this and adding callbacks, but the issue is that I need the correct value in the class before I can use the class for the next call, where I am passing this object to it.
Is there a simple solution I am over looking? I have been through a million of these threads about async: false, and I don't want to start a new one, but what is a better choice in this case?
I want to set a class property from an ajax response when instantiating the class object.
You could have your constructor return an async IIFE, allowing you to then await the creation of a new class instance.
That would look something like this:
class MyTestClassAsync {
constructor() {
return (async() => {
this._ip = (await this.createIpAddrAsync()).ip;
return this;
})();
}
get ip() {
return this._ip;
}
set ip(value) {
this._ip = value;
}
createIpAddrAsync = () => $.get("https://api.ipify.org/?format=json");
}
async function Main() {
var testobj = await new MyTestClassAsync();
console.log(testobj.ip);
}
Main();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I've made the personal decision to append "Async" to the method and class names, just so that it's clear they need to be awaited.
Let's say I have a variable called isVisible. And I have a method called
ReverseVariable(variable: boolean)
{
variable = !variable;
}
I want to call this method from a template like
<button (click)="ReverseVariable(isVisible)"></button>
I want to give it isVisible in the parameters and have isVisible reverse itself. Something like the following example is not an option
ReverseVisibility()
{
this.isVisible = !this.isVisible;
}
Is there any way that I can pass the variable by reference?
Not with a primitive data type like a boolean. What you could do is make a non-primitive like an object
isVisible = {
flag: true
}
Then toggle that in your function
ReverseVisibility(isVisible)
{
isVisible.flag = !isVisible.flag;
}
Here is plnkr demonstrating this (https://plnkr.co/edit/VYEimNoHZvGxeE4S2W4L?p=preview)
So I've got a service call going out to the back end and saving an object, the call has a promise set up to return a number.
That call looks like this
saveTcTemplate(item: ITermsConditionsTemplate): ng.IPromise<number> {
item.modifiedDate = new Date();
return this.$http.post(this.api + '/SaveTcTemplate', item)
.then(this.returnData);
}
returnData = (response: any) => {
return response.data;
};
This is when creating a new object, all the fields are set to their needed values, passed to save in, then called to be displayed.
This is the get function used to pull the object after it's been saved.
getTermsConditions(id: number): ng.IPromise<ITermsConditionsTemplate> {
return this.$http.get(this.api + '/GetTermsConditions',
{
params: {
id: id
}
}).then(this.returnData);
}
This is the initial construction, saving, and getting, of the object
this.newTemplate.copyId = 0;
this.newTemplate.id = 0;
this.newTemplate.isLibrary = true;
this.newTemplate.studyFacilityScheduleId = this.studyFacilityScheduleId;
this.studyTermsConditionsService.saveTcTemplate(this.newTemplate)
.then(this.studyTermsConditionsService.getTermsConditions)
.then(this.setTemplateData);
When set up like this I can successfully save a new item, and have its id (the ng.IPromise part) returned to me, and passed into my Get service call.
The problem is, when set up this way, this.$http.get comes back undefined. From what I think I understand from other stack overflow issues that are similar, it is happening because I called the function without explicitly passing anything into it's parameter when I say
.then(this.studyTermsConditionsService.getTermsConditions)
To test this I also set up the save and get like this
var data: any;
data = this.studyTermsConditionsService.saveTcTemplate(this.newTemplate);
this.studyTermsConditionsService.getTermsConditions(data)
.then(this.setTemplateData);
And that worked, sort of. $http.Get was recognized, and could be used, but the problem with this setup was; due to the asynchronous nature of ng.IPromise the data isn't being sent to my Get function as the promised number value. It's being sent as type $$promise which results in the parameter in my get function being NaN.
So one way I'm passing a usable value, i.e. 32, but I'm not explicitly passing this value, so $http.get is undefined.
The other way, I am explicitly passing a parameter, so $http.get is recognized, and usable, but the explicitly called parameter is of type $$promise, not type number, It seems, unfortunately, the ng.IPromise< number > is not resolved by time I call my Get function.
How do I proceed from here??
I guess you are messed up with this. As you directly pass the function reference as parameter it lost with the context. You should call them explicitly like below.
Code
this.newTemplate.copyId = 0;
this.newTemplate.id = 0;
this.newTemplate.isLibrary = true;
this.newTemplate.studyFacilityScheduleId = this.studyFacilityScheduleId;
this.studyTermsConditionsService.saveTcTemplate((response) => this.newTemplate(response))
.then((response) => this.studyTermsConditionsService.getTermsConditions(response))
.then((response) => this.setTemplateData(response));
Otherwise you could make your function to use of arrow function which would help to available this context inside function.
saveTcTemplate(item: ITermsConditionsTemplate): ng.IPromise<number> { .. }
should be
saveTcTemplate = (item: ITermsConditionsTemplate): ng.IPromise<number> => { .. }
And
getTermsConditions(id: number): ng.IPromise<ITermsConditionsTemplate> { .. }
should be
getTermsConditions = (id: number): ng.IPromise<ITermsConditionsTemplate> => { .. }
So i have this ember object "lineup", when i use
lineup.get('stations').length = 396
but it is wrong, if i inspect that object, and use _data
lineup._data.stations.length = 429
it is the correct one,
Lineup stations is got from ember-data request payload manipulation like this
if (payload.stations) {
payload.stations = payload.stations.map(function(s) {
s.logo = s.logoFilename ? 'http://cdn.tvpassport.com/image/station/100x100/'+s.logoFilename : null;
delete s.logoFilename;
return Ember.Object.create(s);
});
}
any ideas?
Ember.get will sometimes return an ObjectProxy or and ArrayProxy. Sometimes in ember-data you could also get an PromiseObject or and PromiseArray.
If you are in a computed property you can access the data directly, but never use normal JS-dotnotation after a .get! Always do obj.get('full.path.to.prop') and never obj.get('full').get('path').get('to').get('prop') or obj.get('full').path.to.prop.
If you are not in a computed property (for example, you are in an action) use .then on a PromiseObject or PromiseArray.
So for a computed property:
len: Ember.computed('lineup', {
get() {
return this.get('lineup.stations.length');
}
})
And for an action:
alertLen() {
this.get('lineup.stations').then(stations => alert(stations.get('length'));
}