Array to String conversion Error in Laravel vue.js - javascript

I have already posted before with no real replies so I am trying again. I am trying to build a simple table to read data from a mongodb database which will be retrieved by using a query builder in laravel the data needs to be displayed with a vue component but when I try to load up the page an error message comes up:
The full error message if needed : Edit now the error has been fixed the new problem is that no data is being displayed just the titles of the table
ErrorException
Array to string conversion
at vendor/laravel/framework/src/Illuminate/Routing/ResourceRegistrar.php:416
412▕ protected function getResourceAction($resource, $controller, $method, $options)
413▕ {
414▕ $name = $this->getResourceRouteName($resource, $method, $options);
415▕
➜ 416▕ $action = ['as' => $name, 'uses' => $controller.'#'.$method];
417▕
418▕ if (isset($options['middleware'])) {
419▕ $action['middleware'] = $options['middleware'];
420▕ }
+14 vendor frames
15 routes/api.php:20
Illuminate\Support\Facades\Facade::__callStatic()
+3 vendor frames
19 routes/api.php:21
Illuminate\Routing\RouteRegistrar::group()
Here is the code I have used if you wish to replicate:
api.php route:
Route::middleware('api')->group(function () {
Route::resource('/home', [ProductController::class,'home']);
});
web.php route:
Route::get('{any}', function () {
return view('home');
})->where('any', '.*');
PostController Home method:
public function home() {
$posts = Post::paginate(4);
return response()->json($posts);
}
Home component:
<template>
<div>
<table>
<tr>
<td>Id</td>
<td>Title</td>
<td>Status</td>
</tr>
<tr v-for="El in results" v-bind:key="El.id" >
<td>{{El.id}}</td>
<td>{{El.STATUS}}</td>
</tr>
</table>
</div>
</template>
<script>
export default{
data() {
return {
results: []
};
},
methods: {
fetch() {
axios.get('/api/home')
.then(response => this.results = response.data)
.catch(error => console.log(error));
}
}
}
</script>
Home.blade.php:
#extends('layouts.app')
#section('content')
<div class="container" id="app">
<home-component></home-component>
</div>
#endsection
The code seems to be working fine the problem is starting at the api route handler but the why and how to solve it are beyond me. SO, Any and all help and suggestions are appreciated.
Edit: the original question was about an error which has since been removed but replaced with a new problem which is mentioned above.

Your issue is that you are providing too many arguments to the Route::resource(...) method.
The purpose of the Route::resource method is to define multiple routes and their corresponding controller actions according to the resourceful controller pattern.
In your case, you definitely are not using resourceful controllers since you have defined your controller action as public function home(). To fix this, you can simply define the Route::get(...) for the endpoint you are calling in your vue component.
Route::middleware('api')->group(function () {
Route::get('/home', [ProductController::class, 'home']);
});

Related

Why won't my simple get request with axios return what the controller's returning?

I'm learning Laravel with React.js but I can't figure out why my simple get request won't work. I get a 404 error. I've seen a similar question asked on SO and I've tried running composer require barryvdh/laravel-cors on the command line but it still doesn't work.
How can I rectify this?
Here's react code:
constructor(props) {
super(props);
this.axiosGet = this.axiosGet.bind(this);
}
axiosGet(e) {
e.preventDefault();
axios.get('/thank-you')
.then(response => {
console.log(response);
});
}
render() {
<Form onSubmit={this.axiosGet}>
...
</Form>
}
Here's api.php
use App\Http\Controllers\MailController;
Route::get('/thank-you', [MailController::class,'index']);
Here's MailController.php
public function index() {
return "Testing";
}
your route is /api/thank-you. When you are using API routes. API comes in front of your routes.
Change your axios.get('/thank-you') as axios.get('/api/thank-you')

Live-wire - real time chat not responding correctly

I'm trying live-wire to see how to implement it in my upcoming project. The idea is a simple chat ( user enters a body of text and click send then the message will be at the bottom of the chat)
So far everything works fine till I hit the click send button.
Here is picture before I click on send
and here is what happens after I click send
I feel like the issue is in this part of code where I send the message to the live-wire blade
#foreach ($messages as $message)
#if($message->isYou())
<livewire:conversations.conversation-message-own :message="$message" :key="$message->id" />
#else
<livewire:conversations.conversation-message :message="$message" :key="$message->id" />
#endif
#endforeach
here is the component
public $body= "";
public $conversation;
public function mount(Conversation $conversation)
{
$this->conversation = $conversation;
}
public function reply()
{
$this->validate([
'body' => 'required',
]);
$message = $this->conversation->messages()->create([
'user_id' => user()->id,
'body' => $this->body,
]);
$this->emit('message.created',$message->id);
$this->body = "";
}
public function render()
{
return view('livewire.conversations.conversation-reply');
}
}
last is listening to the event component:
class ConversationMessages extends Component
{
public $messages;
public function mount(Collection $messages)
{
$this->messages = $messages;
}
public function render()
{
return view('livewire.conversations.conversation-messages');
}
protected function getListeners()
{
return [
'message.created' => 'prependMessage',
];
}
public function prependMessage($id)
{
$this->messages->prepend(Message::find($id));
}
}
the issue in the inspect log is
Uncaught (in promise) TypeError: Cannot read property 'data' of null
but I think it's not related to the chats data!
Thanks in advance
The problem is in the keys passed to the conversation-messages components. Livewire breaks trying to render these components, even if separated with ifs. You will need to generate different keys, like a string 'message-own-' . $message->id and 'message-'. $message->id . I tested your code with rand() data:
#foreach ($messages as $message)
#if($message->isOwn())
<livewire:conversations.conversation-message-own :message="$message" :key="rand() * $message->id" />
#else
<livewire:conversations.conversation-message :message="$message" :key="rand() * $message->id" />
#endif
#endforeach

