How to get nested json objects using react js - javascript

I'm using laravel, and im trying to check to see if a user is following a user, if so the text box will change from follow to following.
I can get the id of the user easily but i can't check to see if a user has followable id
I need to reference the pivot object, and this object only shows when i do
<div id="profile" data='{{ $myuser->followers}}'></div>
but i need to use $myuser variable by itself.
This is what i have so far.
Profile.blade.php
<div id="profile" data='{{ $myuser}}'></div>
Profile.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
export default class Example extends Component {
constructor(props){
super(props);
let id = JSON.parse(this.props.data);
// console.log('data from component', JSON.parse(this.props.data));
this.state = {
btnText: 'Follow',
className: 'follow-button',
user:{
// i can get a user id, but i cant get the followable id.
id:id.id,
followers:id.pivot.followable_id
}
};
}
myfollow(user) {
axios('/user/follow/'+ this.state.user.id , { method: "POST" })
.then(response => {
console.log(response);
});
};
componentDidMount(){
console.log('data from component', this.state.user.followers);
// if (this.state.user.followers === 3){
// this.setState({
// btnText:'Following',
// className:'following-button'
// });
// }
}
UserController.php
public function getProfile($user)
{
$users = User::with(['posts.likes' => function($query) {
$query->whereNull('deleted_at');
$query->where('user_id', auth()->user()->id);
}, 'follow','follow.follower'])
->where('name','=', $user)->get();
$user = $users->map(function(User $myuser){
$myuser['followedByMe'] = $myuser->follow->count() == 0 ? false : true;
return $myuser;
});
if(!$user){
return redirect('404');
}
return view ('profile')->with('user', $user);
}
MyFollow.php (model)
public function followedByMe()
{
foreach($this->follower as $followers) {
if ($followers->user_id == auth()->id()){
return true;
}
}
return false;
}
User.php Model
?php
namespace App;
use App\User;
use App\Post;
use App\GalleryImage;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\MyFollow;
use Overtrue\LaravelFollow\Traits\CanFollow;
use Overtrue\LaravelFollow\Traits\CanBeFollowed;
class User extends Authenticatable
{
use Notifiable,CanFollow, CanBeFollowed;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function posts()
{
return $this->hasMany(Post::class);
}
public function images()
{
return $this->hasMany(GalleryImage::class, 'user_id');
}
public function likes()
{
return $this->hasMany('App\Like');
}
public function follow()
{
return $this->hasMany('App\MyFollow');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
}

Related

Laravel collection Call to a member function exports() on array

I am trying to pass some values as array on jobExport() collection and am getting an error Call to a member function jobsExport() on array. I understand that the collection need to populatet with modal collection value, but am trying to export multiple record(only record i select) from table , and to make this happend i thing i need to pass value as array from control to modal method, i have searched a loot to find a solution for this but i dont find anythin yet. Here is what i have done
Route
Route::any('export/jobs/{jobs}', [JobController::class, 'export']);
Pass data from vue to laravel
watch: {
selected: function(){
this.url = '/export/jobs/' + this.selected;
}
},
// After sending request on backend route will look like this
http://127.0.0.1:8000/export/jobs/1,2,4
Laravel controller
public function export($jobs)
{
return Excel::download(new JobsExport($jobs), 'jobs.xlsx');
}
Model Method
public function jobsExport()
{
return Job::with('templates', 'teams')
->whereHas('templates', function ($q) {
$q->where('id', $this->id);
})
->get();
}
JobExport
class JobsExport implements WithStyles, FromCollection, WithMapping, WithHeadings
{
use Exportable;
private $jobs;
public function __construct($jobs)
{
$this->jobs = $jobs;
}
public function collection()
{
return $this->jobs->jobsExport();
}
public function map($jobsExport): array
{
// dd($jobsExport->templates->first()->template_name);
return [
$jobsExport->id,
$jobsExport->templates->implode('template_name', ', '),
$jobsExport->job_completed,
];
}
/**
* #return \Illuminate\Support\Collection
*/
public function headings():array
{
return[
'Id',
'Template',
'Completed',
];
}
}
Is the $jobs an id? If so, make it $jobId
public function export($jobId)
{
// assuming you have Job model which holds the jobs table
$jobs = Job::where('id', $jobId)->get();
return Excel::download(new JobsExport($jobs), 'jobs.xlsx');
}
and in your export class
class JobsExport implements WithStyles, FromCollection, WithMapping, WithHeadings
{
use Exportable;
private $jobs;
public function __construct($jobs)
{
$this->jobs = $jobs;
}
public function collection()
{
// change this
//return $this->jobs->jobsExport();
// to
return $this->jobs;
}
public function map($jobsExport): array
{
// dd($jobsExport->templates->first()->template_name);
return [
$jobsExport->id,
$jobsExport->templates->implode('template_name', ', '),
$jobsExport->job_completed,
];
}
/**
* #return \Illuminate\Support\Collection
*/
public function headings():array
{
return[
'Id',
'Template',
'Completed',
];
}
}

Pusher Cannot read properties of undefined (reading 'push')

I want to update the list of users with pusher.
When I submit the console shows this error:
enter image description here
I also get Uncaught refering to pusher.js
The pusher.js cointains code for pusher and it is placed in the footer:
let teamChannel = pusher.subscribe('team-list');
teamChannel.bind('updated-team', function(data) {
app.team.push(JSON.stringify(data));
});
My event:
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NewParticipant implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $team;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($team)
{
$this->team = $team;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|arrays
*/
public function broadcastOn()
{
return new Channel('team-list');
}
public function broadcastAs()
{
return 'updated-team';
}
}
js from my Vue component:
<script>
export default {
data() {
return {
team: [],
}
},
created() {
this.fetchPlayer();
this.listenForChanges();
},
methods: {
fetchPlayer() {
console.log('test');
},
listenForChanges() {
window.Echo.channel('team-list')
.listen('updated-team', (e) => {
console.log("echo is working");
console.log("e is " + e);
})
}
},
computed: {
teamList() {
return this.team;
}
}
}
</script>
My controller has this function:
protected function addPlayer($event, Request $request) {
$profile = auth()->user()->profile;
$profile->participate()->syncWithoutDetaching([$event->id], false);
$team = $event->participants()->get();
event(new NewParticipant($team));
return redirect()->route('event.show', [ 'event' => $event ]);
}
Update: I've moved my pusher code to app.js but the app is still undefined:
const app = new Vue({
el: '#app',
});
let body = document.querySelector("body");
if(body.classList.contains('gruppo-app')) {
Pusher.logToConsole = true;
var pusher = new Pusher('mykey', {
cluster: 'myclutes'
});
let teamChannel = pusher.subscribe('team-list');
teamChannel.bind('updated-team', function(data) {
app.team.push(JSON.stringify(data));
});
}
Update:
The connection with the Pusher is not needed if the Laravel Echo is used.
I focuesd on Echo and I've deleted the this block:
let body = document.querySelector("body");
if(body.classList.contains('gruppo-app')) {
Pusher.logToConsole = true;
var pusher = new Pusher('mykey', {
cluster: 'myclutes'
});
let teamChannel = pusher.subscribe('team-list');
teamChannel.bind('updated-team', function(data) {
app.team.push(JSON.stringify(data));
});
}
To connect Echo correctly the dot . has to be added to the listen function like this:
window.Echo.channel('team-list')
.listen('.updated-team', (e) => {
console.log("echo is working");
console.log("e is " + e);
})
Now the Pusher is working correctly.

How to get data value in regular js file from vue component?

I have component MyComponent.vue where I have data value that constantly changes. I want to pass this value to javascript file(js file should know about changes of value everytime)
Why do I do that? Because my regular js file is a service layer for axios methods. I can import this file in many other components. The file contains axios methods and urls are dynamic.
I want those urls depend on data variable. This data variable comes from MyComponent.js
So the main goal is to make dynamic urls of axios that depend on data variable
I tried some code but it doesn't work, because js file(CategoryService.js) know nothing about this.categoryNumber.
MyComponent.vue:
<script>
export default {
data() {
return {
categoryNumber: 1
}
}
}
</script>
CategoryService.js
import http from "../../../http-common";
let category = "category1";
if (this.categoryNumber === 1) {
category = "category1";
} if (this.categoryNumber === 2) {
category = "category2";
}
class CategoryService {
get(id) {
return http.get(`/${category}/${id}`);
}
update(id, data) {
return http.put(`/${category}/${id}`, data);
}
create(data) {
return http.post(`/${category}`, data);
}
delete(id) {
return http.delete(`/${category}/${id}`);
}
getAll() {
return http.get(`/${category}/all`);
}
}
export default new CategoryService();
So with a bit of refactoring, you could easily get this working.
First of all, I would put the if/else logic of your class into it.
For convenience and scalability, I would use a Vuex store that will keep track of your categoryNumber and share it accross all your components.
Then I would bind my service to my Vue instance so I can easily access it in all my components as well as the store and I would pass the latter to my class as a parameter.
For the last part, I don't know the logic in the http-common file so the code I will show you is a bit nasty. But depending on wether or not you bound 'http' to axios, you could make use of axios interceptors to call the getCategoryNumber() method in every request.
Here's an idea of the implementation I would go for:
const CategoryService = class CategoryService {
constructor(store) {
this._store = store;
this.category = "category1";
}
getCategoryNumber() {
if (this._store.state.categoryNumber === 1) {
this.category = "category1";
}
if (this._store.state.categoryNumber === 2) {
this.category = "category2";
}
console.log(this.category); // for demo puprose
}
get(id) {
this.getCategoryNumber(); // We could use axios request interceptor instead of calling that in every route, but that works !
return http.get(`/${this.category}/${id}`);
}
update(id, data) {
this.getCategoryNumber();
return http.put(`/${this.category}/${id}`, data);
}
create(data) {
this.getCategoryNumber();
return http.post(`/${this.category}`, data);
}
delete(id) {
this.getCategoryNumber();
return http.delete(`/${this.category}/${id}`);
}
getAll() {
this.getCategoryNumber();
return http.get(`/${this.category}/all`);
}
}
const store = new Vuex.Store({
state: {
categoryNumber: 1
},
mutations: {
setCategoryNumber(state, payload) {
state.categoryNumber = payload;
}
}
});
// Bind your service to the Vue prototype so you can easily use it in any component with 'this.$service'
// pass it the store instance as parameter
Vue.prototype.$service = new CategoryService(store);
new Vue({
el: "#app",
store, // dont forget to bind your store to your Vue instance
methods: {
updateCategoryNumber() {
// Put here any logic to update the number
this.categoryNumber = this.categoryNumber === 1 ? 2 : 1;
this.checkServiceCategoryValue();
},
checkServiceCategoryValue() {
// for demonstration purpose
this.$service.getCategoryNumber();
}
},
computed: {
// Look for the store value and update it
categoryNumber: {
get() {
return this.$store.state.categoryNumber;
},
set(value) {
this.$store.commit("setCategoryNumber", value);
}
}
}
});
<div id="app">
<h2>number: {{ categoryNumber }}</h2>
<button type="button" #click="updateCategoryNumber()">
updateCategoryNumber
</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex#2.0.0"></script>
Thanks to #Solar
I just added one more parameter for all urls and put the number of category to it
CategoryService.js:
class CategoryOneService {
get(id, category) {
return http.get(`/${category}/${id}`);
}
getAll(category) {
return http.get(`/${category}/all`);
}
}
functions.js:
let catNum = "";
function getQuestion() {
if (this.categoryNumber === 1) {
catNum = "category1";
}
if (this.categoryNumber === 2) {
catNum = "category2";
}
let questionId = this.questionNumber;
CategoryOneService.get(questionId, catNum)
.then(response => {
this.question = response.data.question;
this.answer = response.data.answer;
})
.catch(error => {
console.log(error);
});
}

Cannot set property 'id' of undefined when trying to get a object

I am having an issue when I try to get a specified user from Firebase, Firestore.
export class TaskService {
tasksCollection: AngularFirestoreCollection<Task>;
taskDoc: AngularFirestoreDocument<Task>;
tasks: Observable<Task[]>;
task: Observable<Task>;
constructor(private afs: AngularFirestore) {
this.tasksCollection = this.afs.collection('tasks', ref => ref.orderBy('title', 'asc'));
}
getTask(id: string): Observable<Task> {
this.taskDoc = this.afs.doc<Task>(`clients/${id}`);
this.task = this.taskDoc.snapshotChanges().pipe(map(action => {
if (action.payload.exists === false) {
return null;
} else {
const data = action.payload.data() as Task;
data.id = action.payload.id;
return data;
}
}));
return this.task;
}
}
And this is my Component.ts file
export class TaskDetailsComponent implements OnInit {
id: string;
task: Task;
hasHours = false;
showHoursOnUpdate: false;
constructor(
private taskService: TaskService,
private router: Router,
private route: ActivatedRoute
) { }
ngOnInit() {
// Get id from url
this.id = this.route.snapshot.params.id;
// Get client
this.taskService.getTask(this.id).subscribe(task => {
if (task != null) {
if (task.hours > 0) {
this.hasHours = true;
}
}
this.task = task;
});
console.log(this.id);
console.log(this.task);
}
}
The result for id is good.
But the result for object (task) is undefined.
P.S
I also have functions for getting all the users and adding a new user, so if that's relevant please let me know in the comments
Your line of code
this.id = this.route.snapshot.params.id;
In this case id is not a table column but it's your document id by Firestore
Here an example of firestore
So your Id in this case is the red one and not the blue one.

How to spread data with one action to many stores in React flux arcitecture?

How to some actions data to many stores?
For example, I got some post data from server in user action.
So this is simple psudo action code.
class UserActions {
getPosts() {
asyncFetch(apiEndPoint, function(data) {
/*
* data : {
* PostStore : [ ... ],
* UserStore : { ... },
* CommentStore : [ ... ],
* AppDataStore : { ... },
* StatusDataStore : { ... },
* ...
* }
*
*/
PostActions.receiveStoreData(data.PostStore);
UserActions.receiveStoreData(data.UserStore);
CommentActions.receiveStoreData(data.CommentStore);
AppDataActions.receiveStoreData(data.AppDataStore);
StatusActions.receiveStoreData(data.StatusDataStore);
...
}
}
}
I'm curious about setting many store data into the each stores calling actions in the action.
How to fix it with best practice?
Your action creator should use the dispatcher to dispatch the corresponding action as below:
import { Dispatcher } from 'flux';
class UserActions {
getPosts() {
asyncFetch(apiEndPoint, function(data) {
const action = {
type: 'ADD_POSTS',
data
};
Dispatcher.dispatch(action);
}
}
// ...
}
Then one or more store can register to the dispatcher and listen to the same ADD_POSTS action:
import { EventEmitter } from 'events';
let posts = [];
const PostStore = Object.assign({}, EventEmitter.prototype, {
dispatcherIndex: AppDispatcher.register(action => {
const { type, data } = action;
switch (type) {
case 'ADD_POSTS':
posts = posts.concat(data);
PostStore.emitChange();
break;
// ...
}
return true;
});
emitChange() {
this.emit('change');
}
// ...
});

Categories

Resources