Custom Blazor Server Component Not Invoking the OnClick Function - razor

I have created a layout component in the same directory as with the MainLayout component. The CustomLayout #inherits LayoutComponentBase and has #Body which gets the content to be rendered inside the layout.
As obviously expected, the layout component has its own css files and UI display is fine. I am only having a problem with the #onclick function. It cannot be invoked. Is there a way to trigger the function on button click?
The reason I want to do this is that I am creating a navigation bar which will have to show/hide a dropdown. I hope I can get some ideas on this. Appreciated.
I am calling a server function this way: #onclick="SomeFuction" or #onclick="SomeFuction" or #onclick="#(() => SomeFuction())"
LayoutComponent:
#inherits LayoutComponentBase
#Body
Home page referencing a custom layout:
[AllowAnonymous]
[Layout(typeof(CustomLayoutComponent))]
[AllowAnonymous] is just for allowing anonymous access.
I am using Blazor Server and .NET 6
Additional Information:
I have put all the code in one page so that it becomes easier to read and understand.
Here is the LayoutComponent razor component:
#inherits LayoutComponentBase
<div class="navbar" id="custom-navbar">
<div class="wrapper">
<a>Home</a>
<a>Service</a>
<button class="btn btn-primary" #onclick="ToggleProductsUI">Products</button>
</div> </div>
<!--Component to show Products UI Component. Scoped css for styling-->
<ProductsComponent TValue="string" UIState="#ProductsUIState"></ProductsComponent>
#code{
// state of the Products submenu Component
string ProductsUIState { get; set; }
// a control function for toggling the Products UI
// this function somehow is not invoked.
void ToggleProductsUI()
{
if (string.IsNullOrWhiteSpace(ProductsUIState))
{
ProductsUIState = "show-ui";
return;
}
ProductsUIState = string.Empty;
}
}
Here is the ProductsUI component code (I have removed the unnecessary event to avoid confusion. The component can be fully controlled by the parent):
public partial class ProductsComponent<TValue> {
[Parameter]
public string? UIState { get; set; } }
Blazor pages that will use the custom layout component will point to it like this:
[AllowAnonymous]
[Layout(typeof(CustomLayoutComponent))]
public partial class Index
{
}
This is working fine. The issue is in the CustomLayoutComponent when trying to invoke the ToggleProductsUI() function

I accidentally inluded a <body> element in the layout and as such, the <script src="_framework/blazor.server.js"></script> was not being run hence the click event was not being invoked. I removed the <body> element. I was misled by the default body{} style in the underlining scopped css. Resolved.

Related

Razor component tag helper not actually loading the razor component

I've been following the steps in this guide to set up Blazor components in my Razor app. I completed all the steps from the "Prepare the app" section of that guide, modifying the _Layout.cshtml & Startup.cs files and adding the _Imports.razor file. To test this, I'm just trying to implement a basic counter component.
I added the below code to MyApp/Components/Counter.razor:
<p>Current count: #currentCount</p>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
#code {
private int currentCount = 0;
[Parameter]
public int InitialValue { get; set; }
private void IncrementCount() => currentCount++;
protected override void OnParametersSet()
{
currentCount = InitialValue;
}
}
Then in MyApp/Pages/Counter.cshtml i have this:
#page
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#using Microsoft.AspNetCore.Components
#using Microsoft.AspNetCore.Components.Web
#using System.Net.Http
#using Microsoft.AspNetCore.Authorization
#using Microsoft.AspNetCore.Components.Authorization
#using Microsoft.AspNetCore.Components.Forms
#using Microsoft.AspNetCore.Components.Routing
#using Microsoft.JSInterop
#using MyApp
#using MyApp.Components
//This does not work--it appears exactly like this in the HTML when the page loads
<component type="typeof(Counter)" render-mode="ServerPrerendered" />
//this works as expected and loads the razor component
#(await Html.RenderComponentAsync<Counter>(RenderMode.ServerPrerendered))
Note that I copied all the using directives from the _Imports.razor file to see if that fixed things, but it didn't make a difference. My understanding is that the RenderComponentAsync function is outdated and the the "component" tag helper is the current way to use razor components. I'd also prefer to use that syntax since it's easier to pass parameters. Does anyone know what I'm missing to get it to work?
Welp, after messing around with this for hours I realized that my app was on Net Core 3.0 and the tag helper is only available in 3.1+. Updating MyApp.csproj to have 3.1 instead fixed it:
<TargetFramework>netcoreapp3.1</TargetFramework>

Theme service that affects all components and modals <ng-template let-modal> in Angular 6+