Is it possible to use laravel's core methods from a Vue.js component?

I'm relatively comfortable with Laravel 5 but a rather green when it comes to Vue.js and I was wandering if it is possible to call Laravel's methods which are available from blade in a Vue.js component.
For example I want to make a Vue.js component for my navbar like so:
<template>
<nav class="navbar navbar-expand-sm navbar-dark bg-info mb-2">
<div class="container">
Go Home
</div>
</nav>
</template>
<script>
export default {
name: "NavBar"
}
</script>
home is a named route in my wep.php file
Route::get('/', 'SiteController#index')
->name('home');
and I added my Vue component like so:
Vue.component('navbar', require('./components/NavBar'));
in my app.js file
How can I get something like this done? In this example I only use the route() method to insert a src tag but I am also interested in doing something analogous to this from a Vue.js component
#if (Route::has('login'))
<div class="top-right links">
#auth
Home
#else
Login
Register
#endauth
</div>
#endif
This is how I would do it from my *.blade.php file but how can I do this type of logic using Vue components? I'm using Laravel 5.7 by the way.
I have been building Vue Apps with Laravel without any blade templates other than an index to present the base HTML meta data required for the SPA to run.
Beyond that, My Apps typically require an authenticated user throughout and so on user authenticated login request I return with the user success response all the settings and permissions relevant to that user.
To get started and populate Vue with all available web routes, add the following to routes/web.php
Route::get('/js/routes.js', function() {
// White list namespaces that can be exposed to Vue.
$allow = ['App\Http\Controllers'];
// uncomment below to enable cache
// $routes = \Illuminate\Support\Facades\Cache::rememberForever('routes.js', function () {
$routes = [];
foreach(Route::getRoutes()->getIterator() AS $route)
{
if(!array_key_exists('namespace',$route->action))
continue;
if(!is_array($route->action['middleware']))
continue;
if(in_array($route->action['namespace'],$allow,true))
continue;
$name = $route->getName();
if(empty($name))
continue;
$routes[$name] = $route->uri;
};
return $routes;
// });
header('Content-Type: text/javascript');
echo 'let routes = ' . json_encode($routes) . ';';
echo 'window.route = function() { let args = Array.prototype.slice.call(arguments); let name = args.shift();if(routes[name] === undefined) { console.error(\'Unknown route \', name); } else { return \'/\' + routes[name].split(\'/\').map(s => s[0] == \'{\' ? args.shift() : s).join(\'/\');}};';
exit();
})->name('assets.routes');
Add to your blade layout file:
<link rel="preload" href="/js/routes.js" as="script">
To then make those routes available in Vue components you can bind to
Vue using the following example after import Vue from 'Vue':
Vue.prototype.$getRoute = window.route;
Vue['getRoute'] = window.route;
You can then in Vue methods use this.getRoute('laravels.route.name', [ id: user.id ]), this works much like the route function in Laravel with url variables passed as the second parameter. In a router link component or click event it would work like so:
<router-link :to="$getRoute('logout')">Logout</router-link>
<button #click="$getRoute('logout')">Logout</button>
The value passed in getRoute is the name of the route in Laravel when you use Route::get(...)->name('my.custom.route.name') in web.php
Use command php artisan route:list to list all route names of Resources you have created that you can use for $getRoute() in Vue.
On a side note as an extra added bonus, you can return custom JSON data for a newly authenticated user by editing Auth/LoginController.php and adding the following method:
public function authenticated(Request $request, $user)
{
return response()->json([
'redirect' => session()->pull('url.intended')
]);
}
You can expand on this and return many addition credentials.
For starters, I am passing back an intended redirect path that actually belongs to Vue router. The Vue application has already pushed the user to the login screen but Laravel also has a snapshot in session of where the user was going before this.$router.push('login') occurred.
Vue.prototype.$Laravel = window.Laravel;
Vue['Laravel'] = window.Laravel;
logMeIn() {
this.$root.isAuthenticating = true;
axios.post(this.$getRoute('login.send'),this.input).then(response => {
this.Laravel.myArray = response.data.myArray;
this.$router.push(response.data.redirect);
}).catch(error => {
this.$root.isAuthenticating = false;
ErrorHandler(this,error);
});
},

