Related
I have a route where when I refresh the page I get only the JSON info for that page. (only on refresh F5). The rest of the routes are ok. I am not sure what I am doing wrong.
web.php
Route::get('/persons', 'MyController#index');
Route::post('/record/{personId?}', 'MyController#create'); // this is the one that don't work on refresh
Route::get('/record/{id}', 'MyController#getRecord');
Route::delete('/record/{id}', 'MyController#destroy');
Route::get('/lookups', 'LkpController#index');
Route::post('/validate', 'MyController#getValidation');
//Routes for VueJs
Route::get('/{any}', function () {
return view('welcome');
})->where('any','^(?!api).*$')->name('home');
router.js
const routes = [
{
path: "/",
name: "Home",
component: Home,
meta: { requiresAuth: true }
},
{
path: "/record",
name: "Record",
component: Record,
meta: { requiresAuth: true }
},
{
path: "/record/:id",
name: "View Record",
component: require ('./components/AddPerson').default,
meta: { requiresAuth: true }
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;
The problem is that you are putting all your routes in web.php, and you have the same routes for your Vue SPA as your Laravel application.
You should put your API routes in your web/api.php file that way they will be automatically prefixed with the 'api' route.
The route that is returning JSON data is not the one you pointed out, it is the next one:
Route::get('/record/{id}', 'MyController#getRecord'); // this is the one that don't work on refresh
This is because your Vue router is pointing to the exact same route:
{
path: "/record/:id",
name: "View Record",
component: require ('./components/AddPerson').default,
meta: { requiresAuth: true }
}
Both routes point to yourwebsite.com/record/{id}, but on refresh you make a brand new request to your Laravel application, that means your not in your Vue application anymore and your browser will load whatever Laravel will tell them first, in this case it will be the first route in the routes/web.php file:
Route::get('/record/{id}', 'MyController#getRecord');
Edit: This is how you should do it if you cannot use API routes due to authentication:
You must make sure that your don't have duplicate routes between your Vue router and your Laravel routes, you can just prefix them with something that makes sense to you.
Route::prefix('prefix')->group(function () {
Route::get('/persons', 'MyController#index');
Route::post('/record/{personId?}', 'MyController#create');
Route::get('/record/{id}', 'MyController#getRecord');
Route::delete('/record/{id}', 'MyController#destroy');
Route::get('/lookups', 'LkpController#index');
Route::post('/validate', 'MyController#getValidation');
});
//Routes for VueJs
Route::get('/{any}', function () {
return view('welcome');
})->where('any','^(?!api).*$')->name('home');
In this example, the route you are having trouble with will now be prefixed with 'prefix' yourwebsite.com/prefix/record/{id}, you can change it to whatever you need it to be.
I wanna display data with specefic ID in laravel using vuejs.
I get the ID from the link but it seems that there is no request sent to the controller.
api.php :
<?php
use Illuminate\Http\Request;
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::resource('user','API\UserController');
Route::resource('departement','API\DepartementController');
Route::resource('specialite','API\SpecialiteController')->parameters(['specialite'=>'id']);
my controller :
public function show($id)
{
$specialite=Specialite::with('dep')->findOrFail($id);
$spec = Specialite::with('dep')->where('id',$specialite)->get();
return $spec;
}
my view :
<script>
export default {
data(){
return{
specialites:{},
form: new Form({
id:'',
name:'',
user_id:'',
bio:''
}),
id:0,
}
},
methods: {
loadspecialite(){
//axios.get('api/user').then(({data})=>(this.enseignants=data.data));
axios.get('api/specialite/'+this.id).then(response=>{this.specialites=response.data;});
},
created() {
this.id=this.$route.params.id;
this.loadspecialite();
Fire.$on('AfterCreate',()=>{
this.loadspecialite();
})
}
}
</script>
Vue-router:
let routes = [
{ path: '/Profile/:id', component: require('./components/a.vue').default },
]
thank you.
hope tou will help me.
Firstly, I don't see how this.id would carry the id from the router as created is not guaranteed to have been fired AFTER the router has routed.
Your loadspecialite should get the value from the currentRoute when called and i think the var is slightly wrong:
let id = this.$router.currentRoute.params.id;
Your route resource should be:
Route::resource('specialite','API\SpecialiteController');
The request uri would be:
axios.get(`/api/specialite/${id}`).then(...)
You can find out the exact uri path for all registered routes in Laravel by using an SSH terminal to run console command: php artisan route:list
This should produce the following:
+--------+-----------+----------------------------------+------------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+----------------------------------+------------------------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | api/specialite | api.specialite.index | App\Http\Controllers\API\ApplicationController#index | api,auth:api |
| | POST | api/specialite | api.specialite.store | App\Http\Controllers\API\ApplicationController#store | api,auth:api |
| | GET|HEAD | api/specialite/create | api.specialite.create | App\Http\Controllers\API\ApplicationController#create | api,auth:api |
| | GET|HEAD | api/specialite/{specialite} | api.specialite.show | App\Http\Controllers\API\ApplicationController#show | api,auth:api |
| | PUT|PATCH | api/specialite/{specialite} | api.specialite.update | App\Http\Controllers\API\ApplicationController#update | api,auth:api |
| | DELETE | api/specialite/{specialite} | api.specialite.destroy | App\Http\Controllers\API\ApplicationController#destroy | api,auth:api |
| | GET|HEAD | api/specialite/{specialite}/edit | api.specialite.edit | App\Http\Controllers\API\ApplicationController#edit | api,auth:api |
P.S. there is no need to create a form object if you are not sending any attached files, Laravel and axios will revert to use JSON by default with ajax requests.
Laravel will return JSON object by default in response to a JSON ajax call direct from a resource on your controller:
function show($id) {
return Specialite::findOrFail($id);
}
Fail will return a 400+ header that in turn can be handled by axsios .catch
.catch( error => { console.log(error.response.message) } )
Laravel from validation messages would be accessible via:
.catch( error => { console.log(error.response.data.errors) } )
Axios will post an object/array as a JSON request:
data() {
return {
form: {
id:'',
name:'',
user_id:'',
bio:''
},
}
}
...
axios.post('/api/specialite',this.form).then(...);
I do believe that the code is functioning fine. It is a formatting error in the vue component object. Basically your created() handler is in the due methods, thus it won't be handled when the created event is done.
// your code snippet where there is an issue
methods: {
loadspecialite(){
//axios.get('api/user').then(({data})=>(this.enseignants=data.data));
axios.get('api/specialite/'+this.id).then(response=>{this.specialites=response.data;});
}, // end of loadspecialite
created() {
this.id=this.$route.params.id;
this.loadspecialite();
Fire.$on('AfterCreate',()=>{
this.loadspecialite();
})
} // end of created
} //end of methods
What you should do is just remove the created() out of methods and also check the syntax of the function again.
const Foo = {
template: '<div>foo</div>'
}
const Bar = {
template: '<div><span> got {{form}}</span></div>',
data() {
return {
specialites: {},
form: 'fetching...',
id: 0,
}
},
methods: {
loadspecialite() {
// test method for getting some data
axios.get('https://httpbin.org/anything/' + this.id)
.then(response => {
this.form = response.data.url;
}).catch(error => {
console.error(error)
})
},
}, // <- this is the end of methods {}
/**
* Created method outside of methods scope
*/
created() {
this.id = this.$route.params.id;
this.loadspecialite();
}
}
// rest is vues demo router stuff
const routes = [{
path: '/foo',
component: Foo
},
{
path: '/bar/:id',
component: Bar
}
]
const router = new VueRouter({
routes // short for `routes: routes`
})
const app = new Vue({
router
}).$mount('#app')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue Routed</title>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<style>
button {
padding: 0.75rem;
background: #eee;
border: 1px solid #eaeaea;
cursor: pointer;
color: #000
}
button:active {
color: #000;
box-shadow: 0px 2px 6px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div id="app">
<h1>Hello App!</h1>
<p>
<span> Click a button </span>
<router-link to="/foo"><button>Go to Foo</button></router-link>
<router-link to="/bar/3"><button>Go to Where it will get the data</button></router-link>
</p>
<!-- route outlet -->
<!-- component matched by the route will render here -->
<router-view></router-view>
</div>
</body>
</html>
All thing has set well, just your show method should respond in JSON:
use Illuminate\Http\Response;
function show($id) {
result = Specialite::findOrFail($id);
return response()->json($result,Response::HTTP_OK);
}
I have an issue trying to use the show method in a resource controller in laravel 5.7. I'm working with VueJS and axios to http requests. The index method is called instead show method. I'm pass do the call with get method and the ID param.
Routes:
POST | products | products.store | App\Http\Controllers\ProductoController#store | web |
| | GET|HEAD | products/create | products.create | App\Http\Controllers\ProductoController#create | web |
| | DELETE | products/{product} | products.destroy | App\Http\Controllers\ProductoController#destroy | web |
| | PUT|PATCH | products/{product} | products.update | App\Http\Controllers\ProductoController#update | web |
| | GET|HEAD | products/{product} | products.show | App\Http\Controllers\ProductoController#show | web |
| | GET|HEAD | products/{product}/edit | products.edit | App\Http\Controllers\ProductoController#edit | web |
web.php:
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController#index')->name('home');
//Productos
Route::resource('products', 'ProductoController');
Route::get('duplicar/{param?}', 'ProductoExtController#duplicar');
Route::post('mostrarProductos', 'ProductoExtController#mostrarProductos');
Route::post('guardarValoresEditados', 'ProductoExtController#guardarValoresEditados');
Route::get('imprimirListadoPrecios', 'ProductoExtController#imprimirListadoPrecios');
Route::post('mostrarProductosStickers', 'ProductoExtController#mostrarProductosStickers');
Route::post('imprimirStickers','ProductoExtController#imprimirStickers');
//Colecciones
Route::resource('colecciones', 'ColeccionController');
//Categorias
Route::resource('categorias', 'CategoriaController');
//Crostas
Route::resource('crostas', 'CrostaController');
Route::get('crostasaut/{param?}', 'CrostaExtController#autocomplete');
//Folias
Route::resource('folias', 'FoliaController');
Route::get('foliasaut/{param?}', 'FoliaExtController#autocomplete');
Route::get('{path}', 'HomeController#index')->where('path','([A-z\d-\/_.]+)?');
And this is my http-request with axios:
editarProducto(pDatosFila){
axios.get('products', {
params: {
id: pDatosFila.f014_id
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
}
And to end this is the response:
current_page: 1
data: [{f014_id: 25, f014_id_old: null, f014_nombre: "carlos ruales", f014_deleted: null,…},…]
first_page_url: "http://localhost:8000/products?page=1"
from: 1
last_page: 1
last_page_url: "http://localhost:8000/products?page=1"
next_page_url: null
path: "http://localhost:8000/products"
per_page: 10
prev_page_url: null
to: 7
total: 7
What you are currently doing is :
axios.get('products', {
params: {
id: pDatosFila.f014_id
}
})
is equivalent to : axios.get('/products?id=id') (NOT THE RIGHT FORMAT)
What you need to do is :
axios.get('/products/product_id')
So, you can do something like :
axios.get('products/'+ pDatosFila.f014_id)
and this will work. :)
PS : always check the network tab in developer tools in browser.
Reference : axios
Sorry for the long post. I am new to Laravel. I am trying to add an online hand signature functionality to a controller. I have chosen to use the javascript library https://github.com/szimek/signature_pad. I want to use an ajax call to the controller to save the signature image through php but i get a http 500 error whenever I try make the ajax call. Any help would be greatly appreciated as I'm all out of ideas.
In my laravel log
[2018-12-27 22:18:12] local.ERROR: BadMethodCallException: Method [signdoc] does not exist. in D:\...\vendor\laravel\framework\src\Illuminate\Routing\Controller.php:82
On the view, I am able populate a signature pad with the button functionalities. This is the javascript from the view where the ajax called is made. I followed this guide https://github.com/szimek/signature_pad/issues/167
<script type="text/javascript">
console.log("test");
var signaturePad = new SignaturePad(document.getElementById('signature-pad'), {
backgroundColor: '#c6c6c6',
penColor: 'rgb(0, 0, 0)'
});
var signButton = document.getElementById('sign');
var cancelButton = document.getElementById('clear');
var undoButton = document.getElementById('undo');
signButton.addEventListener('click', function (event) {
event.preventDefault();
if(!signaturePad.isEmpty()){
//TODO
//save picture
var data = signaturePad.toDataURL('image/png');
var imagen = data.replace(/^data:image\/(png|jpg);base64,/, "");
var fdata = new FormData();
fdata.append('imgData', imagen);
console.log(data);
console.log(window.document.location);
var url =
jQuery.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
type:"POST",
dataType: "json", //datatype expected to be returned
url:"/transaction/modify/signdoc",
data: fdata,
sucess: function (data) {
if(data && data.status == "success"){
bootbox.alert("signed document!!" + data.info);
}
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(XMLHttpRequest.responseText);
console.log(textStatus);
console.log(errorThrown);
}
}).done(function(msg) {
// Image saved successfuly.
alert('success');
}).fail(function (data) {
alert('failed');
});
}
});
cancelButton.addEventListener('click', function (event) {
signaturePad.clear();
});
undoButton.addEventListener('click', function (event) {
var data = signaturePad.toData();
if (data) {
data.pop(); // remove the last dot or line
signaturePad.fromData(data);
}
});
</script>
In my route
Route::get('report/transactionsreporting','TransactionsController#reportindex');
Route::get('report/AverageNightlyRate','TransactionsController#reportindex');
Route::get('report/transactionsProportionRate','TransactionsController#reportproportionrate');
Route::get('report/transactionsAverageRental','TransactionsController#reportaveragerental');
Route::get('report/transactionsGroupSize','TransactionsController#reportgroupsize');
Route::get('report/transactionsInquiryCheckInDate','TransactionsController#reportinquirycheckindate');
Route::get('report/transactionsClosed','TransactionsController#reporttranclosed');
Route::get('report/transactionsRate','TransactionsController#reportindex');
Route::get('report/inquiryCheckInDate','InquirysController#reportinquirycheckindate');
Route::post('transaction/modify/signdoc', 'TransactionsController#signdoc');
//this is the route to the function in controller
Route::get('transaction/getbc/{transactionID}','TransactionsController#getBC');
Route::get('transaction/showAll', 'TransactionsController#show');
Route::post('transaction/delete', 'TransactionsController#delete');
Route::get('transaction/modify/{id}', 'TransactionsController#modify');
Route::post('transaction/modify/store', 'TransactionsController#update');
Route::post('transaction/modify/addcashflow', 'TransactionsController#addcashflow');
Route::post('transaction/modify/modifycashflow', 'TransactionsController#modifycashflow');
Route::post('transaction/modify/deletecashflow', 'TransactionsController#deletecashflow');
Route::post('transaction/modify/deletedoc', 'TransactionsController#deletedoc');
Route::get('transaction/confirm','TransactionsController#confirmInquiry');
Route::post('transaction/add','TransactionsController#add');
Route::get('/transaction/download/{id}', 'TransactionsController#getDownload');
In my transaction controller
class TransactionsController extends Controller
{
public function signdoc(Request $request){
//TODO
// save image
echo "signdoc";
log::info("sign doc controller asdfsadfsafsadfsad");
return response()->json(['status'=>'success','info'=>' entered sign doc function!!'])
->header('Content-Type','json');
}
}
in the terminal I check the php artisan route:list and the routes seems correct
| web |
| | POST | transaction/add | | App\Http\Controllers\TransactionsController#add
| web,auth |
| | GET|HEAD | transaction/confirm | | App\Http\Controllers\TransactionsController#confirmInquiry
| web,auth |
| | POST | transaction/delete | | App\Http\Controllers\TransactionsController#delete
| web,auth |
| | GET|HEAD | transaction/download/{id} | | App\Http\Controllers\TransactionsController#getDownload
| web,auth |
| | GET|HEAD | transaction/getbc/{transactionID} | | App\Http\Controllers\TransactionsController#getBC
| web,auth |
| | POST | transaction/modify/addcashflow | | App\Http\Controllers\TransactionsController#addcashflow
| web,auth |
| | POST | transaction/modify/deletecashflow | | App\Http\Controllers\TransactionsController#deletecashflow
| web,auth |
| | POST | transaction/modify/deletedoc | | App\Http\Controllers\TransactionsController#deletedoc
| web,auth |
| | POST | transaction/modify/modifycashflow | | App\Http\Controllers\TransactionsController#modifycashflow
| web,auth |
| | POST | transaction/modify/signdoc | | App\Http\Controllers\TransactionsController#signdoc
| web,auth |
| | POST | transaction/modify/store | | App\Http\Controllers\TransactionsController#update
| web,auth |
| | GET|HEAD | transaction/modify/{id} | | App\Http\Controllers\TransactionsController#modify
| web,auth |
| | GET|HEAD | transaction/showAll | | App\Http\Controllers\TransactionsController#show
| web,auth |
| | GET|HEAD | transactionDelete/showAll | | App\Http\Controllers\TransactionsDeleteController#show
| web,auth |
| | POST | updateOwner | | App\Http\Controllers\HouseOwnersController#updateOwner
| web,auth |
| | GET|HEAD | welcome | | Closure
| web |
Please use name in the route ... ->name('transaction.modify.sincdoc');
Route::post('transaction/modify/signdoc', 'TransactionsController#signdoc')->name('transaction.modify.sincdoc');
And the blade where you are writing the url use route clause
url:"{{route('transaction.modify.syncdoc')}}",
Please try this and let me know how it works :)
I was wondering if there's an elegant way to trigger the refetch of a query in react-apollo when a subscription receives new data (The data is not important here and will be the same as previous one). I just use subscription here as a notification trigger that tells Query to refetch.
I tried both using Subscription component and subscribeToMore to call "refetch" method in Query's child component but both methods cause infinite re-fetches.
NOTE: I'm using react-apollo v2.1.3 and apollo-client v2.3.5
here's the simplified version of code
<Query
query={GET_QUERY}
variables={{ blah: 'test' }}
>
{({ data, refetch }) => (
<CustomComponent data={data} />
//put subscription here? It'll cause infinite re-rendering/refetch loop
)}
<Query>
Finally I figured it out myself with the inspiration from Pedro's answer.
Thoughts: the problem I'm facing is that I want to call Query's refetch method in Subscription, however, both Query and Subscription components can only be accessed in render method. That is the root cause of infinite refetch/re-rendering. To solve the problem, we need to move the subscription logic out of render method and put it somewhere in a lifecycle method (i.e. componentDidMount) where it won't be called again after a refetch is triggered. Then I decided to use graphql hoc instead of Query component so that I can inject props like refetch, subscribeToMore at the top level of my component, which makes them accessible from any life cycle methods.
Code sample (simplified version):
class CustomComponent extends React.Component {
componentDidMount() {
const { data: { refetch, subscribeToMore }} = this.props;
this.unsubscribe = subscribeToMore({
document: <SUBSCRIBE_GRAPHQL>,
variables: { test: 'blah' },
updateQuery: (prev) => {
refetch();
return prev;
},
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const { data: queryResults, loading, error } } = this.props;
if (loading || error) return null;
return <WhatEverYouWant with={queryResults} />
}
}
export default graphql(GET_QUERY)(CustomComponent);
It's possible if you use componentDidMount and componentDidUpdate in the component rendered by the Subscription render props function.
The example uses recompose higher order components to avoid too much boilerplating. Would look something like:
/*
* Component rendered when there's data from subscription
*/
export const SubscriptionHandler = compose(
// This would be the query you want to refetch
graphql(QUERY_GQL, {
name: 'queryName'
}),
lifecycle({
refetchQuery() {
// condition to refetch based on subscription data received
if (this.props.data) {
this.props.queryName.refetch()
}
},
componentDidMount() {
this.refetchQuery();
},
componentDidUpdate() {
this.refetchQuery();
}
})
)(UIComponent);
/*
* Component that creates the subscription operation
*/
const Subscriber = ({ username }) => {
return (
<Subscription
subscription={SUBSCRIPTION_GQL}
variables={{ ...variables }}
>
{({ data, loading, error }) => {
if (loading || error) {
return null;
}
return <SubscriptionHandler data={data} />;
}}
</Subscription>
);
});
Another way of accomplishing this while totally separating Query and Subscription components, avoiding loops on re-rendering is using Apollo Automatic Cache updates:
+------------------------------------------+
| |
+----------->| Apollo Store |
| | |
| +------------------------------+-----------+
+ |
client.query |
^ +-----------------+ +---------v-----------+
| | | | |
| | Subscription | | Query |
| | | | |
| | | | +-----------------+ |
| | renderNothing | | | | |
+------------+ | | | Component | |
| | | | | |
| | | +-----------------+ |
| | | |
+-----------------+ +---------------------+
const Component =() => (
<div>
<Subscriber />
<QueryComponent />
</div>
)
/*
* Component that only renders Query data
* updated automatically on query cache updates thanks to
* apollo automatic cache updates
*/
const QueryComponent = graphql(QUERY_GQL, {
name: 'queryName'
})(() => {
return (
<JSX />
);
});
/*
* Component that creates the subscription operation
*/
const Subscriber = ({ username }) => {
return (
<Subscription
subscription={SUBSCRIPTION_GQL}
variables={{ ...variables }}
>
{({ data, loading, error }) => {
if (loading || error) {
return null;
}
return <SubscriptionHandler data={data} />;
}}
</Subscription>
);
});
/*
* Component rendered when there's data from subscription
*/
const SubscriptionHandler = compose(
// This would be the query you want to refetch
lifecycle({
refetchQuery() {
// condition to refetch based on subscription data received
if (this.props.data) {
var variables = {
...this.props.data // if you need subscription data for the variables
};
// Fetch the query, will automatically update the cache
// and cause QueryComponent re-render
this.client.query(QUERY_GQL, {
variables: {
...variables
}
});
}
},
componentDidMount() {
this.refetchQuery();
},
componentDidUpdate() {
this.refetchQuery();
}
}),
renderNothing
)();
/*
* Component that creates the subscription operation
*/
const Subscriber = ({ username }) => {
return (
<Subscription
subscription={SUBSCRIPTION_GQL}
variables={{ ...variables }}
>
{({ data, loading, error }) => {
if (loading || error) {
return null;
}
return <SubscriptionHandler data={data} />;
}}
</Subscription>
);
});
Note:
compose and lifecycle are recompose methods that enable easier a cleaner higher order composition.