According to the post Toggle a class by a button from header component that affect main app component by Angular 6+
I created a theme service, it works fine for all components by toggling class 'dark-mode' in app.component.html, until I found out that all of my modals <ng-template let-modal> are under the <body>, of course these modals don't work.
So I turned to another solution from this answer: stackoverflow.com/a/59123790/6630524
I tried to inject DOCUMENT and Renderer2 into the header component and it works fine. But I see now the theme service is deprecated, so I put Renderer2 to the theme service (not in header component anymore), and it refused to work!
Could you help me to retain the theme service, and still be able to apply the theme to all the components (and these modals as well)?
It turned out no one answered for quite some time, so I decided to dig into the solution myself, see below FYI.
First, still keep using the theme service by putting Renderer2 into the theme.service.ts (previously set locally in header component):
#Injectable({
providedIn: 'root'
})
export class ThemeService {
public isDarkMode: boolean;
private renderer: Renderer2;
constructor(
#Inject(DOCUMENT) private document: Document,
rendererFactory: RendererFactory2
) { this.renderer = rendererFactory.createRenderer(null, null);
}
public toggleDarkMode() {
this.isDarkMode = !this.isDarkMode;
if (this.isDarkMode) {
this.renderer.addClass(this.document.body, 'dark-mode');
} else {
this.renderer.removeClass(this.document.body, 'dark-mode');
}
}
}
Second, you need to set a public variable in a constructor, here I choose the constructor of header.component.ts:
export class HeaderComponent implements OnInit {
/*
* Inject the theme service which will be called by our button (click).
* #param {ThemeService} themeService instance.
*/
constructor(public themeService: ThemeService) {}
ngOnInit() {}
}
Then put a button in the header.component.html to toggle your themes (Note: You can put the button anywhere in your project, along with the public variable defined in the corresponding abc.component.ts)
<button (click)="themeService.toggleDarkMode()"><i class="fa fa-moon"></i></button>
Now the public toggleDarkMode function is fired anytime the button clicked, then the modals and other components will reflect the change when theme service get updated by your defined dark-mode style, for example:
#lucky.dark-mode {
color: #a2b9c8;
background-color: #01263f!important;
}
And you might need this applied to all the <body> in index.html:
<body id="lucky">
<app-root></app-root>
</body>
Good luck and happy coding!

Problem with Styling and Visibility while showing modal in Angular 4 Application --

Facing problem with opening and displaying a modal in my Angular4 .NET Application. I would click a link and consecutively a modal would show. In my case the date link for invoice number [pl see the image].
I followed the approach shown here -- http://jasonwatmore.com/post/2017/01/24/angular-2-custom-modal-window-dialog-box
Now what I have currently is, my opaque screen blocking the background but the modal is not displaying as I was hoping for. Like this
I don't know why the modal didn't appear. I am guessing z-index problem maybe? Cause I do not see any console errors. So probably not angular code related matter. Most likely CSS is what I feel. My main app screen is divided into 2 segments as you can see, col-sm-3 and col-sm-9 body content.
Basically this is what I wrote to test my code.
my main app window layout -
<div class='container-fluid'>
<div class='row'>
<div class='col-sm-3'>
<nav-menu></nav-menu>
</div>
<div class='col-sm-9 body-content'>
<alert-component></alert-component>
<router-outlet></router-outlet>
</div>
</div>
</div>
my modal related html --
<div class="col-md-4" style="border-radius:8px; background:linear-gradient(50deg, #e1ecfa, #f2fbde); text-align:right; margin-left:12px; padding:10px;">
<b style="color:darkblue">Invoices issued to this customer</b>
<ul style="list-style:none" *ngFor="let i of iObj">
<li (click)="openInvoiceModal('custom-modal-1')" class="glyphicon glyphicon-arrow-right">
<a>
<b>{{i.inv_id}}, on {{i.inv_date}}</b>
</a>
</li>
</ul>
</div>
*** my test modal ***
<modal id="custom-modal-1">
<div class="modal">
<div class="modal-body">
<h1>Invoice Modal!</h1>
<p>
Home page text: Hello There!
</p>
<button (click)="closeModal('custom-modal-1');">
Close
</button>
</div>
</div>
<div class="modal-background"></div>
</modal>
typescript with this page --
openInvoiceModal(id: string) {
this.modalService.open(id);
}
closeInvoiceModal(id: string) {
this.modalService.close(id);
}
All the other files and code are the same as has been written in that link/tutorial. I tried experimenting at one place with z-index also. But it didn't serve the purpose. So I am baffled.
A few alterations in the modalcomponent file also according to my layout css etc, so I am posting it here.
export class ModalComponent implements OnInit, OnDestroy {
#Input() id: string;
private element: JQuery;
constructor(private modalService: ModalService, private el: ElementRef) {
this.element = $(el.nativeElement);
}
ngOnInit(): void {
let modal = this;
// ensure id attribute exists
if (!this.id) {
console.error('modal must have an id');
return;
}
this.element.appendTo('.container-fluid');
this.element.on('click', function (e: any) {
var target = $(e.target);
if (!target.closest('.modal-body').length) {
modal.close();
}
});
this.modalService.add(this);
}
// remove self from modal service when directive is destroyed
ngOnDestroy(): void {
this.modalService.remove(this.id);
this.element.remove();
}
open(): void {
this.element.show();
$('.container-fluid').addClass('modal-open');
}
// close modal
close(): void {
this.element.hide();
$('.container-fluid').removeClass('modal-open');
}
}
I am not sure why the modal itself is not showing. Although the opaque background is being called means - I am going the right way. ALMOST!
What am I missing? Where is the glitch? Surely it has to be some small tricky part that I am failing to grab! Kindly guide me.
Let me know if you need more code stubs from me to understand my scenario. I will be happy to share.
In anticipation,