Angular2 event called only on SOME of the elements

So i have a Modal Component with a form in it, this component is used for both creating an entry to the DB and editing an existing one.
It has a subscription option to the onSubmit event, which is being executed on a successful submit.
What happens for some reason is that some of this component's element subscription executes and some won't, and it looks like those on the "create-mode" will and those on the "edit-mode" wont.
Code Section
CreateOrUpdateTransactionComponent:
#Component({
selector: 'create-update-transaction',
templateUrl: './CreateOrUpdateTransaction.html',
providers: [AccountTransactionsService]
})
export class CreateOrUpdateTransactionComponent {
closeResult: string;
modalRef: NgbModalRef;
#Input() transaction: Transaction = new Transaction();
#Input() isCreate: boolean;
#Output() onSubmit: EventEmitter<void> = new EventEmitter<void>();
constructor(private modalService: NgbModal,
private transactionsService: AccountTransactionsService) {}
sendTransaction(): void{
let localModalRef = this.modalRef;
this.transactionsService.createOrUpdateTransaction(this.transaction, (isSuccessful)=>{
if (isSuccessful) {
this.onSubmit.emit(); //<--- The problem is here
localModalRef.close();
}
});
}
}
The HTML:
<table>
<caption>Account Transactions</caption>
<thead>
// Omitted thead
</thead>
<template let-transaction ngFor [ngForOf]="accountTransactions" let-i="index">
<tr data-toggle="collapse" [attr.data-target]="'#'+i">
// Omitted <td>s
<td> //<----These updateTransactions() are not being executed
<create-update-transaction [isCreate]="false" [transaction]="transaction" (onSubmit)="updateTransactions()"></create-update-transaction>
</td>
</tr>
<div class="container collapse" [attr.id]="i">
// some content
</div>
</template>
</table>
<create-update-transaction [isCreate]="true" (onSubmit)="updateTransactions()"></create-update-transaction>
//<---- This updateTransactions() successfully executes
Notice
If I only display one row in the table not using ngFor (keeping the call to the back-end to update the DB), it works perfectly fine.
Any idea why would this happen?
Thanks in advance!
Update1
Debugging i could notice that when on the create-mode the this.onSubmit.observers is an array with one observer and on the edit-mode its an array with 0 observers, so thats the problem. any idea why?
Update2
Debugging again and found that the this in this.transactionsService.createOrUpdateTransaction... is fine and its' onSubmit.observers contains 1 observer, before reaching the callback's code, in which the this.onSubmit.observers is an array of 0 observers
AccountTransactionsService:
#Injectable()
export class AccountTransactionsService{
private loggedBankAccount: number;
private queryObservable: ObservableQuery;
constructor(private userManagingService: UserManagingService) {
this.loggedBankAccount = userManagingService.getLoggedBankAccountNumber();
this.queryAccountTransactions();
}
queryAccountTransactions(): void{
this.queryObservable = // querying the back-end
}
createOrUpdateTransaction(transaction: Transaction, callback: (isSuccessfull: boolean) => void): void{
let isSuccessful: boolean = false;
client.mutate(/*inserting the backend*/).then((graphQLResult) => {
const { errors, data } = graphQLResult;
if (data) {
console.log('got data', data);
isSuccessful = true;
}
if (errors) {
console.log('got some GraphQL execution errors', errors);
}
callback(isSuccessful);
}).catch((error) => {
console.log('there was an error sending the query', error);
callback(isSuccessful);
});
}
getAccountTransactions(): ObservableQuery{
return this.queryObservable;
}
}
Notice
If i just execute the callback give to the AccountTransactionService.createOrUpdateTransaction (removing the call to the back-end to actually update the DB) it works perfectly fine and all the subscribers to this onSubmit event are being called.
this Console image
Set the null as a parameter :
this.onSubmit.emit(null);
So I found out the case was that the data the ngFor is bound to was being replaced by a new instance as I updated the backend, hence, it rerendered it's child Components causing reinitializing (destory + init) of them, which made the instance of the Component to be overwritten.
In order to solve this issue i have changed the querying of the accountTransaction to be only one time querying, on initializing of the parent component, and never refetching again (which triggers the rerendering).
Im displaying to the client the changes only if they succeeded on the server side, and if they failed i use a backup of the data, so the client is kept update of the real state of the server WITHOUT refetching
For the future lookers to come:
The key to the problem was that the Parent Component's *ngFor depended on data that was changing in the Child Components, causing reinitializing of the *ngFor (the Child Components) BEFORE finishing executions of the Child Components methods.
Hope it'll be helpful to somebody :)

