VueJS how to delay method to execute after render? - javascript

I have button that executes parse method
parse: function()
{
this.json.data = getDataFromAPI();
applyColor();
},
applyColor: function()
{
for (var i=0; i<this.json.data.length; i++)
{
var doc = document.getElementById(this.json.data[i].id);
doc.style.background = "red";
}
}
The problem is that applyColor cannot execute properly because this.json.data is not rendered until parse() function ends.
I'd want to achieve something like this:
this.json.data = getDataFromAPI();
exit parse() method
execute applyColor();
but without huge changes to code -> maybe some kind of "put that aside for later" like
this.json.data = getDataFromAPI();
promise(500ms) applyColor();
exit parse() method
500ms
executes applyColor()
What I've been trying?
this.$forceUpdate(); before apply

You can use the computed properties to trigger an update based on the update of other properties:
https://v2.vuejs.org/v2/guide/computed.html
But I don't fully understand why you loop through the elements of the dataset in the applyColor method and why you don't loop it in the Vue template with creating a CSS class for the style like this:
<div v-for="element, index in json.data" :key="index" class="bg-red">{{element}}</div>

you need something like this
getDataFromAPI should be promise, when data comes you send 'this' variable as a reference using self.
parse: function()
{
var self = this;
getDataFromAPI().then((data)=>{
self.json.data = data
self.applyColor();
})
},
applyColor: function()
{
for (var i=0; i<this.json.data.length; i++)
{
var doc = document.getElementById(this.json.data[i].id);
doc.style.background = "red";
}
}

Async and await seems like it will work with the least changes of code. You need to define parse with async and this.json.data = await getDataFromApi(), doing it this way , the code will execute in the exact order you have it on description, so applyColors will have this.json.data . Ref: https://javascript.info/async-await

Related

Accessing Svelte component properties in a callback?

Imagine that you have a lot of properties in a component:
let a = 'foo';
let b = 'bar';
// ...
let z = 'baz';
You then want to do something like update all of them from an external callback, like in another library (i.e. something that isn't and can't be a Svelte component itself).
A simple use case is just an AJAX method to load in a bunch of data (assume this ajax function works and you can pass it a callback):
onMount(async function() {
ajax('/data', function(data) {
a = data.a;
b = data.b;
// ...
z = data.z;
});
});
This works, but it's incredibly boilerplaty. What I'd really like is a way to loop through all the properties so they can be assigned to programmatically, especially without prior knowledge on the outside library/callback's part.
Is there no way to get access to a Svelte component and its properties so you can loop through them and assign them from an outside function?
Vue has a simple solution to this, because you can pass the component around, and still check and assign to its properties:
var vm = this;
ajax('/data', function(data) {
for (var key in data) {
if (vm.hasOwnProperty(key)) {
vm[key] = data[key];
}
});
});
I have seen some solutions to this, but they're all outdated - none of them work with Svelte 3.
Apologies if this has been asked before. I've spent days trying to figure this out to avoid all that extra boilerplate and the closest I could find is Access Component Object in External Callback? which does not have an answer right now.
If possible, you could put the ajax call in the parent component and have the data returned from it stored in a temporary object, that you then pass on to the component using the spread operator.
<Component { ...dataObject }></Component>
let dataObject = {};
onMount(async function() {
ajax('/data', function(data) {
dataObject = data;
});
});
You can reduce the boilerplate by using destructuring:
onMount(async function() {
ajax('/data', data => {
({ a, b, ..., z } = data);
});
});
But if you have a very large number of variables, you might be better off just putting them in an object in the first place:
let stuff;
onMount(async function() {
ajax('/data', data => {
stuff = data;
});
});

Call function from another function with parameters passed from both functions

I have this load-more listener on a button that calls the functions and it works fine.
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
moviesPage++;
getMovies(moviesPage);
//getMovies(genreId, moviesPage);
} else if (document.querySelector('#series.active-link')) {
seriesPage++;
getSeries(seriesPage);
}
});
Now I have another listener on a list of links that calls the following code. It takes the genreId from the event parameter to sent as an argument to the api call. Also works fine so far.
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
movie.movieGenre(genreId)
.then(movieGenreRes => {
ui.printMovieByGenre(movieGenreRes);
})
.catch(err => console.log(err));
};
What I want to do is to call getByGenre from the load-more listener while passing also the moviesPage argument as you can see on the commented code so it can also be passed to the api call.
What would be the best way to do that? I've looked into .call() and .bind() but I'm not sure if it's the right direction to look at or even how to implement it in this situation.
Short Answer
Kludge: Global State
The simplest, though not the most elegant, way for you to solve this problem right now is by using some global state.
Take a global selection object that holds the selected genreId. Make sure you declare the object literal before using it anywhere.
So, your code might look something like so:
var selection = { };
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
selection.genreId = genreId;
movie.movieGenre(...);
};
...
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
...
if (selection.genreId !== undefined) {
getMovies(selection.genreId, moviesPage);
}
} else if (...)) {
...
}
});
Closure
A more elegant way for you to accomplish this is by using a closure, but for that I have to know your code structure a bit more. For now, global state like the above will work for you.
Longer Answer
Your concerns have not been separated. You are mixing up more than one concern in your objects.
For e.g. to load more movies, in your load-more listener, you call a function named getMovies. However, from within the .dropdown-menu listener, you call into a movie object's method via the getByGenre method.
Ideally, you want to keep your UI concerns (such as selecting elements by using a query selector or reading data from elements) separate from your actual business objects. So, a more extensible model would have been like below:
var movies = {
get: function(howMany) {
if (howMany === undefined) {
howMany = defaultNumberOfMoviesToGetPerCall;
}
if (movies.genreId !== undefined) {
// get only those movies of the selected genre
} else {
// get all kinds of movies
}
},
genreId : undefined,
defaultNumberOfMoviesToGetPerCall: 25
};
document.get...('.load-more').addEventListener('whatever', (e) => {
var moviesArray = movies.get();
// do UI things with the moviesArray
});
document.get...('.dropdown-menu').addEventListener('whatever', (e) => {
movies.genreId = e.target.dataset.genreId;
var moviesArray = movies.get();
// do UI things with the moviesArray
});

