I'm still new with AngularJS directives, now I'm trying to use columnize plugin but without success. This answer is a good start but won't solve my problem because Categories.get() fetches data asynchronously from the server and setting a timeout doesn't seem the solution at all.
<div columnize>
<ul>
<li ng-repeat="data in Categories.get()">
<a ng-href="{{ data.uri }}">{{ data.name }}</a>
<ul>
<li ng-repeat="data in data.categories" ng-include="'list.html'"></li>
</ul>
</li>
</ul>
</div>
Edit: Categories service.
app.factory('Categories',function ($http) {
var categories = [],
URL = '/categories';
if (categories.length == 0) {
$http.get(URL).success(function (data) {
categories = data;
})
}
return {
get: function () {
return categories;
},
....
};
Based on the source of my data how should make the columnize directive with the columnize jQuery plugin?
Related
ok I am stumped and I know its going to be something stupid. So let me explain more in detail. The code above is what I believe should work for pagination from a Laravel controller to a vue component. I get that I am not fully accounting for pagination and I can handle that later but the first results are not available for use when I do this and I do not understand where my syntax is wrong.
Vue:
<ul role="list" class="space-y-4">
<li
v-for="item in allActivities"
:key="item.id"
class="bg-gray-800 px-4 py-6 shadow sm:p-6 sm:rounded-lg"
>
{{ item.description }}
</li>
</ul>
Mounted:
mounted() {
axios
.get("/activity")
.then((response) => (this.allActivities = response.data));
},
Controller
public function index()
{
$activity = Activity::paginate(10);
return $activity;
}
If in the v-if I change it to allActivities.data it refreshes to show the data until I reload the page and get id not found.
If I change the axios to response.data.data it works, but I lose pagination.
IM stuck
response.data is the result from laravel
response.data.data if the data key in the result which is a list of your models
response.data will contain these keys if using
current_page
data
first_page_url
from
last_page
last_page_url
links
next_page_url
path
per_page
prev_page_url
to
total
I did not fully understand the problem. If you want to change page, send the value of the page you want to display to the fetchActivities() method.
EDIT!
<script>
export default {
data() {
return {
allActivities: {
data: [],
total: null
}
}
},
methods: {
async fetchActivities(page = 1){
const {data:activities} = await axios.get(`/activity?page=${page}`)
this.allActivities.data.push(activities.data)
this.allActivities.total = activities.total
}
},
created() {
this.fetchActivities()
}
}
</script>
<template>
<div v-if="allActivities.data.length > 0">
<ul role="list" class="space-y-4">
<li
v-for="(item, index) in allActivities.data"
:key="index"
class="bg-gray-800 px-4 py-6 shadow sm:p-6 sm:rounded-lg"
>
{{ item.description }}
</li>
</ul>
</div>
</template>
In a Laravel project I'm attempting to use Ajax to populate a DIV and then use #foreach to populate the store gallery. If possible I wanted to use a Server side content generation strategy. However, something silly is going on that I can't figure out-- I'm getting the following error:
$user_query is undefined
Make the variable optional in the blade template. Replace {{ $user_query }} with {{ $user_query ?? '' }}
How is this (#foreach ($user_query as $item)) suppose to be constructed in the blade?
routes
Route::get('/ajaxStoreRequest', 'StoreController#ajaxStoreRequest');
Route::post('/ajaxStoreRequest/post', 'StoreController#ajaxStoreRequestPost');
Here is the StoreController.php:
<?php
namespace App\Http\Controllers;
use App\Coin;
use Illuminate\Http\Request;
use App\DataTables\StoresDataTable;
use App\DataTables\StoresDataTableEditor;
use Illuminate\Support\Facades\DB;
class StoreController extends Controller
{
public function ajaxStoreRequest()
{
return view('store.index');
}
public function ajaxStoreRequestPost(Request $request)
{
$user_query= DB::table('coins')
->select('id','year', 'mint', 'series', 'rating', 'rating_group','photo_link1', 'for_sale_price')
->where('for_sale', '=', 1)
->where('sold', '=', 0)
->where('series', '=', $request->series)
->where('year', '=', $request->year)
->where('mint', '=', $request->mint)
->where('rating_group', '=', $request->rating_group)
->get();
return response()->json($user_query);
}
}
a DD($user_query); shows the following array format
Illuminate\Support\Collection {#552
#items: array:1 [
0 => {#555
+"id": 2
+"year": 1878
+"mint": "Philadelphia"
+"series": "Morgan Dollar"
+"rating": "Ungraded"
+"rating_group": "PCGS"
+"photo_link1": "editor/20200328_002220_888888966_732020162248388.jpg"
+"for_sale_price": "44"
}
]
}
Here is the Ajax script from the bottom of the store/index.blade.php
<script type="text/javascript">
$.ajaxSetup({
beforeSend: function(xhr, type) {
if (!type.crossDomain) {
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
}
},
});
$(".btn-submit").click(function(e){
e.preventDefault();
var series = $("#series option:selected").val();
var year = $("input[name=year]").val();
var mint = $("#mint option:selected").val();
var rating_group = $("#rating_group option:selected").val();
var price_filter = $("input[name=price_filter]:checked").val();
$.ajax({
type:'POST',
url:'/ajaxStoreRequest/post',
data:{series:series, year:year, mint:mint, rating_group:rating_group, price_filter:price_filter},
success:function(result){
$('.collapse').collapse('show');
$('#user_query').html(result);
}
});
});
</script>
Here is the HTML further up in the store/index.blade.php
<div class="row row-deck js-gallery collapse">
<div class="user_query" id="user_query">
#foreach ($user_query as $item) <!-- THIS IS THE ERROR -->
<div class="col-md-6 col-xl-4">
<div class="block">
<div class="options-container">
#if ($item->photo_link1)
<img class="img-fluid options-item" src="storage/{{$item->photo_link1}}" style="width:300px; height:300px" alt="">
#else
<img class="img-fluid options-item" src="images/no-user-profile-picture-1200x900.jpg" style="width:300px; height:300px" alt="">
#endif
<div class="options-overlay bg-black-75">
<div class="options-overlay-content">
<a class="btn btn-sm btn-primary img-lightbox" href="gallery/{{$item->id}}">
View
</a>
<a class="btn btn-sm btn-light" href="javascript:void(0)">
<i class="fa fa-plus text-success mr-1"></i> Add to cart
</a>
</div>
</div>
</div>
<div class="block-content">
<div class="mb-2">
<div class="h4 font-w600 text-success float-right ml-1">${{$item->for_sale_price}}</div>
<a class="h4" href="be_pages_ecom_store_product.html">{{$item->year}}-{{$item->mint}}</a>
</div>
<p class="font-size-sm text-muted">{{$item->series}} ({{$item->rating}} {{$item->rating_group}})</p>
</div>
</div>
</div>
#endforeach
</div>
</div>
I'm attempting to use Ajax to populate a DIV and then use #foreach
This is fundamentally backwards. store/index.blade.php is rendered server side, so any data returned from your ajax call must be added to the DOM using JavaScript.
api.php
Route::get('/products', 'ProductsController#index');
Query:
$products = DB::table('sizes')
->join('products', 'sizes.id', '=', 'products.sizes')
->join('categories', 'products.category', '=', 'categories.id')
->select('products.*', 'categories.catname', 'categories.catimage', 'categories.catdescription', 'sizes.size')
->where([['products.is_active', '=', 1],['categories.is_active', '=', 1],])
->orderBy('products.id', 'ASC')
->paginate(5);
return $products;
Vue component:
<div v-for="product in products.data" :key="product.id">
<h1>{{ product.name }}</h1>
</div>
<pagination :data="products" #pagination-change-page="getResults"></pagination>
methods: {
getResults(page = 1) {
this.$url.get('products/results?page=' + page)
.then(response => {
console.log(response)
this.products = response.data;
});
}
}
The initial load of products works, it shows 5 products and shows pagination. Whenever I try to click a new page from the pagination, I end up with multiple errors.
CORS(which I don't see how since my app is completely public) and two network errors
from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
net::ERR_FAILED
Uncaught (in promise) Error: Network Error
Is there something I'm missing here? Am I supposed to make another endpoint that handles pagination or should this be handled from the same exact endpoint where it fetches initial pagination?
1. Copy and paste this code in a new component, like 'Pagination.vue'
<template>
<nav aria-label="...">
<ul class="pagination justify-content-center">
<li class="page-item" :class="{ disabled: pagination.current_page <= 1 }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(1)" >First page</a>
</li>
<li class="page-item" :class="{ disabled: pagination.current_page <= 1 }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(pagination.current_page - 1)"><i class="fa fa-arrow-left"></i></a>
</li>
<li class="page-item" v-for="(page,index) in pages" :key="page" :class="isCurrentPage(page) ? 'active' : ''">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(page)">{{ page }}
<span v-if="isCurrentPage(page)" class="sr-only">(current)</span>
</a>
</li>
<li class="page-item" :class="{ disabled: pagination.current_page >= pagination.last_page }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(pagination.current_page + 1)"><i class="fa fa-arrow-right"></i></a>
</li>
<li class="page-item" :class="{ disabled: pagination.current_page >= pagination.last_page }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(pagination.last_page)">Last Page</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
props:['pagination', 'offset'],
methods: {
isCurrentPage(page){
return this.pagination.current_page === page
},
changePage(page) {
if (page > this.pagination.last_page) {
page = this.pagination.last_page;
}
this.pagination.current_page = page;
this.$emit('paginate');
}
},
computed: {
pages() {
let pages = []
let from = this.pagination.current_page - Math.floor(this.offset / 2)
if (from < 1) {
from = 1
}
let to = from + this.offset -1
if (to > this.pagination.last_page) {
to = this.pagination.last_page
}
while (from <= to) {
pages.push(from)
from++
}
return pages
}
}
}
</script>
2. Make it global in your js/app.js file,
Vue.component('pagination', require('./components/Pagination.vue').default);
3. In the vue component, below to the data, set the pagination component like this, you can cange the offset as much you can
<pagination v-if="pagination.last_page > 1"
:pagination="pagination"
:offset="7"
#paginate="getItems()">
</pagination>
4. Set current page to 1,
data(){
return{
items: [],
pagination: {
current_page: 1,
},
}
},
5. Make a method to send the page number and collect paginated data,
getItems(){
axios.get('api/items?page='+this.pagination.current_page)
.then(response => {
this.items = response.data.data;
this.pagination = response.data.meta;
});
},
6. Make sure you return data paginated data with resource collection,
public function index(){
return new GeneralCollection(Item::with('category')->orderBy('name')->paginate(10));
}
***if you, don't have the collection file, make one , like 'GeneralCollection',
php artisan make:resource GeneralCollection
then, include it on the controllers where you want to return collected data,
use App\Http\Resources\GeneralCollection;
7. Congrats !
This is my vue
new Vue({
el: '#notificationMenu',
data: {
showModal: false,
patients: [],
duepatients: []
},
created: function(){
this.getPatients();
this.addUsers();
},
methods: {
getPatients: function(){
$.getJSON("{{route('api_patients')}}", function(patients){
this.patients = patients;
}.bind(this));
setTimeout(this.getPatients, 1000);
},
addUsers: function(){
this.$http.get('/govaccine/addUsers', '').then((response) => {
}, (response) => {
this.formErrors = response.data;
});
setTimeout(this.addUsers, 10000);
},sendSMS: function(val){
$.getJSON('sendsms/' + val,function(duepatient){
this.duepatients = duepatient;
}.bind(this));
}
}
});
and this is my html
<ul class="notifications-list" v-for="patient in patients">
<li class="item no-data">You don't have notifications</li>
<!-- use the modal component, pass in the prop -->
<modal v-if="showModal" #close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
</modal>
<a v-on:click="sendSMS(patient.due_date)">
<li class="item js-item " data-id="5" >
<div class="details">
<strong>#{{ patient.total}} patients</strong> are due for immunization on #{{patient.due_date}}
<span class="date">{{-- #{{patient.created_at}} --}}</span>
</div>
<button type="button" class="button-default button-dismiss js-dismiss">×</button>
</li>
</a>
</ul>
If I loop through vue js data using v-for how do i pass the data each loop in {{route('patient.show', parameter)}} so i can put a parameter on it.
or is there anyway to go to the controller and go to the other page?
yes you should use router-link. complete example here.
if I got you question wrong just tell me.
I'm new to Vue.js, trying to create a single page blog just to get my feet wet with the vue/laravel combo, and I am stuck when it comes to deleting a "story" from the array of "stories" I am working with. I know the routes are fine because the story actually deletes, no errors are thrown, but the deleted story will remain in the array until I refresh the page. From what I've read elsewhere, the code I have implemented should update the array immediately. I have attached the relevant parts of my blade view, vue.js file, and controller. Thanks in advance!
JS (VUE)
new Vue({
el: '[vue-news]',
search: "",
data: {
stories: ''
},
ready: function() {
// GET request
this.$http.get('/app/news/get', function(data, status, request) {
// set data on vm
this.$set('stories', data);
// console.log(data);
}).error(function(data, status, request) {
// handle error
});
},
methods: {
// DELETE STORY FROM ARRAY
deleteNews: function(id, index) {
this.$http.delete('app/news/' + id + '/delete').success(function(response) {
this.stories.$remove(index);
swal("Deleted!", "Your news story has been deleted.", "success");
}).error(function(error) {
console.log(error);
swal(error);
});
}
}
});
BLADE
<section vue-news>
<div class="row news-row">
<div class="columns large-9 medium-9 small-12">
<article data-id="#{{ story.id }}" class="story panel" v-for="story in stories | filterBy search" track-by="$index">
<h1>#{{ story.title }}</h1>
<p>#{{ story.content }}</p>
<p>#{{ story.created_at }}</p>
<p>#{{ story.id }}</p>
<p>#{{ story.timestamp }}</p>
Read More...
<div class="options">
<a #click="editNews" href="#">
<i class=" edit fa fa-pencil-square-o"></i>
</a>
{{-- DELETE NEWS BUTTON --}}
<a #click.prevent="deleteNews(story.id, $index)" href="#">
<i class="delete fa fa-trash-o"></i>
</a>
</div>
</article>
</div>
<div class="columns large-3 medium-3 small-12">
<input type="text" v-model="search">
</div>
</div>
</section>
CONTROLLER
public function delete($id)
{
return response()->json(News::destroy($id));
}
The $remove method now treats the argument as an item to search for rather than an index. In other words, try this out:
Delete method:
deleteNews: function(id, story) {
this.$http.delete('app/news/' + id + '/delete').success(function(response) {
this.stories.$remove(story);
swal("Deleted!", "Your news story has been deleted.", "success");
}).error(function(error) {
console.log(error);
swal(error);
});
}
HTML section:
<a #click.prevent="deleteNews(story.id, story)" href="#">
<i class="delete fa fa-trash-o"></i>
</a>
Source: https://github.com/vuejs/vue/releases
Edit: Since you are passing the entire story item, you can actually just pass one argument and shorten the code to this:
Vue:
deleteNews: function(story) {
this.$http.delete('app/news/' + story.id + '/delete').success(function(response) {
this.stories.$remove(story);
swal("Deleted!", "Your news story has been deleted.", "success");
}).error(function(error) {
console.log(error);
swal(error);
});
}
HTML:
<a #click.prevent="deleteNews(story)" href="#">
<i class="delete fa fa-trash-o"></i>
</a>