i want simple array not [__ob__: Observer] - laravel

i call for the api to fetch data, i test it with postman and laravel send it as an array correctly, but vue turn my array into [ob: Observer] and i cant extract my array from it thanks to #Stockafisso it has been solved but
Edit: now my problem is, programming_languages array is only accesible inside getLanguages() method not anywhere else
data(){
return{
answer: '',
maxWrong: '',
programming_languages : []
}
},
methods:{
getLanguages(){
axios.get('/api/toController').then((response)=> {
this.programming_languages = JSON.parse(JSON.stringify(response.data)); //works fine
this.answer = this.programming_languages[Math.floor(Math.random() * this.programming_languages.length)].name; //works fine
this.maxWrong = this.answer.length;// works fine here, i dont want to initiaize answer variable in this method
});
},
randWord(){
this.answer = this.programming_languages[Math.floor(Math.random() * this.programming_languages.length)].name;// it is not working here
//Error in created hook: "TypeError: Cannot read property 'name' of undefined"
this.maxWrong = this.answer.length;// doesn't work here
}
},
created(){
this.getLanguages();
this.randWord();
}
what can i do?
thank you

most probable the error(s) is here:
this.programming_languages.push(JSON.parse(JSON.stringify(response.data)));
you are inserting the Laravel returned array inside the declared programming_languages, while you should override or concat it.
To explain better, a little example:
consider this:
let arr1 = [];
let arr2 = [1,2,3];
if you do arr1.push(arr2) your arr1 will be [[1,2,3]] while you want it to be [1,2,3].
Going back to your problem, to fix it you have two ways:
a)
.then((response)=> {
this.programming_languages = JSON.parse(JSON.stringify(response.data));
b)
.then((response)=> {
let swapArray = [...this.programming_languages];
this.programming_languages = swapArray.concat(JSON.parse(JSON.stringify(response.data)));
If this does not fix the error,
it could be the problem is in the array returned from Laravel. If it has not numeric or not sorted keys, JS will "fill" the missing spaces with Ob. Observer to try to keep the keys.

thank you all, finally i managed to solve it
first i extract array from vue's [ob: Observer]
this.programming_languages = JSON.parse(JSON.stringify(response.data));
special thanks to #stockafisso
and second was that i couldn't get this programming_languages array out of axios method, that happens due to nature of asynchronous call, see this informative link
How do I return the response from an asynchronous call?
i managed to solve it this way, async and await
methods: {
async getPosts(){
await axios.get('/api/hangman').then(response => {
this.programming_languages = JSON.parse(JSON.stringify(response.data));
}).catch(error => console.log(error));
},
randWord(){
this.answer = this.programming_languages[Math.floor(Math.random() * this.programming_languages.length)].name;
this.maxWrong = this.answer.length;
},
},
created(){
this.getPosts().then(() => this.randWord());
}

Related

Why is my React state hook (setState) so picky about the array I give it?

There is something about state hooks in React that I don't understand. I am dealing with the following situation in my code:
const [myArray, setMyArray] = useState(["foo"])
useEffect(() => {
console.log("myArray has been updated")
}, [myArray])
const clickUpdate = () => {
var myUpdatedArray = myArray
myUpdatedArray.push("bar")
setMyArray(myUpdatedArray)
}
This does not work. Calling the clickUpdate does not update myArray.
The problem is in the clickUpdate function. When I rewrite the function in the following way it works just fine:
...
const clickUpdate = () => {
var myUpdatedArray = [...myArray, "bar"]
setMyArray(myUpdatedArray)
}
or alternatively:
...
const clickUpdate = () => {
setMyArray(myArray=> [...myArray, "bar])
}
Either solution works for me just fine, but I am curious:
What is it about the .push mutation of myUpdatedArray that makes my setMyArray not work properly? Or what else is going on here that I am missing?
Thank you!
React compares the values in the deps array by reference, that's way it works when you create a new array.
If you are just going to add/remove items (not editing them) then you can be more performant and pass myArray.length to the deps list. Thus react will detect changes when the length is changed.
useEffect(() => {
console.log("myArray has been updated")
}, [myArray.length])

Vue js function countSubcategories() returns [object Promise]

countSubcategories() function returns [object Promise] where it should return row counts of mapped subcategories.
This code is in vue.js & Laravel, Any suggestions on this?
<div v-for="(cat,index) in cats.data" :key="cat.id">
{{ countSubcategories(cat.id) }} // Here subcategories row counts should be displayed.
</div>
<script>
export default {
data() {
return {
cats: {},
childcounts: ""
};
},
created() {
this.getCategories();
},
methods: {
countSubcategories(id) {
return axios
.get("/api/user-permission-child-count/" + `${id}`)
.then(response => {
this.childcounts = response.data;
return response.data;
});
},
getCategories(page) {
if (typeof page === "undefined") {
page = 1;
}
let url = helper.getFilterURL(this.filterpartnerForm);
axios
.get("/api/get-user-permission-categories?page=" + page + url)
.then(response => (this.cats = response.data));
}
}
};
</script>
As Aron stated in the previous answer as you are calling direct from the template the information is not ready when the template is rendered.
As far as I understood you need to run getCategories first so then you can fetch the rest of your data, right?
If that's the case I have a suggestion:
Send an array of cat ids to your back-end and there you could send back the list of subcategories you need, this and this one are good resources so read.
And instead of having 2 getCategories and countSubcategories you could "merge" then like this:
fetchCategoriesAndSubcategories(page) {
if (typeof page === "undefined") {
page = 1;
}
let url = helper.getFilterURL(this.filterpartnerForm);
axios
.get("/api/get-user-permission-categories?page=" + page + url)
.then(response => {
this.cats = response.data;
let catIds = this.cats.map(cat => (cat.id));
return this.countSubcategories(catIds) // dont forget to change your REST endpoint to manage receiving an array of ids
})
.then(response => {
this.childcounts = response.data
});
}
Promises allow you to return promises within and chain .then methods
So in your created() you could just call this.fetchCategoriesAndSubcategories passing the data you need. Also you can update your template by adding a v-if so it doesn't throw an error while the promise didn't finish loading. something like this:
<div v-if="childCounts" v-for="(subcategorie, index) in childCounts" :key="subcategorie.id">
{{ subcategorie }} // Here subcategories row counts should be displayed.
</div>
Hello!
Based on the provided information, it could be 2 things. First of all, you may try replacing:
return response.data;
with:
console.log(this.childcounts)
and look in the console if you have the correct information logged. If not, it may be the way you send the information from Laravel.
PS: More information may be needed to solve this. When are you triggering the 'countSubcategories' method?
I would do all the intial login in the component itself, and not call a function in template like that. It can drastically affect the performance of the app, since the function would be called on change detection. But first, you are getting [object Promise], since that is exactly what you return, a Promise.
So as already mentioned, I would do the login in the component and then display a property in template. So I suggest the following:
methods: {
countSubcategories(id) {
return axios.get("..." + id);
},
getCategories(page) {
if (typeof page === "undefined") {
page = 1;
}
// or use async await pattern
axios.get("...").then(response => {
this.cats = response.data;
// gather all nested requests and perform in parallel
const reqs = this.cats.map(y => this.countSubcategories(y.id));
axios.all(reqs).then(y => {
// merge data
this.cats = this.cats.map((item, i) => {
return {...item, count: y[i].data}
})
});
});
}
}
Now you can display {{cat.count}} in template.
Here's a sample SANDBOX with similar setup.
This is happen 'cause you're trying to render a information who doesn't comeback yet...
Try to change this method inside created, make it async and don't call directly your method on HTML. Them you can render your variable this.childcounts.

How to get the data from BehaviorSubject after its completed?

I have a function that returns a BehaviorSubject but when I try to use the data I get back from the function I need to use it once all the data is back, is there a way to know when the BehaviorSubject is done pulling all the data?
I tried using .finally but it never gets called. Here is the code I'm using.
getData() {
let guideList = '';
this.getChildren(event.node)
.subscribe(
function(data) {
console.log('here');
guideList = data.join(',');
},
function(err) {
console.log('error');
},
function() {
console.log('done');
console.log(guideList);
}
);
}
getChildren(node: TreeNode) {
const nodeIds$ = new BehaviorSubject([]);
//doForAll is a promise
node.doForAll((data) => {
nodeIds$.next(nodeIds$.getValue().concat(data.id));
});
return nodeIds$;
}
Attached is a screen shot of the console.log
Easiest way is to just collect all the data in the array and only call next once the data is all collected. Even better: don't use a subject at all. It is very rare that one ever needs to create a subject. Often people use Subjects when instead they should be using a more streamlined observable factory method or operator:
getChildren(node: TreeNode) {
return Observable.defer(() => {
const result = [];
return node.doForAll(d => result.push(d.id)).then(() => result);
});
}

Handling JSON Objects in RxJS

I am new to cyclejs and rxjs in general and was hoping someone could help me solve my problem.
I am trying to build a demo application for my understanding and stuck with rendering JSON objects on the DOM.
My demo application calls the NASA near earth objects API for the past 7 days and tries to display them.
There is a Load More button at the bottom which on clicking will load data of the previous 7 days (Today - 7 upto Today - 14).
The response I get from the API is as follows
{
"links" : {
"next" : "https://api.nasa.gov/neo/rest/v1/feed?start_date=2016-09-06&end_date=2016-09-12&detailed=false&api_key=DEMO_KEY",
"prev" : "https://api.nasa.gov/neo/rest/v1/feed?start_date=2016-08-25&end_date=2016-08-31&detailed=false&api_key=DEMO_KEY",
"self" : "https://api.nasa.gov/neo/rest/v1/feed?start_date=2016-08-31&end_date=2016-09-06&detailed=false&api_key=DEMO_KEY"
},
"element_count" : 39,
"near_earth_objects" : {
"2016-09-06" : [{
some data
},
{
some data
}],
2016-08-31: [{...}],
...
}
}
I am interested in near_earth_objects JSON object but I am unable to map it beacause of it being an Object.
How do I handle such a situations? Below is the code that I have
function main(sources) {
const api_key = "DEMO_KEY";
const clickEvent$ = sources.DOM.select('.load-more').events('click');
const request$ = clickEvent$.map(() => {
return {
url: "https://api.nasa.gov/neo/rest/v1/feed?start_date=2015-09-06&end_date=2016-09-13&api_key=" + api_key,
method: "GET"
}
}).startWith({
url: "https://api.nasa.gov/neo/rest/v1/feed?start_date=2016-08-31&end_date=2016-09-06&api_key=" + api_key,
method: "GET"
});
const response$$ = sources.HTTP.filter(x$ => x$.url.indexOf("https://api.nasa.gov/neo/rest/v1/feed") != -1).select(response$$);
const response$ = response$$.switch(); //flatten the stream
const nasa$ = response$.map(response => {
return response.body
});
const sinks = {
DOM: nasa$.map(nasa =>
([nasa.near_earth_objects]).map(objects => {
var vdom = [];
//I am not very happy with this part. Can this be improved?
for (var key in objects) {
if (objects.hasOwnProperty(key)) {
vdom.push(objects[key].map(obj => div([
h1(obj.name)
])))
}
}
//returning the vdom does not render on the browser. vdom is an array of arrays. How should i correct this?
console.log(vdom);
return vdom;
})
),
HTTP: request$
};
return sinks;
};
Conceptually, you want to extract the entries of nasa.near_earth_objects (i.e., turn the Object into an Array), then flat map that Array into an Observable sequence.
I'll assume you're already using lodash in your project (you can do it without lodash, but you'll just need to write more glue code manually). I'll also assume you're importing RxJS' Observable as Rx.Observable; adjust the names below to suite your code.
You can accomplish the first task using _.toPairs(nasa.near_earth_objects), and the second part by calling .flatMap(), and returning Rx.Observable.from(near_objects). The resulting Observable will emit items for each key in nasa.near_earth_objects. Each item will be an array, with item[0] being the item's key (e.g., 2016-09-06) and item[1] being the item's value.
Using that idea, you can replace your DOM sink with something like:
nasa$.map(nasa => _.toPairs(nasa.near_earth_objects))
.flatMap(near_objects => Rx.Observable.from(near_objects))
.map(near_object => div([
h1(near_object[1].name)
]))
),

Simple ajax in laravel 4

i have following code
ajax
//ajax edit button
$('.edit_button').on('click', function(e) {
e.preventDefault();
var id_produk = $(this).attr('id');
$.ajax({
type : "POST",
url : "editproduk",
data : id_produk,
dataType: 'JSON',
success : function(data) {
alert('Success');
console.log(data);
},
error: alert('Errors')
});
});
i always get messagebox error
and don't know where i'm missing,
because in chrome - inspect element - console not give any clue
my route
Route::post('/account/editproduk', array(
'as' => 'edit-produk-post',
'uses' => 'AccountController#postEditProduk'
));
my controller
public function postEditProduk() {
if (Request::ajax()) {
return "test test";
}
}
extended question
i running my script well after using return Response::json() like this
$id_produk = Input::get('id_produk');
$produk = Produk::findOrFail($id_produk);
return Response::json($produk);
and access it in view by this script
success : function(data) {
alert('Success');
console.log(data["name-produk"]);
}
but if i want to return array json like
$id_produk = Input::get('id_produk');
$produk = Produk::findOrFail($id_produk);
$spesifikasi = SpesifikasiProduk::where('id_produk', '=', $id_produk);
return Response::json(array($produk, $spesifikasi));
i can't access it in view like this...
success : function(data1, data2) {
alert('Success');
console.log(data1["name-produk"] - data2["title-spek"]);
}
how to access json array
extended question update
if i'm wrong please correct my script
because i get a litle confused with explenation
is this correct way to return it?
Response::json(array('myproduk' => 'Sproduk', 'data2' => 'testData2'));
result
console.log(produk["myproduk"]);
--------------------------------
Object {id_produk: 1, nama_produk: "produk1", deskripsi: "desc_produk"
console.log(produk["data2"]);
--------------------------------
testData2
and i still don't have idea how to print nama_produk in my_produkarray
Question 1:
Why is this code not sending JSON data back.
public function postEditProduk() {
if (Request::ajax()) {
return "test test";
}
}
Answer: Because this is not the right way to send the JSON data back.
From the Laravel 4 docs, the right way to send JSON data back is linked. Hence the correct code becomes:
public function postEditProduk() {
if (Request::ajax()) {
return Response::json("test test");
}
}
Question 2:
Why am I not able to access the data in data1 and data2
success : function(data1, data2) {
alert('Success');
console.log(data1["name-produk"] - data2["title-spek"]);
}
Answer: Because this is not the right way to catch the JSON data. The right way to send is given in the Laravel 4 API reference docs.
static JsonResponse json(string|array $data = array(), int $status = 200, array $headers = array(), int $options)
As you can see the method json takes string or array as the first parameter. So you need to send all your data in the first parameter itself (which you are doing). Since you passed only one parameter, you have to catch only 1 parameter in your javascript. You are catching 2 parameters.
Depending on what $produk and $spesifikasi is, your data will be present in one single array. Lets say that $produk is a string and $spesifikasi is an array. Then your data on the javascript side will be this:
[
[0] => 'value of $produk',
[1] => array [
[0] => 'value1',
[1] => 'value2'
]
]
It would be best if you print the log your entire data and know the structure. Change your code to this:
success : function(data) {
console.log(data.toString());
}
This will print your entire data and then you can see the structure of your data and access it accordingly. If you need help with printing the data on your console, google it, or just let me know.
I sincerely hope that I have explained your doubts clearly. Have a nice day.
Edit
extended question answer:
Replace this line:
$spesifikasi = SpesifikasiProduk::where('id_produk', '=', $id_produk);
With this:
$spesifikasi = SpesifikasiProduk::where('id_produk', '=', $id_produk)->get();
Without calling the get() method, laravel will not return any value.
Then access your data in javascript like this:
console.log(JSON.stringify(data));
This way you will get to know the structure of your data and you can access it like:
data[0]["some_key"]["some_other_key"];
In your controller you're returning text while your ajax request awaits json data, look at these lines of codes, I think you should get your answer:
if(Request::ajax()) {
$province = Input::get('selectedProvince');
//Get all cites for a province
if ($cityList = City::where('province_id','=', $province)) {
return Response::make($cityList->get(['id', 'name']));
}
return Response::json(array('success' => false), 400);
}

Resources