Call a function in node.js programming

I have the following code:
Manager.prototype.checkOrder = function() {
var finish = function(err, filled) {
if(!filled) {
log.info(this.action, 'order was not (fully) filled, cancelling and creating new order');
this.exchange.cancelOrder(this.order);
}
log.info(this.action, 'was successfull');
}
this.exchange.checkOrder(this.order, _.bind(finish, this));
}
And I don't know how to call it on other parts of the code.
I have tried this :
setTimeout(this.checkOrder, util.minToMs(1));
but it didn't work.
I want to do it without using setTimeout function.
As you are using prototypes you probably want to create an Object first. After that you can call the method like every other function.
var myManager = new Manager();
myManager.checkOrder();

How to form callback when using context.executeQueryAsync delegates in javascript

Sorry for yet another question about callbacks. In trying to solve this problem, I've run across about a million of them. However, I'm having trouble wrapping my head around this particular scenario.
I have the code below, which obviously doesn't work as delegates apparently don't return values (I'm learning as I go, here). So, I know I need a callback at this point, but I'm not sure how to change this code to do that. Can anyone help?
function MyFunction() {
var ThisLoggedInUser = checkCurrentUser();
//do some stuff with the current user
}
function checkCurrentUser() {
var context = SP.ClientContext.get_current();
var siteColl = context.get_site();
var web = siteColl.get_rootWeb();
this._currentUser = web.get_currentUser();
context.load(this._currentUser);
context.executeQueryAsync(Function.createDelegate(this, this.CheckUserSucceeded),
Function.createDelegate(this, this.CheckUserfailed));
}
function CheckUserSucceeded() {
var ThisUser = this._currentUser.get_title();
return ThisUser;
}
function CheckUserfailed() {
alert('failed');
}
Based on your comment, you have to rething the way you want your code because you cannot use ThisUser in MyFunction().
For example you could do that:
function CheckUser() { ... }
// then call the function to find the current user
CheckUser();
// then in CheckUserSucceeded you call MyFunction()
function CheckUserSucceeded() {
MyFunction(this._currentUser.getTitle())
}
// and now you can use ThisUser in MyFunction()
function MyFunction(ThisUser) {
// do something with ThisUser
}
Your CheckUserSucceed won't return anything because it's asynchronous....
So you have to do something like that:
var ThisUser;
function CheckUserSucceeded() {
ThisUser = this._currentUser.getTitle()
// here you can call an other action and do something with ThisUser
}
You may also want to check the $SP().whoami() function from http://aymkdn.github.io/SharepointPlus/ and see the documentation.

JavaScript object is undefined

I need some help please with a javascript object. it goes like this:
I call this function addFilter(element) with a html DOM element.
The function looks like this:
function MyList() {
this.arr = new Array();
this.index = 0;
}
MyList.prototype.add = function(filter) {
this.arr[this.index++] = filter;
//alert(this.arr[0] + ' mylist arr');
}
function Filter(element) {
this.setElement(element);
}
Filter.prototype.setElement = function (element) {
this.element = element;
this.kinorid = $(element).attr('id');
}
function addFilter(element) {
filters.Add(new Filter(element));
}
var filters = new MyList();
Now with in another function that in my case the function creates the jquery UI Slider, and every time the slider changes i need to get the parent element of that element that was sent to addFilter like i said in the beginning. so then i try doing
var value = filters.arr[0];
but like i said it id undefined.
Can any one please help me by reviewing the code, and tell me whats wrong.
Thank you very much.
You still haven't said where or how you're using filters.arr[0], without which it's very difficult to help you.
Assuming your code using it looks something like this:
AddFilter($("#theElement"));
display(typeof filters.arr[0]);
filters.arr[0].element.css("color", "blue");
It should be working; live example.
My only thought is if AddFilter and filters are not defined within the same scope. You're using filters within AddFilter, so AddFilter must be defined in the same scope as filters (or in a sub-scope). So this would be fine:
var filters;
function AddFilter() { ... }
And this
function AddFilter() { ... }
var filters;
And this
var filters;
$(function() {
function AddFilter() { ... }
});
But not
function AddFilter() { ... }
$(function() {
var filters;
// ...
});
...because in that last case, AddFilter is defined outside the scope in which filters is defined.
Your code is very convoluted, I don't understand it at all. Anyway, you are looking for (I think):
var value = filters.arr[0].element;
since you assign the element reference to arr[this.index].
Incidentally, if you are passing an element, then:
$(this).attr('id');
is an awfully slow way to do:
this.id;
Edit
The code I used (where there was a div with id 'd0' in the DOM):
var filters = new MyList();
AddFilter(document.getElementById('d0'));
alert(filters.arr[0].element);

Categories