Vue.js async update from inside JS import - javascript

I have two form components, which have a common JS validator.
import { validateInput } from './validateInput.js'
export default {
data () {
return {
email: 'a#a.com',
validEmail: false
}
},
methods: {
validateInput
},
watch: {
'email': function (val) {
this.validEmail = validateInput('email', val)
// ^ makes async request
}
}
}
The validator uses jQuery.ajax - async HTTP request to server. The problem is that, since validateInput() is asynchronous, it returns before it gets a response from the server (this.validEmail is undefined)
How do I update the Vue instance (this.validEmail) after the async request in this JS imported function is completed?
In simple terms - how do I access the Vue instance from inside validateInput in a way that works asynchronously?
I've tried passing the Vue instance as an argument, doesn't update:
validateInput('email', val, this)
----
// validateInput.js
email() {
$.ajax({
...
success(response) {
vueInstance.validEmail = response // doesn't work
}
})
vueInstance.validEmail = false
// ^^^^ this works
}

Try this
watch: {
'email': function (val) {
validateInput('email', val,this)
}
}
and
validateInput('email', val, ref)
----
// validateInput.js
email() {
$.ajax({
...
success(response) {
ref.validEmail = response // doesn't work
}
})
// ^^^^ this works
}
Or the traditional way to deal with asynchronous calls is pass a callback that has a closure over the variable that needs edited.
watch: {
'email': function (val) {
validateInput('email', val,function(response){this.validEmail=response})
}
}
//...
validateInput('email', val, cb)
//...
success(response) {
cb(response);
}

Related

Call API in another page in nuxtjs

I would like to put my calls to my API in a separate page and not in the template page of my app. So, I create a file "customersAPI.js" and I put this code :
export function findAllCustomers () {
axios.get('http://127.0.0.1:8000/api/customers')
.then((reponse)=>{
console.log(reponse.data['hydra:member'])
return reponse.data['hydra:member']
}).catch(err=>console.log(err))
}
So I try to retrieve my data in my template page and put these data in data but It does not work because of the asynchronous thing of api call and because I don't know how to pass the data...
I do this in my template page :
data() {
return {
customer: [],
}
},
mounted() {
this.getAllCustomers();
},
getAllCustomers() {
this.customer = findAllCustomers();
}
I know it is not the good way to do this but I don't know how to do... So I need clarification about that. And, every time I go into the documentation, there are no examples with an API call outside of the part where there is the page template. Is it a good practice to want to put the api call apart? And in general calls to functions so that the code is not too long?
Thanks for help
In your case I advise you to try add async in mounted or in func.
async mounted() {
this.customers = await this.findAllCustomers();
},
------
methods: {
async getAllCustomers(){
this.customer = await findAllCustomers();
}
}
But better practice to fetch information from store:
COMPONENT
<script>
import {mapActions} from 'vuex'
export default {
data() {
return {
customer: [],
}
},
mounted() {
this.customer = this.fetchAll();//better to get via getters
},
methods() {
...mapActions('customers', ['fetchAll']),
//OR
// fetchAllCustomers(){
// this.$store.dispath('customers/fetchAll')
// }
}
}
</script>
STORE
// async action that put all customers in store
const fetchAll = async ({ commit }) => {
commit(types.SET_ERROR, '')
commit(types.TOGGLE_LOADING, true)
try {
const { data} = await customerAPI.findAll(namespace)
commit(types.SET_ALLIDS, data['hydra:member'])
commit(types.TOGGLE_LOADING, false)
return data['hydra:member']
} catch (error) {
commit(types.TOGGLE_LOADING, false)
commit(types.SET_ERROR, error)
}
},
API
// func that receive promise
export function findAll () {
return axios.get('http://127.0.0.1:8000/api/customers')
}
Please read about vuex
https://vuex.vuejs.org/guide/actions.html

Parameter value sent to but not received by action

