Async call for computed property - Vue.js - ajax

I have a computed property that will only be used if a match for a property exists. Because of this, I'm making the call to get the data asynchronous so that it's only retrieved when needed. I'm having an issue though trying to make an async call to return data for a computed property.
Below is what I have:
new Vue({
el: "#formCompleteContainer",
data: {
form: {},
components: []
},
computed: {
employeeList: function () {
var self = this;
if (_.some(this.components, function (component) {
return component.ComponentInfo.Type === 8
})) {
var employees = [];
$.ajax({
url: "/Form/GetAllUsers",
type: "GET"
}).done(function (results) {
employees = results;
});
return employees;
} else {
return [];
}
}
}
});
I know this isn't working because I'm returning before the call is complete. I've seen how to use deferredobjects and what not but I can't seem to figure out how to implement it with Vue.

For your use case, I don't think computed property can implement the goal.
My solution:
create one data property as one 'defered' object,
then uses one watch to async call your backend to get new data, finally assign to the defered object
like below demo:
Vue.config.productionTip = false
app = new Vue({
el: "#app",
data: {
product: "Boots",
deferedProduct: ''
},
watch: {
product: function (newVal, oldVal) {
setTimeout(() => {
this.deferedProduct = 'Cats in ' + newVal + '!'
}, 1500)
}
},
methods: {
nextProduct: function () {
this.product += 'a'
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<button #click="nextProduct()">Click Me!</button>
<h2>{{product}}</h2>
<h2>{{deferedProduct}}</h2>
</div>

This is what vue-async-computed is meant for. It resolves the promise you returned and handles any race conditions.
new Vue({
el: "#formCompleteContainer",
data: {
form: {},
components: []
},
asyncComputed: {
employeeList: function () {
if (_.some(this.components, function (component) {
return component.ComponentInfo.Type === 8
})) {
return $.ajax({
url: "/Form/GetAllUsers",
type: "GET"
});
} else {
return Promise.resolve([]);
}
}
}
});

After doing some more research I have gone another route. I agree with Sphinx that I don't think what I am trying to achieve will work with a computed property.
Instead, this is what I am going with:
new Vue({
el: "#formCompleteContainer",
data: {
form: {},
components: [],
employees: []
},
methods: {
getEmployees: function () {
var self = this;
if (_.some(this.components, function (component) {
return component.ComponentInfo.Type === 8;
})) {
$.ajax({
url: "/Form/Form/GetAllUsers",
type: "GET"
}).done(function (results) {
self.employees = results;
});
}
}
},
created: function () {
this.form = pageModel.Form;
this.components = pageModel.Components;
},
mounted: function () {
this.getEmployees();
}
});

As pointed out already, mounted and other 3rd party solutions can work.
However, better readability and component loading will come from putting the desired Promise within a data property. And then using the Vue lifecycle hook created, we can wait for that Promise to resolve with a .then.
For example:
requestService.js:
...
async foo(){
let myRequest = someRequest.createInstance()
await myRequest.onReady()
return myRequest.getSomePromise()
}
...
And then import the service into your component, as well as declaring a data prop:
myComponent.vue
...
data: (){
myPromiseLoc: null,
}
...
created: (){
requestService.foo().then( result =>
{
this.myPromiseLoc = result
}
}
...

Related

Vue js: Is it posible to initialize data through a method?

Basically I want to initialize my vValidNombre field on my form by comparing two values, so It would be nice to use a method, something like this:
<script type="text/javascript">
var avatar = new Vue({
el: '#validaciones',
data: {
vNombre: $('input[name=nombre]').val(),
vValidNombre: validar(),
},
methods: {
validar: function(){
if ('true' == 'true') {
return = true;
}
else {
return false;
}
}
}
})
</script>
This code doesn't work, but is it possible to do something like that?
EDIT: I'm using Vue 2
Not really. When it is initialised, vValidNombre would be undefined. However, you can do something like this with the ready method:
var avatar = new Vue({
el: '#validaciones',
data: {
vNombre: $('input[name=nombre]').val(),
vValidNombre: null,
},
ready: function() {
this.vValidNombre = this.validar();
}
methods: {
validar: function(){
// do something here
// and return it
},
bindDom: function() {
}
},
})

Vue.js 2.0 - Passing arguments in methods AJAX Axios

I need to pass an arguments in methods using ajax axios.
var app = new Vue({
el: '#app',
data: {
urlAdmission:
admissions: [
{ name : 'asdf'},
{ name : 'sd'}
]
},
mounted: function(){
this.allAdmissions()
},
methods: {
allAdmissions: _.debounce( function(){
var app = this
axios.get('http://localhost/school/api/hello')
.then( function(response ){
app.admissions = response.data.admissions
})
.catch( function(error){
console.log(error)
})
})
}
});
As you can see in mounted I call the methods this.allAdmissions() I need to pass an argument so that I can reuse the function. For example this.allAdmissions('http://localhost/school/api/hello'). Then use it in axios.get('url'). Thanks
It looks like what you're trying to do is make a function that can accept a url and bind the results of the url to a variable value in your data. Here is how you might do that.
methods: {
allAdmissions: _.debounce(function(url, value){
axios.get(url)
.then(function(response){
this[value] = response.data.admissions
}.bind(this))
.catch(function(error){
console.log(error)
})
})
}
Then, if you call that method like this,
this.allAdmissions('http://localhost/school/api/admissions‌​', "admissions")
allAdmissions will set the admissions property on your data to the result of your call. This works if you always want to use response.data.admissions because you hardcoded that. If you wanted that to be variable as well, you might pass in a third value like so
methods: {
getSomeData: _.debounce(function(url, value, responseValue){
axios.get(url)
.then(function(response){
this[value] = response.data[responseValue]
}.bind(this))
.catch(function(error){
console.log(error)
})
})
}
In case some will need multiple ajax request. Here is an example.
var app = new Vue({
el: '#app',
data: {
value: '',
admissions: [],
schoolyear: []
},
created: function(){
this.ajaxAll()
},
methods: {
ajaxAll: _.debounce( function(){
var app = this
var admissions = 'admissions'
var schoolYear = 'schoolyear'
axios.all([this.getAllData('http://localhost/school/api/admissions', 'admissions'), this.getAllData('http://localhost/school/api/schoolyear', 'schoolyear')]);
}),
getAllData: function(url, value){
var app = this
return axios.get(url)
.then(function(response){
app[value] = response.data[value]
console.log(response.data.admissions)
})
}
}
})
Credit to #Bert Evans.

Iterate inside the sequlize result set

I need to iterate over result set from the sequelize result. I have a code that works, but I think something is wrong with it, and it should not be done this way.
I have a feeling this is a blocking code.
This is the code that works:
models.Project.findAll({
where: {ProjectId: projectId}
})
.then(function (projects) {
//Iteration is here
var projectList = [];
projects.forEach(function (res) {
projectList.push(res.dataValues.PartId);
});
//then bulk lookup for the result
models.Customers.findAll({
where: {'id': {in: [projectList]}}
}).then(function (customers) {
reply(customers).code(200);
}, function (rejectedPromiseError) {
reply(rejectedPromiseError).code(401);
});
//reply(sameparts).code(200);
}, function (rejectedPromiseError) {
reply(rejectedPromiseError).code(401);
});
This iteration parts is done:
projects.forEach(function (res) {
projectList.push(res.dataValues.PartId);
});
And then this code is executed as another query:
models.Customers.findAll({
where: {'id': {in: [projectList]}}
}).then(function (customers) {
reply(customers).code(200);
}, function (rejectedPromiseError) {
reply(rejectedPromiseError).code(401);
});
How can I rearange it so it uses Promises?
EDIT (Possible solution):
After playing a bit, I believe I have implemented promises.
getAll: function (request, reply) {
var projectId = request.params.projectid;
var promises = [];
var post;
models.SamePart.findAll({
where: {ProjectId: projectId}
})
.then(function (sameparts) {
//Iteration is here
sameparts.forEach(function (res) {
promises.push(
Promise.all([
models.Parts.findAll({where: {id: res.dataValues.PartId}})
]))
});
//Bulk lookup for the parts that were marked as identical
return Promise.all(promises);
}).then(function (completepartslist) {
reply(completepartslist).code(200);
});
Is this a correct approach? It seems that completepartslist contains many unwanted objects, including Promise stuff. How can I flatten it, so to avoid complex for loops?
If you are using .then(), then you are, in all probability, already using promises.
Your original, working code doesn't appear to be blocking.
Your final getAll() looks like it should simplify to :
getAll: function (request, reply) {
models.SamePart.findAll({
where: { ProjectId: request.params.projectid }
}).then(function (sameparts) {
return Promise.all(sameparts.map(function (res) {
return models.Parts.findAll({ where: { id: res.dataValues.PartId } });
}));
}).then(function (completepartslist) {
reply(completepartslist).code(200);
});
}
However, you need to add error handling back in.
even more simplified
getAll: function (request, reply) {
models.SamePart.findAll({
where: { ProjectId: request.params.projectid }
}).reduce(function (completepartslist, sameparts) {
return models.Parts.findAll({ where: { id: sameparts.PartId } }).
then(function(res){
completepartslist.concat(res)
});
}), []);
}).then(function (completepartslist) {
reply(completepartslist).code(200);
});
}

react-flux application error - unable to find the function even if it's defined

I have a react component - coursePage.js
function getCourseInitState(){
return {
courses: CourseStore.getAllCourses()//courseStore is required in script
};
}
var Courses = React.createClass({
getInitialState: function(){
return getCourseInitState();
},
render: function () {
return (
<div>
<h1> Course </h1>
<CourseList courses={this.state.courses} />
</div>
);
}
});
Action file -courseAction
var CourseAction = {
CourseList: function(){
var courseList = CourseApi.getAllCourses();
Dispatcher.dispatch({
actionType: ActionTypes.COURSE_INITIALIZE,
courseList: courseList
});
}
Store File - courseStore
var CourseStore = assign({}, EventEmitter.prototype, {
addChangeListener: function(callback){
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback){
this.removeListener(CHANGE_EVENT, callback);
},
emitChange: function(){
this.emit(CHANGE_EVENT);
},
getAllcourses: function(){ //here is the function define
return _courses;
},
getCourseById: function(id){
return _.find(_courses, {id: id});
}
});
Dispatcher.register(function(action){
switch(action.actionType){
case ActionTypes.COURSE_INITIALIZE:
_courses = action.CourseList;
CourseStore.emitChange();
break;
}
});
module.exports = CourseStore;
in console I am getting "Uncaught TypeError: CourseStore.getAllCourses is not a function"
I don't want to call api directly in my coursePage.js so I find this way of initialising the page but it is not working.
(Please note - I am new to this) As per my recent learning Action file must always call API and send the request to State. I can load with help of componentWillMount function. But, I wanted to solve with this.If not wrong, then it is more neat and preferable way of implementing?
You have a typo -> getAllcourses in the Store and in the Component you call getAllCourses
getAllCourses: function(){ //Should be getAllCourses instead of getAllcourses
return _courses;
},

backbone collection add does not trigger model validate

I am rather new to backbone and wanted to test a simple script that handles a to do list. Here is the code i used so far:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template($('#' + id).html());
}
App.Models.Task = Backbone.Model.extend({
validate: function(attributes) {
if ( !$.trim(attributes.title) ) {
return 'Invalid title';
}
}
});
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task
});
App.Views.Task = Backbone.View.extend({
tagName: 'li',
template: template('taskTemplate'),
initialize: function () {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
events: {
'click .edit': 'editTask',
'click .delete': 'destroy'
},
destroy: function() {
if (confirm('Are you sure?')) {
this.model.destroy();
}
},
remove: function() {
this.$el.remove();
},
editTask: function() {
var newTaskTitle = prompt('New title:', this.model.get('title'));
this.model.set('title', newTaskTitle, {validate: true});
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
App.Views.AddTask = Backbone.View.extend({
el: 'form#addTask',
initialize: function() {
},
events: {
'submit': 'submit'
},
submit: function(event) {
event.preventDefault();
var newTaskTitle = $(event.currentTarget).find('input[type=text]').val();
var task = new App.Models.Task({ title: newTaskTitle });
this.collection.add(task, {add: true, merge: false, remove: false});
}
});
App.Views.Tasks = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.Task({ model: task });
this.$el.append(taskView.render().el);
}
});
var tasks = new App.Collections.Tasks([
{
title: 'Go to store',
priority: 4
},
{
title: 'Go to mall',
priority: 3
},
{
title: 'Get to work',
priority: 5
}
]);
var addTaskView = new App.Views.AddTask({ collection: tasks });
var tasksView = new App.Views.Tasks({ collection: tasks });
$('div.tasks').append(tasksView.render().el);
})();
So the model validation works fine ... the only pb is that collection.add does not validate the newly added model .... is the a way to force the validation?
Thanks,
Rares
From the fine manual:
validate model.validate(attributes, options)
[...] By default validate is called before save, but can also be
called before set if {validate:true} is passed.
Collection#add does not call save nor does it call set with the validate: true option. If you want to validate during add, say so:
collection.add(models, { validate: true });
That will get validate:true all that way down to Model#set.
A quick look at a simplified example may be helpful:
var M = Backbone.Model.extend({
set: function() {
console.log('setting...');
Backbone.Model.prototype.set.apply(this, arguments);
},
validate: function() {
console.log('validating...');
return 'Never!';
}
});
var C = Backbone.Collection.extend({
model: M
});
var c = new C;
c.on('add', function() {
console.log('Added: ', arguments);
});
c.on('invalid', function() {
console.log('Error: ', arguments);
});
Now if we do this (http://jsfiddle.net/ambiguous/7NqPg/):
c.add(
{ where: 'is', pancakes: 'house?' },
{ validate: true }
);
You'll see that set is called with validate: true, validate will be called, and you'll get an error. But if you say this (http://jsfiddle.net/ambiguous/7b2mn/):
c.add(
{ where: 'is', pancakes: 'house?' },
{add: true, merge: false, remove: false} // Your options
);
You'll see that set is called without validate: true, validate will not be called, and the model will be added to the collection.
The above behavior is quite strongly implied but not explicitly specified so you may not want to trust it. Model#initialize does say:
you can pass in the initial values of the attributes, which will be set on the model.
and set does explicitly mention the validate option. However, there is no guarantee that Collection#add will send options to the model constructor or set or that the model's constructor will send the options to set. So if you want to be really paranoid and future proof, you could add a quick check for this "options get all the way down to set" behavior to your test suite; then, if it changes you'll know about it and you can fix it.
if you pass options to your collection add method, the validation method will not be called and as your arguments in this case are all set to the default value, there is not need to pass them
this.collection.add(task);
you may want to take a look at this question.
Prevent Backbone.js model from validating when first added to collection

Resources