Instant Search with keyup vuejs and laravel - vue.js

I tried to build an instant search using vuejs and laravel 5.3 but somehow It wont work with no errors showing
Controller (fullcode https://pastebin.com/6mQ4eWTf) :
public function index(Request $request) {
$search = $request->search;
$items = Staff::where('nama', 'LIKE', '%'.$search.'%')->paginate(5);
$response = [
'pagination' => [
'total' => $items->total(),
'per_page' => $items->perPage(),
'current_page' => $items->currentPage(),
'last_page' => $items->lastPage(),
'from' => $items->firstItem(),
'to' => $items->lastItem()
],
'data' => $items
];
staff.js method (fullcode https://pastebin.com/NDxzqsyp) :
methods: {
getVueItems: function (page) {
this.$http.get('/staffitems?page=' + page + '&search=' + this.search).then((response) => {
this.$set('items', response.data.data.data);
this.$set('pagination', response.data.pagination);
});
setTimeout(this.getVueItems, 5000);
},
Blade (fullcode https://pastebin.com/6uDZRryE) :
<input v-on:keyup.enter="getVueItems" type="text" class="form-control" name="search" placeholder="Cari..." v-model="search"/>
Routes :
Route::get('/staffcrud', 'StaffController#Crud');
Route::resource('/staffitems', 'StaffController');
The data correctly shown (tested by getting json response from /staffitems?page=1&search=jon with or without search value), but somehow when I do type words to search in input search column, nothing happened as soon as i finished typing, probably event handling in blade are wrong or my method in staff.js any solution for this?

You should add the debounce in your mounted() method:
mounted() {
this.getVueItems = _.debounce(this.getVueItems, 5000); // i'm using lodash here.
}

To build a really effective instant database search you should consider using pusher and laravel echo together with vuex
You can check out this out >>> Ethiel Adiassa's Live Search Tutorial with laravel and pusher

In your blade template use only:
v-on:keyup='vueGetItems'
Because your instant search is firing only on enter key up. I hope this will work or contact me to get full working code.

Related

Laravel API Eloquent Where Clause with Vue not working

My question is, why doesn't my where clause work?
I use a Laravel API for my vue (with vuex) project.
Here's the Controller function
public function specific_client(Request $request) {
$id = $request->id;
return JsonResource::collection(
Measurement::where('client_id', '=',$id)
->with(['clients', 'measurement_data'])->get());
}
I also use vuetify, this is how I get the client_id :
<v-select v-model="cnr" :items="clients" item-text="clientnumber" item-value="id" :hint="cnr" solo></v-select>
My store.js :
fetchClientMeasurements({commit}, cnr) {
axios.post("http://localhost:8000/api/clientnr", cnr)
.then(response => {
console.log(response.data.data);
console.log(cnr);
commit("setMeasurements", response.data.data);
});
},
My API Route :
Route::post('clientnr', [MeasurementController::class, 'specific_client']);
When I console log "cnr" I get back the correct ID but I don't get any data back. If I replace $id in the where clause I do get back the correct information. I feel like it is a stupid mistake I made somewhere, but that's what I'm here for.
In your axios request, you need to label the id parameter.
axios.post("http://localhost:8000/api/clientnr", cnr)
should be
axios.post("http://localhost:8000/api/clientnr", {id: cnr})

Twitter typeahead.js not working in Vue component

I'm trying to use Twitter's typeahead.js in a Vue component, but although I have it set up correctly as tested out outside any Vue component, when used within a component, no suggestions appear, and no errors are written to the console. It is simply as if it is not there. This is my typeahead setup code:
var codes = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('code'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: contextPath + "/product/codes"
});
$('.typeahead').typeahead({
hint: true,
highlight: true,
minLength: 3
},
{
name: 'codes',
display: 'code',
source: codes,
templates: {
suggestion: (data)=> {
return '<div><strong>' + data.code + '</strong> - ' + data.name + '</div>';
}
}
});
I use it with this form input:
<form>
<input id="item" ref="ttinput" autocomplete="off" placeholder="Enter code" name="item" type="text" class="typeahead"/>
</form>
As mentioned, if I move this to a div outside Vue.js control, and put the Javascript in a document ready block, it works just fine, a properly formatted set of suggestions appears as soon as 3 characters are input in the field. If, however, I put the Javascript in the mounted() for the component (or alternatively in a watch, I've tried both), no typeahead functionality kicks in (i.e., nothing happens after typing in 3 characters), although the Bloodhound prefetch call is made. For the life of me I can't see what the difference is.
Any suggestions as to where to look would be appreciated.
LATER: I've managed to get it to appear by putting the typeahead initialization code in the updated event (instead of mounted or watch). It must have been some problem with the DOM not being in the right state. I have some formatting issues but at least I can move on now.
The correct place to initialize Twitter Typeahead/Bloodhound is in the mounted() hook since thats when the DOM is completely built. (Ref)
Find below the relevant snippet: (Source: https://digitalfortress.tech/js/using-twitter-typeahead-with-vuejs/)
mounted() {
// configure datasource for the suggestions (i.e. Bloodhound)
this.suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
identify: item => item.id,
remote: {
url: http://example.com/search + '/%QUERY',
wildcard: '%QUERY'
}
});
// get the input element and init typeahead on it
let inputEl = $('.globalSearchInput input');
inputEl.typeahead(
{
minLength: 1,
highlight: true,
},
{
name: 'suggestions',
source: this.suggestions,
limit: 5,
display: item => item.title,
templates: {
suggestion: data => `${data.title}`;
}
}
);
}
You can also find a working example: https://gospelmusic.io/
and a Reference Tutorial to integrate twitter typeahead with your VueJS app.

"Controller method not found." Laravel error. How can I figure out what method was called that it couldn't find?

I started using Laravel a month ago. Trying to make a page with one form on it and I keep getting a "Controller method not found." error. There is only one form on the page and that method is being called fine and making the query that I want. It does redirect to the same page with that form, /dashboard/edit/module.
It would be awesome if I could see what method is not being found or where it is in my code. Any ideas how I can do this? Someone mentioned App::error or messing with Log::error in global.php but I can't get any more information shown.
Edit:
I found that the form action is only being found if I upload a video to the form. Other then that it gives me the same error. Here is the controller method of this form. The page with this form is /dashboard/edit/module and I would like to reload that page after this runs.
public function postSection(){
$path = public_path() . "/videos/";
$filename = uniqid() . '.mp4';
$file_path = $path . $filename;
$moduleID = Input::get('moduleID');
if(!Input::hasFile('video')){
return Redirect::back()->with('error', 'Must enter a valid video file.');
}else{
try {
Input::file('video')->move($path, $filename);
$section = New Section;
$section->module_id = $moduleID;
$section->video = $file_path;
$section->save();
//I was trying to use this instead of View::make to stop form resubmission
//return Redirect::to('/dashboard/edit/module')->with('moduleID', $moduleID);
return View::make('forms.editMod')->with('moduleID', $moduleID);
}catch ( Exception $e){
return Redirect::back()->with('error', 'Unable to save the video file.');
}
}
}
Edit: And my routes.php
//Route for the home page
Route::get('/', function(){ return View::make('home'); });
//Controllers
Route::controller('dashboard/edit', "ContentController");
Route::controller('user', "UserController");
Route::group(array('before'=> 'auth|dash'), function(){
//dashboard home
Route::get('/dashboard', function(){ return View::make('dashboard');});
//dashboard subs
Route::get('dashboard/modules', function(){ return View::make('forms.module');});
Route::get('dashboard/users', function(){ return View::make('users');});
Route::get('dashboard/reporting', function(){ return View::make('reporting');});
//user pages
Route::get('dashboard/register', function(){ return View::make('forms.register');});
Route::post('dashboard/edit/user', array('before'=>'csrfajax', function(){
$id = Input::get('userID');
$user = User::find($id);
return View::make('forms.editUser')->with('user', $user);
}));
});
And the form...
{{ Form::open(array('files' => true, 'action' => 'ContentController#postSection', 'id' => 'sectionform')) }}
<fieldset>
{{ Form::label('Video (mp4)') }}
{{ Form::hidden('moduleID', $module->id)}}
{{ Form::file('video', array('id' => 'video', 'class' => 'form-control')) }}<br>
{{ Form::submit('Save') }}
</fieldset>
{{ Form::close() }}
In routes.php, to reach this method you're using Route::controller, which results in RESTful routing. If the browser's HTTP request used the POST action, you'd correctly be routed to public function postSection(){}. But if the HTTP request used GET, which is the default for web browsing, especially when no data is being submitted by the browser, Laravel would be looking for a method called public function getSection(){}. This would explain why when you're uploading something (i.e. using POST), you reach the page, but otherwise you don't.
See the official documentation for the full low-down on implicit routing: http://laravel.com/docs/controllers#implicit-controllers

why using viewSimple renderer in Phalcon corrupts main renderer

First I declare simple view like this:
$di->set('viewSimple', function() {
$view = new \Phalcon\Mvc\View\Simple();
$view->setViewsDir('../app/views/');
$view->registerEngines(array(
".volt" => 'volt'
));
return $view;
});
then to generate html email, I use it as following:
public function renderHTMLEmail($template_name, $template_params) {
$content = $this->viewSimple->render("emails/$template_name", $template_params);
return $this->viewSimple->render("emails/master", array( 'content' => $content) );
}
My emails are being generated just fine, but whenever I call my renderHTMLEmail function actual page rendering is somehow corrupted and page appears totally blank (I have to use redirect as workaround). This is a mystery to me as main view renderer is completely different object. Can I prevent this?
Or does anybody have recommended method of generating arbitrary pieces of html outside of main view rendering process which would not interfere with it? There is couple of similar questions on SO, but none of solutions work. They either don't generate my email or they corrupt main view.
Found a solution, when registering the "\Phalcon\Mvc\View\Simple" component into the DI container make sure to use a new volt instance otherwise it will join up with the application's \Phalcon\Mvc\View volt instance somehow. This is what lot (https://stackoverflow.com/users/1672165/lot) was suggesting in the comments.
Code sample:
public function getTemplate($name, $params)
{
$parameters = array_merge(array(
'publicUrl' => $this->getDI()->getApp()->url->host . $this->getDI()->getApp()->url->baseUri
), $params);
$app = $this->getDI()->getApp();
$this->getDI()->set('simpleView', function() use ($app) {
$view = new \Phalcon\Mvc\View\Simple();
$view->setViewsDir(APP_DIR . $app->application->viewsDir);
$view->registerEngines(array(
".volt" => function ($view, $di) {
$volt = new \Phalcon\Mvc\View\Engine\Volt($view, $di);
$volt->setOptions(array(
'compiledPath' => APP_DIR . '/cache/volt/',
'compiledSeparator' => '_',
'compiledExtension' => '.compiled'
));
$compiler = $volt->getCompiler();
$compiler->addFunction('is_a', 'is_a');
return $volt;
}
));
return $view;
});
/* #var $simpleView \Phalcon\Mvc\View\Simple */
$simpleView = $this->getDI()->get('simpleView');
foreach ($parameters as $key => $value) {
$simpleView->{$key} = $value;
}
$html = $simpleView->render('emails/' . $name, $parameters);
return $html;
}
Sample was pulled straight from our working app so may need some re-working but should help solve the original issue.
This works for me using \View as the application DI registered view component which renders controller action volt views and the above \View\Simple with fresh volt registration successfully emails and allows the original view with volt to carry on.
This was asked a while ago however i've not seen a working solution anywhere else.
For completeness I've registered a github issue with the findings: https://github.com/phalcon/cphalcon/issues/3096
I recently had to deal with a similar problem, I'm not certain where you're falling short, but one important thing was to use a separate view from the one used by the application – you're already doing this. See if the following works.
protected function renderView($view, $template, array $data = null)
{
return $view
->reset()
->pick('emails/' . $template)
->setVars($data)
->start()
->render(null, null)
->finish()
->getContent();
}
public function renderHTMLEmail($template_name, $template_params)
{
$content = $this->render($template_name, $template_params);
return $this->render('master', array('content' => $content));
}

Passing variables through the URL in Laravel

I am very new to Laravel and I am trying to fully understand how routes work. I want to pass variables through the URL. I understood how I do that, but my problem is a little different:
routes.php
Route::get("/user/{user}", array(
'as' => 'profile-user',
'uses' => 'ProfileController#user'
));
ProfileController.php
class ProfileController extends BaseController{
public function user($user) {
$user = User::where('username', '=', Session::get('theuser') );
if($user->count()) {
$user = $user->first();
return View::make('layout.profile')
->with('user', $user);
}
return App::abort(404);
}
}
In my View, simply:
{{ $user->username }}
Now my problem: This works somewhat, but after the press of a button, this URL will look something like this:
[this is not a link](http://localhost/tutorial/public/index.php/user/%7Buser%7D)
If I get to edit the URL to something like
[this is not a link](http://localhost/tutorial/public/index.php/user/Serban)
it does the same thing. But I do not wish to manually edit the URL. How can I get the second URL line without editing?
When you are building your form, pass the user argument like this
Form::open(array('route' => array('route-name-for-update', $user->username)))
You may also use Form Model Binding (read more on the doc):
Form::model($user, array('route' => array('user.update', $user->username)))
Here, user.update is the route name that requires a route defined with this name for update method.
Meanwhile, a more interesting approach for me is to do something like this:
Route::group(array('prefix' => 'user/{user}'), function()
{
Route::get("/{char}", array(
'as' => 'profile-user',
'uses' => 'ProfileController#user'));
}
);
Controller:
public function user($user, $char) {
$user = User::where('username', '=', Session::get('theuser') );
$char = Character::where('char_name', '=', 'Cucu' );
if($user->count()) {
$user = $user->first();
$char = $char->first();
return View::make('layout.profile')
->with('user', $user)
->with('char', $char);
}
return App::abort(404);
}
It doesn't seem for me to be able to do something like this at the push of a button
$logged_user = Session::get('theuser');
return Redirect::route('profile-user', $logged_user);
because I can't put 2 parameters in the Redirect function. This code will get the URL
[this is not a link](http://localhost/tutorial/public/index.php/user/SerbanSpire)
which obviously doesn't exist
The correct URL would be
[this is not a link]http://localhost/CaughtMiddle/tutorial/public/index.php/user/SerbanSpire/Cucu)
where SerbanSpire is the $user and Cucu is $char. How can I get the correct URL?
When you link to a route you will need to pass the param to the route like so
{{ route('profile-user', 'Serban') }}