I initiate loading scans through this action:
export function loadPickingScans (orderReference) {
return { type: SCANNING_LOAD_SCANS, orderReference };
}
It's called in my smart (page) component:
componentDidMount() {
const { loadPickingScans } = this.props;
loadPickingScans(this.props.match.params.orderReference);
}
This is the url:
enter code herehttp://localhost:3000/orders/my-order-reference/scans
this.props.match.params.orderReference correctly contains my-order-reference.
However, adding a log to my action, orderReference is received as undefined.
What should I do to receive this expected value?
Update
By request:
function mapDispatchToProps(dispatch) {
return {
loadPickingScans: () => dispatch(loadPickingScans())
};
}
In mapDispatchToProps, while dispatching the action, you haven't passed any argument to it and hence it logs undefined in the method, you need to write it like
function mapDispatchToProps(dispatch) {
return {
loadPickingScans: (value) => dispatch(loadPickingScans(value))
};
}
or simply
const mapDispatchToProps = {
loadPickingScans
}

Best way to update html after promise result

I wonder to know the best way of binding result of a promise which is as an injection to html tag using angular 2(I use ionic 2)...
As you know the main problem with async coding is loosing reference to the current object. It seems I should pass current object as a prameter to Promise function generator.
I searched internet for better solution but nothing I found! So is there any better approch?
Ionic 2 itself use observation and subscribe to do async proccess. But the major problem is that for existing functions which are not observable it couldn't work!
My approch:
Injectable class:
export class PromiseComponent {
doPromise = function (obj: any) {
return new Promise(function (resolve2) {
setTimeout(function () {
resolve2({ num: 3113, obj: obj });
}, 5000);
});
}
}
Call on click:
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc);//UPDATED HERE
}
//UPDATED HERE
secondFunc = function (res) {
this.promiseVal = res.num
}
Html:
<div>{{promiseVal}} </div>
<button (click)="doMyPromise()">Do Promise</button>
If you want to consume a promise inside your component:
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise().then((res) => {
this.promiseVal = res.num
});
}
And I don't know the reasoning behind your Service but it usually is like this (optional):
export class PromiseComponent {
doPromise() { //This method will return a promise
return new Promise(function (resolve2) {
setTimeout(function () {
resolve2({ num: 3113, obj: obj });
}, 5000);
});
}
}
After OP edited the post:
You can change this:
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc);//UPDATED HERE
}
to
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc.bind(this));//UPDATED HERE
}
As you know the main problem with async coding is losing reference to the current object
That's not true, the arrow function does not bind its own this therefore you don't need to send this to doPromise
export class PromiseComponent {
doPromise () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve({ num: 3113 })
}, 5000)
})
}
}
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise()
.then(res => {
this.promiseVal = res.num
})
}

How do I chain Intern Page Object function calls?