how to pass values from controller to layout file in yii2

I'm trying to pass a variable value from controller to my main.php layout file but i keep getting error Undefined variable: contentWrapperBackground. below is my code
controller
public function actionList()
{
$contentWrapperBackground = "ff4400;";
$contentBackground = "3c8dbc;";
return $this->render('list', [
'contentWrapperBackground' =>$contentWrapperBackground,
'contentBackground' => $contentBackground,
]);
}
and in my layout file like this
<div class="content-wrapper" style="background-color:#<?=$contentWrapperBackground?>">
<!-- Content Header (Page header) -->
<!-- Main content -->
<section class="content" style="background-color:#<?=$contentBackground?>">
but i always get error Undefined variable: contentWrapperBackground. I'm trying to change the background color of for different pages. any help on this, and am also open to another idea on how to make this work thanks
don't use session for this!
Simple solution:
class Controller extends \yii\web\Controller
{
$public $contentWrapperBackground;
$public $contentBackground;
}
class YourController extends Controller
{
public function actionList()
{
$this->contentWrapperBackground = "ff4400;";
$this->contentBackground = "3c8dbc;";
return $this->render('list', []);
}
}
in your main layout
<div class="content-wrapper" style="background-color:#<?=Yii::$app->controller->contentWrapperBackground?>">
or another option
<div class="content-wrapper" style="background-color:#<?=$this->context->contentWrapperBackground?>">
you can set param in controller
Yii::$app->view->params['paramName'] = 'data';
get data in layout
$this->params['paramName'];

GWT uibinder autocorrect off

im using GWT uibinder method and my html contains a textbox like
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:idmw="urn:import:com.testing.wid.impl">
<g:HTMLPanel>
<table align="center" valign="center" height="25%">
<tr><td><g:TextBox ui:field='searchS' /></td></tr>
</table>
</g:HTMLPanel>
How can i TURN OFF autocorrect and autocapitalize for this Textbox??
i tried
<g:TextBox ui:field='searchS' autocapitalize="off" autocorrect="off"/>
but i get
[ERROR] Class TextBox has no appropriate setAutocorrect()
method Element <g:TextBox autocapitalize='off' autocorrect='off' ui:field='searchS'>
Any other way i can do this???
Thanks
As already pointed by #Boris Brudnoy there is no built-in way to do it with TextBox. Takin futher his suggestion it will be nice to extract this into new custom component (to simplify reuse and support):
Add new package (for example com.app.shared.customcontrol)
Add new CustomTextBox:
public class CustomTextBox extends TextBox {
public void setAutocomplete(String value){
this.getElement().setAttribute("autocomplete", value);
}
public void setAutocapitalize(String value){
this.getElement().setAttribute("autocapitalize", value);
}
}
Declare new namespace using UI binder and use your component:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:c="urn:import:com.app.shared.customcontrol">
<g:HTMLPanel ...>
<c:CustomTextBox ui:field="..." autocomplete="off" autocapitalize="off" />
</g:HTMLPanel>
</ui:UiBinder>
As alternative way if you want apply these settings system wide you can do it via constructor:
public class CustomTextBox extends TextBox {
public CustomTextBox() {
this.getElement().setAttribute("autocomplete", "off");
this.getElement().setAttribute("autocapitalize", "off");
}
....
}
What you've tried will not work since GWT doesn't translate UiBinder attributes directly into HTML element properties. Instead, as your error message hints, it looks up widget setter methods of the form set[UiBinder_attribute]. Since there is neither setAutocorrect nor setAutocapitalize method in the TextBox class, the errors you're getting are expected.
What you could do is drop to the element level and write something like this, e.g. in your widget's constructor:
public MyWidget() {
initWidget(uiBinder.createAndBindUi(this));
searchS.getElement().setProperty("autocapitalize", "off");
searchS.getElement().setProperty("autocorrect", "off");
}