I am using Laravel 9 and Livewire 2.x. Pagination links are not working correctly; it only changes one item. I tried to change the link on the address bar like /search?query=example&page=2, and that worked perfectly.
Component Class
use Livewire\Component;
use App\Models\Product;
use Livewire\WithPagination;
class Search extends Component
{
use WithPagination;
protected $paginationTheme = 'bootstrap';
public $query;
protected $queryString = ['query'];
public function render()
{
$products = $this->searchQuery($this->query);
return view('livewire.products.search', [
'products' => $products->paginate(6),
])->extends('layouts.buyer')->section('content');
}
public function mount()
{
}
public function searchQuery($input){
return Product::where('name','like','%' . $input . '%' );
}
}
View
<div class="container my-5">
#if($products->isNotEmpty())
<div class="row g-4">
#foreach($products as $product)
<div class="col-lg-3">
#livewire('products.buyer-product-item', ['product' => $product])
</div>
#endforeach
</div>
#else
<div class="alert alert-danger">Product Was Not Found !!!</div>
#endif
<nav class="mt-5" aria-label="Page navigation example">
{{ $products->links() }}
</nav>
</div>
Hey After viewing the console I found there is an error.
Cannot read property 'fingerprint' of null
After including the key for the nested component the issue was solved.
#livewire('products.buyer-product-item', ['product' => $product], key($product->id))
Related
I want to add a toolbar inside website, the toolbar change inside component on each page. For now, I have this but I want my toolbar to be like this. How could i make this toolbar to update depend on the page the user go ?
The toolbar would be in the MainLayout and need to change content with a switch (not the best option I think) or is it possible to give new content to MainLayout from the page content ?
This is the code for the banner component :
<div class="extend-space" style="left:#($"-{Convert.ToInt32(offsetX)}px")">
<div class="banner" style="left:#(Convert.ToInt32(offsetX)+"px");width:#(Convert.ToInt32(width)+"px");">
<div class="banner-title">
#if (Icon != null)
{<i id="banner-title-icon" class="icon fas fa-#Icon"></i>}
<h3 class="title">#Title</h3>
</div>
<div class="toolbar">
<span id="arrow-left" class="scrollable" onclick="lastTool()">
<i class="fas fa-angle-left arrow"></i>
</span>
<span id="toolbar">
#ChildContent
</span>
<span id="arrow-right" class="scrollable" onclick="nextTool()">
<i class="fas fa-angle-right arrow"></i>
</span>
</div>
</div>
ChildContent should be a list of buttons with function onclick on it so this is the part that need to update on each page.
I add an example of how I use it on a page :
<XLBanner Title="Catégories" Icon="sitemap">
<XLButton Icon="plus" Content="#SharedLocalizer["Add"]" OnClickFunction="#AddCategorie" />
<XLButton Icon="save" Content="#SharedLocalizer["Save"]" OnClickFunction="#Save" disabled="#(!UnsavedChanges)" />
<XLButton Icon="redo" Content="#SharedLocalizer["Reset"]" OnClickFunction="#DeleteUnsavedChanges" disabled="#(SelectedCategorie == null)" />
<XLButton Icon="trash-alt" Content="#SharedLocalizer["Remove"]" OnClickFunction="#SuppCategorie" disabled="#(SelectedCategorie == null)" />
<XLButton Icon="copy" Content="#SharedLocalizer["Copy"]" OnClickFunction="#CopyCategorie" disabled="#(SelectedCategorie == null)" />
<XLButton Icon="download" Content="#SharedLocalizer["Export"]" OnClickFunction="#Export" /
</XLBanner>
What would be needed to update is the XLButton and the OnClickFunction.
My banner has differents tools depend on the page exemple dashboard page, exemple categorie page
If I understand the question correctly a version of this should work for you.
Basically:
Create a simple service that holds the menu data and has an event that is raised whenever the menu data changes and register it.
Use a DynamicComponent in Layout that plugs into the service.
Trigger StateHasChanged on the Layout whenever the service raises a menu change event.
Set the menu you want in each page in OnInitialized.
Two "Menus" to work with:
Menu1.razor
<h3>Menu1</h3>
Menu2.razor
<h3>Menu2</h3>
A simple LayoutService
public class LayoutService
{
public Type MenuControl { get; private set; } = typeof(Menu1);
public Dictionary<string, object>? MenuParameters { get; private set; }
public event EventHandler? MenuChanged;
public void ChangeMenu(Type menu)
{
this.MenuControl = menu;
MenuChanged?.Invoke(this, EventArgs.Empty);
}
public void ChangeMenu(Type menu, Dictionary<string, object> parameters)
{
this.MenuParameters = parameters;
this.MenuControl = menu;
MenuChanged?.Invoke(this, EventArgs.Empty);
}
}
registered in Program.cs:
builder.Services.AddScoped<LayoutService>();
MainLayout.razor
#inherits LayoutComponentBase
#inject LayoutService LayoutService;
#implements IDisposable
<PageTitle>BlazorApp1</PageTitle>
<DynamicComponent Type=this.LayoutService.MenuControl Parameters=this.LayoutService.MenuParameters />
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
About
</div>
<article class="content px-4">
#Body
</article>
</main>
</div>
#code {
protected override void OnInitialized()
=> this.LayoutService.MenuChanged += this.MenuChanged;
private void MenuChanged(object? sender, EventArgs e)
=> this.InvokeAsync(StateHasChanged);
public void Dispose()
=> this.LayoutService.MenuChanged -= this.MenuChanged;
}
And example page:
#page "/"
#inject LayoutService LayoutService
Page Content
#code {
protected override void OnInitialized()
{
this.LayoutService.ChangeMenu(typeof(Menu1));
}
Don't get too focused on the layout as a single entity that you have to use for every page in the whole site. You can have as many Layout components as you want, and you can nest them just like you would with any class and derived class.
https://blazor-university.com/layouts/nested-layouts/
I have rows of <textarea />s and want to handle the onfocus and onblur events and pass along the row data. Cannot get it to work at all.
<div>
#foreach (Person p in People){
<textarea rows="4" onfocus="#hasFocus(p)" onblur="#lostFocus(p)">p.Name</textarea>
}
</div>
public void hasFocus(Person p) {
...
}
The events are not triggering.
This works for me:
<div>
#foreach (Person p in People){
<textarea rows="4"
#onfocus="#( () => hasFocus(p) )"
#onblur="#( () => lostFocus(p) )">
p.Name
</textarea>
}
</div>
According to docs, the event attribute name is #on{eventname}.
Then use a lambda to pass your person parameter.
Use #onfocus and #onblur
<textarea rows="4" #onfocus="hasFocus" #onblur="lostFocus">p.Name</textarea>
and
public void hasFocus(FocusEventArgs args)
{
}
public void lostFocus(FocusEventArgs args)
{
}
If you use only onfocus it will be an html attribute and not an eventlistener.
To pass your person object you need an action like this
#onfocus="(args) => hasFocus(p)"
I do not know the code to add the user, selected in a People Picker from the pnp.sp library.
I've tried the below code example (by using State) but this I understand is not saving the users selection.
private _getPeoplePickerItems() {
pnp.sp.web.siteUsers.get().then((data) =>{
this.setState({
DeptContact: data
});
});
}
And the people picker in the render:
<PeoplePicker
context={this.props.context}
titleText="People Picker"
personSelectionLimit={3}
groupName={''}
showtooltip={false}
isRequired={false}
disabled={false}
selectedItems={this._getPeoplePickerItems}
showHiddenInUI={false}
principalTypes={[PrincipalType.User]}
resolveDelay={1000}
/>
</div>
</div>
I expect a user to be able to enter a user into the people picker and resolve it and then submit it. This user is then added to a 'Person' column in a sharepoint list.
My test code for your reference(react framework).
import * as React from 'react';
import styles from './PnpReact.module.scss';
import { IPnpReactProps } from './IPnpReactProps';
import { escape } from '#microsoft/sp-lodash-subset';
import pnp from "#pnp/pnpjs";
import { PeoplePicker, PrincipalType } from "#pnp/spfx-controls-react/lib/PeoplePicker";
export interface IDefaultData{
PeoplePickerDefaultItems:string[];
}
export default class PnpReact extends React.Component<IPnpReactProps, IDefaultData> {
public constructor(props: IPnpReactProps,state: IDefaultData){
super(props);
this.state = {
PeoplePickerDefaultItems:[]
};
}
//get users from peoplepicker
private _getPeoplePickerItems(items: any[]) {
console.log(items);
}
public componentDidMount(){
this.GetDefaultUsers();
}
private GetDefaultUsers() {
pnp.sp.web.siteUsers.get().then((items: any[]) =>{
var defaultUsers:string[]=[];
//get last 2 users
for(var i=items.length-1;i>items.length-3;i--){
defaultUsers.push(items[i].Email);
}
this.setState({
PeoplePickerDefaultItems:defaultUsers
});
});
}
public render(): React.ReactElement<IPnpReactProps> {
return (
<div className={ styles.pnpReact }>
<div className={ styles.container }>
<div className={ styles.row }>
<PeoplePicker
context={this.props.context}
titleText="People Picker"
personSelectionLimit={3}
groupName={''}
showtooltip={false}
isRequired={false}
disabled={false}
selectedItems={this._getPeoplePickerItems}
defaultSelectedUsers={this.state.PeoplePickerDefaultItems}
showHiddenInUI={false}
principalTypes={[PrincipalType.User]}
resolveDelay={1000}
/>
<div className={ styles.column }>
<span className={ styles.title }>Welcome to SharePoint!</span>
<p className={ styles.subTitle }>Customize SharePoint experiences using Web Parts.</p>
<p className={ styles.description }>{escape(this.props.description)}</p>
<a href="https://aka.ms/spfx" className={ styles.button }>
<span className={ styles.label }>Learn more</span>
</a>
</div>
</div>
</div>
</div>
);
}
}
Fail to make navigation in Blazor.net (Core 3.0 alpha 6) dynmic (loaded by Http Get Method)
I tried to include in "MainLayout.razor" file of default blazor project template the following dynamic loop.
the #body content was loaded correctly but navigation menu remains empty...
#inherits LayoutComponentBase
#using Pegasus.Shared
#inject HttpClient Http
<div class="sidebar">
#if (categories == null)
{
<p><em>Loading...</em></p>
}
else
{
#foreach (var category in categories)
{
<div>#category.Name</div>
}
}
</div>
<div class="main">
<div class="top-row px-4">
About
</div>
<div class="content px-4">
#Body
</div>
</div>
#code{
gb_Category[] categories;
protected override async Task OnInitAsync()
{
Console.WriteLine("Test");
var storeId = new Guid("c72d9757-98d5-4da2-89ea-2ffe44490162");
categories = await Http.GetJsonAsync<gb_Category[]>($"api/Category/actives/{storeId}");
}
}
No error message but empty navigation menu and I don't know why exaclty right now...
Same code runs if I put it into #page "/" but within #body of the MainLayout...
Goal is to load navigation items dynamic via Http and afterwards navigate through main data (#page's).
Thx
I am trying get all components on page and set them constraints. All components have id .I try to use IdSpace to get all ids on page but how to get Idspace ? I used
Window main ;
Page page ;
page = main.getPage(); // This is null
How to get IdSpace ?
Where did you write the code ?
My suggestion is to apply a composer to the root component ,
and iterate all the components inside.
Here's a runnable sample for iterating all the component and setvalue for all the labels.
http://zkfiddle.org/sample/36t45ag/1-Iterate-all-the-components
And the code is here.
<window border="normal" title="hello" apply="pkg$.TestComposer">
<label ></label>
<vlayout>
<label ></label>
<label ></label>
<label ></label>
<div>
<label ></label>
<label ></label>
</div>
</vlayout>
</window>
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
iterate(comp);
}
public void iterate(Component comp){
/*
* do something for the component
*/
if(comp instanceof Label){
((Label)comp).setValue("Found label!");
}
List<Component> list = comp.getChildren();
for(Component child:list){
iterate(child);
}
}