Following the Intern user guide, I wrote a simple page object:
define(function(require) {
function ListPage(remote) {
this.remote = remote;
}
ListPage.prototype = {
constructor: ListPage,
doSomething: function(value) {
return this.remote
.get(require.toUrl('http://localhost:5000/index.html'))
.findByCssSelector("[data-tag-test-id='element-of-interest']")
.click().end();
}
};
return ListPage;
});
In the test, I want to call doSomething twice in a row, like this:
define(function(require) {
var registerSuite = require('intern!object');
var ListPage = require('../support/pages/ListPage');
registerSuite(function() {
var listPage;
return {
name: 'test suite name',
setup: function() {
listPage = new ListPage(this.remote);
},
beforeEach: function() {
return listPage
.doSomething('Value 1')
.doSomething('Value 2');
},
'test function': function() {
// ...
}
};
});
});
However, when I run the test, I get this error:
TypeError: listPage.doSomething(...).doSomething is not a function
I tried some approaches described in this question, to no avail.
A better way to implement page objects with Intern is as helper functions rather than Command wrappers. Groups of related helper functions can then be used to create Page Object modules.
// A helper function can take config parameters and returns a function
// that will be used as a Command chain `then` callback.
function doSomething(value) {
return function () {
return this.parent
.findByCssSelector('whatever')
.click()
}
}
// ...
registerSuite(function () {
name: 'test suite',
'test function': function () {
return this.remote.get('page')
// In a Command chain, a call to the helper is the argument
// to a `then`
.then(doSomething('value 1'))
.then(doSomething('value 2'));
}
}

Generate Routes in .run() with config from service Angularjs

I am building routes/states and the menu based on what the user is authorized to see. I've looked around and tried a few different things, but i'm hitting a brick wall. The SessionService object in the RoleService Factory is empty whenever RoleService.validateRole() is called. No route is added and the app is effectively dead. Why is the injected factory empty and the methods undefined.
Here is a simplified layout of the app starting in order of dependencies.
In app.run(), I am adding the states to the app instead of doing it in the config.
$stateProviderRef.state(value.stateName, state);
The states come from (a factory) AppConfig.getStates(), which returns an array.
var states = AppConfig.getStates();
In getStates() we validate each route's role.
if(RoleService.validateRole(routes[i].role))
The RoleService depends on the SessionService and the validateRole function does this check:
if(SessionService.currentUser.role === role)
The SessionService depends on the AuthenticationService which is just a factory that returns a promise using $http (the user object). The SessionService.currentUser is a function that .then()s the returned promise from the AuthenticationService.
return {
currentUser: function(){
AuthenticationService.then(function(result){
return result;
});
}
};
I'm not sure of a better way to explain the code without including the entire files.
Based on the plunker (mentioned in comment), I updated/cloned it to another, which is working
I. simple - when static data are returned (no $http)
Because the service SessonService was defined like this:
return {
currentUser: function() {
...
we cannot call it as a property:
...
return {
validateRoleAdmin: function () {
if (SessionService.currentUser.role === 'admin') {
...
},
validateRole: function (role) {
if(SessionService.currentUser.role === role){
...
it is a function it must be called as a function currentUser():
return {
validateRoleAdmin: function () {
if (SessionService.currentUser().role === 'admin') {
...
},
validateRole: function (role) {
if(SessionService.currentUser().role === role){
...
II. waiting for async calls
The adjusted example
Next, if we in example create a static result of the service AuthenticationService:
angular.module('daedalus').factory('AuthenticationService',
function() {
return {"idsid": "ad_jdschuma","role": "user","id": "33333"}
}
)
we cannot expect there will be some then method:
currentUser: function() {
//AuthenticationService.then(function(result) {
// return result;
//});
return AuthenticationService;
}
And to make it really async we can replace it with this:
angular.module('daedalus').factory('AuthenticationService',
['$timeout', function($timeout) {
return {
getData: function() {
return $timeout(function() {
return {
"idsid": "ad_jdschuma",
"role": "user",
"id": "33333"
}
})
}
};
}])
And then use even the .then() - Session service:
angular.module('daedalus').factory('SessionService', ['AuthenticationService',
function(AuthenticationService) {
return {
currentUser: function(){
return AuthenticationService
.getData()
.then(function(result){
return result;
});
}
};
}]
)
And the RoleService:
return {
...
validateRole: function(route) {
console.log('SessionService currentUser: ' + JSON.stringify(SessionService))
return SessionService
.currentUser()
.then(function(userRole) {
if (userRole.role === route.role) {
return route;
} else {
return null;
}
})
}
And with this in place in appConfig
getStates: function(){
var items = [];
var deffered = $q.defer();
var validatedCount = routes.length;
for(var i=0,len=routes.length; i<len; i++){
var route = routes[i];
RoleService
.validateRole(route)
.then(function(route){
if(route) {
items.push(route.stateConfig)
}
if(--validatedCount === 0 ){ // all processed
deffered.resolve(items)
}
})
}
return deffered.promise;
}
We can do that in run:
AppConfig
.getStates()
.then(function(states) {console.log(states)
angular.forEach(states, function(value, key) {
var state = {
"url": value.url,
"templateUrl": value.templateUrl,
"controller": value.controller
};
$stateProviderRef.state(value.stateName, state);
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.sync();
});
$urlRouter.listen();
Check it here
The concept of the second solution (async) is too .thenified(). I just intended to show that all is working. Better approach how to get security data is completely covered here:
Confusing $locationChangeSuccess and $stateChangeStart

Categories

Resources