lit-element - How to call an event from a slot button? - lit-element

element
import { LitElement, html } from 'lit-element';
class Component extends LitElement {
render () {
return html`
<slot name="activator">
<button #click="${this.openDialog}">Default Button</button>
</slot>
<custom-dialog></custom-dialog>
`;
}
openDialog () {
// code to open dialog
}
}
customElements.define('custom-dialog', Component);
index.html
<head>
<script type="module" src="src/custom-dialog.js"></script>
</head>
<body>
<custom-dialog>
<button slot="activator">Outside Button</button>
</custom-dialog>
</body>
Given the custom component above and my implementation on a simple html page. You'll notice that I'm using a slot button.
How do I call the openDialog() method using the slot button?
I checked docs for events but I found nothing relevant to this.
Thanks in advance.

You need a click event listener on the slot or some ancestor of the slot:
Try moving the #click binding to the slot element itself. click events bubble, so this will handle both the default slot content button and the slotted button from the light DOM. This might not work in ShadyDOM, so you may want to put the event listened on a wrapper element around the slot.
import { LitElement, html } from 'lit-element';
class Component extends LitElement {
render () {
return html`
<slot name="activator" #click="${this.openDialog}">
<button>Default Button</button>
</slot>
<custom-dialog></custom-dialog>
`;
}
openDialog () {
// code to open dialog
}
}
customElements.define('custom-dialog', Component);

Related

Applying class scoped CSS custom properties to web component

Is there a specific rule around how class scoped CSS custom properties cascade/are inherited with the shadow root? Let's say I have a default theme with custom properties scoped to :root and a dark theme scoped to a .dark-mode class, I can apply that class to the app-container component and my simple-button text color is set to blue. However, if I apply the dark-mode class directly to the simple-button as demonstrated, it inherits black from :root. It doesn't even pick up .dark-mode. I'm looking for some official docs that might explain this as well.
<!DOCTYPE html>
<head>
<script type="module" src="./app-container.js"></script>
<style>
:root {
--text-color: black;
}
</style>
<style>
.dark-mode {
--text-color: blue;
}
</style>
</head>
<body>
<app-container name="World"></app-container>
</body>
// app-container.js
import {html, css, LitElement} from 'lit';
import './simple-button.js';
export class AppContainer extends LitElement {
static styles = css`p { color: var(--text-color)}`;
static properties = {
};
constructor() {
super();
}
render() {
return html`
<p>App Container</p>
<simple-button class="dark-mode"></simple-button>
`;
}
}
customElements.define('app-container', AppContainer);
// simple-button.js
import {html, css, LitElement} from 'lit';
export class SimpleButton extends LitElement {
static styles = css`button { color: var(--text-color)}`;
static properties = {
};
constructor() {
super();
}
render() {
return html`<button>Button</button>`;
}
}
customElements.define('simple-button', SimpleButton);

Blazor, how can I change a component in MainLayout when I change page

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/

Want to make navigation dynamic in MainLayout (Blazor.net)

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

Create a Cancel button for Joomla 3.2 custom component

I am trying to build a Joomla 3.2 custom component and am having a difficult time getting the cancel button (admin section) to work. I've added the button, but when I click it I get the error:
0 Invalid controller: name='helloworld', format=''
This is for the component 'helloworld' and the view 'goodbye'. Could someone look at my files and tell me how to get the 'cancel' button to work? I just want it to close the current page and return to the default component page.
Thanks for any help.
/administrator/views/goodbye/view.html.php
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import Joomla view library
jimport('joomla.application.component.view');
/**
* HTML View class for the HelloWorld Component
*/
class HelloWorldViewGoodbye extends JViewLegacy
{
// Overwriting JView display method
function display($tpl = null)
{
$this->addToolbar();
// Display the view
parent::display($tpl);
}
protected function addToolbar()
{
JToolbarHelper::title('Race Results','tbar');
JToolbarHelper::cancel('helloworld.cancel', 'JTOOLBAR_CLOSE');
}
}
/administrator/views/goodbye/tmpl/default.php
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
?>
<h1>Cancel Race Results</h1>
<script type="text/javascript">
Joomla.submitbutton = function(task)
{
if (task == 'helloworld.cancel')
{
Joomla.submitform(task, document.getElementById('helloworld-form'));
}
}
</script>
<form id="helloworld-form" name="adminForm" method="post" action="<?php echo JRoute::_('index.php?option=com_helloworld&view=goodbye'); ?>">
<input type="hidden" name="option" value="com_helloworld" />
<input type="hidden" name="task" value="" />
<input type="hidden" name="view" value="goodbye" />
<?php echo JHtml::_('form.token'); ?>
</form>
/administrator/controllers/goodbye.php
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import Joomla modelitem library
jimport('joomla.application.component.controller');
/**
* HelloWorld Model
*/
class HelloWorldControllerGoodbye extends JControllerLegacy
{
public function edit() {
}
public function add() {
}
public function remove() {
}
public function save() {
}
public function apply() {
}
}
/administrator/models/goodbye.php
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import Joomla modelitem library
jimport('joomla.application.component.model');
/**
* HelloWorld Model
*/
class HelloWorldModelGoodbye extends JModelLegacy
{
}
depending on action you would like to achieve
Add to controller
public function cancel($key = null) {
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
$this->setRedirect(JRoute::_('index.php?option='.$this->option.'&view=name_of the_view' , false));
return true;
}
In view.html add condition to your button
if (condition when to use button)
{
JToolbarHelper::cancel('your_controller_name.cancel');
}
else
{
JToolbarHelper::cancel('your_controller_name', 'JTOOLBAR_CLOSE');
}
Change HelloWorldViewGoodbye to GoodbyeViewGoodbye and same other.

Allow page layout to hide control on master page

If I want to use my regular master page for a new page layout, except I don't want the main navigation menu control (a 3rd party control) to be visible, how can I let the page layout hide it? In asp.net I would expose a public property or method on the master page and then call it from the child page, but not sure what can be done in SharePoint since there is no code behind or discernible master page class.
I got it working like this but I'm not in love with the implementation.
On Master Page:
...
<c:Menu id="myMenu" runat="server" />
...
</form>
</body>
</html>
<script runat="server">
public bool IsConsumerNavVisible
{
set
{
myMenu.Visible = value;
}
}
</script>
On PageLayout:
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
System.Reflection.PropertyInfo pi = Page.Master.GetType().GetProperty("IsConsumerNavVisible");
pi.SetValue(Page.Master, false, null);
}
</script>
So I exposed a public property on the master page to set the visibility and then used reflection on the PageLayout to find & set that property. I tried putting the PageLayout code in just a <% %> script block and it executed but the menu would end up visible anyway. Putting it in a Page_Load event handler fixed that. If there is a better way I'm all ears.

Resources