$http.get in Vue.js does not work when view is accessed from resource controller in Laravel

I am facing a confusing JavaSript problem with Vue.js in a Laravel project: The same page works if I route to it directly from routes.php, but it does not if I use the resource controller.
routes.php:
Route::resource('foo', 'FooController');
get('foo_create', function () {
return view('foo.create'); // works
});
get('foobar_to_fetch', function () {
return App\Node::all(); // some json
});
FooController.php:
class FooController extends Controller
{
public function create()
{
return view('foo.create'); // broken
}
}
views/foo/create.php:
<div id="foo">
<table>
<tr v-repeat="foobar">
<td>{{ id }}</td>
<td>{{ name }}</td>
</tr>
</table>
<pre>{{ $data | json }}</pre>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.11.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-resource/0.1.16/vue-resource.min.js"></script>
<script src="http://localhost/foo/resources/views/foo/create.js"></script>
views/foo/create.js:
new Vue({
el: '#foo',
data: {
foobar: []
},
ready: function() {
this.fetchFoobar();
},
methods: {
fetchFoobar: function () {
alert('no problem until here');
this.$http.get('foobar_to_fetch', function (foobar_fetched) {
alert(foobar_fetched.length);
this.foobar = foobar_fetched;
})
}
}
});
If I go to /foo_create I see the two alerts and the table with the fetched data. If I go to /foo/create (which is of course what I want) I see only the first alert and nothing more.
I found out that it works in both cases if I give $http.get the full path:
this.$http.get('http://localhost/foo/public/index.php/foobar_to_fetch', function (foobar_fetched) {
But how is it possible that the same page behaves differently, depending on how I got there?
Where does $http.get look if it is told to look for 'foobar_to_fetch' and the page is accessed as /foo/create? And is there a relative path that would work in this case?
Try:
methods: {
fetchFoobar: function () {
alert('no problem until here');
this.$http.get('/foobar_to_fetch', function (foobar_fetched) {
alert(foobar_fetched.length);
this.foobar = foobar_fetched;
})
}
}
depending on where you triggered this get request, it will change the url because 'foobar_to_fetch' is a relative url and '/foobar_to_fetch' is absolute compared to the domain name.
You can also detect this by opening the network console ( on Chrome ) and check out the headers sent by the Ajax request.

Categories

Resources