I know I am able to add a class to a #Html.Actionlink by using, which acts on a single Actionlink at a time:
#Html.ActionLink("Add",
"UpdateNote",
"Notes",
new { id = 0, type = (int)THOS.Utilities.Enumerations.Enumerations.Note.RelatedApplicationType.Law, appid = ((ObjectModelLibrary.Law)ViewData["currentLaw"]).LawID, baseappid = ((ObjectModelLibrary.Law)ViewData["currentLaw"]).LawID }
new { #class = "btn btn-primary icon-edit"},
null)
However,
Is there a way of defining a class (much like adding a style to all divs in the css file like:
div{
color: red;
}
^ ^
| |
this way will act on *all* divs
instead of going
<div class="myClass"></div>
I can just write:
<div></div>
which will automatically have color:red included
would there be a way of defining a class for an ActionLink without going to each actionlink and typing #Class="myClass"
For Example
for adding styling for all button instances:
input[type="button"]{
background-color:red;
}
Can i do this with something like:
input[type="actionlink"]{
//styles for all actionlinks in project
}
and so all actionlinks can be written as:
#Html.ActionLink("Action","Controller")
and automatically include the styling stated in my css file?
I would do this the first way, but i've already ~100 made without defining a class, and don't fancy copy and pasting:
class="myClass"
ActionLink generates just normal anchors, so you can write:
a{
color:red;
}
But to get just ActionLinks you will need to call them by a class or a container.
Also may be a better way to do it is to create a custom ActionLink and put a default class inside and using this class you can do your selector, like this you will use this new Custom ActionLink and no need to copy paste classes.
public static class LinkExtensions
{
public static MvcHtmlString MyActionLink(
this HtmlHelper htmlHelper,
string linkText,
string action,
string controller
)
{
var currentAction = htmlHelper.ViewContext.RouteData.GetRequiredString("action");
var currentController = htmlHelper.ViewContext.RouteData.GetRequiredString("controller");
if (action == currentAction && controller == currentController)
{
var anchor = new TagBuilder("a");
anchor.Attributes["href"] = "#";
anchor.AddCssClass("currentPageCSS");
anchor.SetInnerText(linkText);
return MvcHtmlString.Create(anchor.ToString());
}
return htmlHelper.ActionLink(linkText, action, controller);
}
}
%= Html.MyActionLink("hello foo", "Index", "Home") %>
<%= Html.MyActionLink("hello bar", "About", "Home") %>
Code copied from https://stackoverflow.com/a/5084672/20126
Related
For testing purpouses I want to have custom attribute on each, for example, button each page of my app without any manual adding.
Let's say I have page:
#page "/mypage"
<h1>This is page</h1>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
<button class="btn btn-primary" #onclick="IncrementCount">NO, click ME</button>
#code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
and, eventually, after some kind of additional rendering step, I want to see this html in my browser:
<h1>This is page</h1>
<button class="btn btn-primary" my-custom-attribute="button-click-me">Click me</button>
<button class="btn btn-primary" my-custom-attribute="button-no-click-me">NO, click ME</button>
So, the rule for makeing my-custom-attribute content comes from component content itself. It can be described as "component name + dash + text from component to lower case" => button(component name) + - + click-me(text from component to lower case with dashes instead spaces). Rule should be described somewhere in C# code and should "targeting" on list of components(buttons, divs, etc).
Is there any way to achieve that? Maybe there is a way to tweak Blazor rendering process somehow?
I can see no other solution than using a custom generic component. Someone may come up with a better answer. If so, I'll happily delete this one.
Here's a basic custom component to render html elements:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
namespace BlazorApp1.Pages
{
public class MyComponentBase : ComponentBase
{
[Parameter] [EditorRequired] public string Tag { get; set; } = "div";
[Parameter] public string MyAttributeValue { get; set; } = string.Empty;
[Parameter] public string? Content { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> UserAttributes { get; set; } = new Dictionary<string, object>();
private string myAttribute => string.IsNullOrWhiteSpace(Content) ? this.MyAttributeValue : this.Content.Replace(" ", "-");
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, Tag);
builder.AddMultipleAttributes(1, UserAttributes);
if (!string.IsNullOrWhiteSpace(this.myAttribute))
builder.AddAttribute(2, "my-attribute", this.myAttribute);
if (ChildContent is not null)
builder.AddContent(3, ChildContent);
else if (!string.IsNullOrWhiteSpace(Content))
builder.AddMarkupContent(4, Content);
builder.CloseElement();
}
}
}
And a Demo:
#page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<MyComponentBase Tag="button" MyAttribute="Blazor-Component" class="btn btn-primary" #onclick=this.OnClick Content="My Blazor Button" />
<MyComponentBase Tag="div" MyAttributeValue="Survey-Prompt-Div" class="m-2">
<SurveyPrompt Title="How is Blazor working for you?" />
</MyComponentBase>
<div>
#this.message
</div>
#code {
private string message = string.Empty;
private void OnClick()
{
this.message = $"Hello Blazor at {DateTime.Now.ToLongTimeString()} ";
}
}
While this sort of works, it highlights some issues with what you are trying to do.
You can't just get the "content" between the opening and closing tags of an element. The content is a RenderFragment, not a string that you can parse and use for your attribute. Therefore anything like the second block in the example requires a manual setting of the attribute (you may as well define it directly!).
The complexity of the component depends on what type of content you want to apply your attribute to.
Anyway, the component demonstrates the basic principles of how to capture parameters and attributes and use them to construct elements.
I saw this post, the answers are from about a year ago, and am hoping that there is a better way to do this now.
The second answer by user11623871 seems to be the best way to do this that I could find, but when there are multiple different class names applied to an element, it will be hard to make sure all the right ones are applied.
Is there something in blazor like in JS where I can just simply select the element and then remove or add a class whenever needed?
What it would look like in plain js:
var element = document.getElementById("myDIV");
element.classList.add("mystyle");
Something like this?
<div class="#string.Join(" ", CSSClasses)"></div>
<button type="button" #onclick="Add">ADD</button>
<button type="button" #onclick="Remove">REMOVE</button>
#code {
private List<string> CSSClasses = new List<string>();
void Add()
{
CSSClasses.Add("Class1");
CSSClasses.Add("Class2");
}
void Remove()
{
CSSClasses.RemoveAll(x => x == "Class1");
}
}
Ok, so now I'm trying to learn .net core mcv and I'm having a problem mapping data from MySQL back to my form. When I make the form as a single text box with a single button, and the other fields outside the form (but on the same page), the mapping works fine. If I include all the fields within the form, the data is obtained but not displayed on the page. I have even gone so far as to code one of the multiple submit buttons as an update of the data. I use the first text box to get the item from the database, which it does (but does not map to the text-boxes), then in the second text box (which should have the existing data, but is empty) I put the information to update in the database, click on the submit button for that text box, and the database is updated (but the text boxes in the view remain blank).
My model:
using System;
namespace DbTest.Models
{
public class ProductInventory
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public int Field4 { get; set; }
}
}
my controller:
using System;
using Microsoft.AspNetCore.Mvc;
using MySql.Data.MySqlClient;
using Microsoft.AspNetCore.Authorization;
using DbTest.Models;
namespace DbTest.Controllers
{
public class InventoryController : Controller
{
// [Authorize]
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ProcessForm(string button, ProductInventory p)
{
IActionResult toDo = null;
if (button == "Button1")
{
toDo = GetItem(p);
}
if (button == "Button2")
{
toDo = UpdateField2(p);
}
if (button == "Button3")
{
toDo = UpdateField3(p);
}
if (button == "Button4")
{
toDo = UpdateField4(p);
}
return toDo;
}
// [HttpPost]
public IActionResult GetItem(ProductInventory p)
{
//CODE SNIP - DATABASE QUERY, IT ALL WORKS, SO WHY BOTHER YOU WITH THE DETAILS?
return View("Index", p);
}
public IActionResult UpdateField2(ProductInventory p)
{
//CODE SNIP - DATABASE UPDATE, ALL WORKS, NOTHING TO SEE HERE
return View("Index", p);
}
}
}
And finally, my view:
#model DbTest.Models.ProductInventory
#{
ViewData["Title"] = "Inventory Page";
}
#using (Html.BeginForm("ProcessForm", "Inventory", FormMethod.Post))
{
<div>
Search Item (Field 1):
#Html.TextBoxFor(model => model.Field1)
<input type="submit" name="button" value="Button1" />
</div>
<div>
Field 2:
#Html.TextBoxFor(model => model.Field2)
<input type="submit" name="button" value="Button2" />
</div>
<div>
Field 3:
#Html.TextBoxFor(model => model.Field3)
<input type="submit" name="button" value="Button3" />
</div>
<div>
Field 4:
#Html.TextBoxFor(model => model.Field4)
<input type="submit" name="button" value="Button4" />
</div>
}
To reiterate, if I close the form after Button1:
#using (Html.BeginForm("ProcessForm", "Inventory", FormMethod.Post))
{
<div>
Search Item (Field 1):
#Html.TextBoxFor(model => model.Field1)
<input type="submit" name="button" value="Button1" />
</div>
}
<div>
Field 2:
//etc.
the mapping works, but only the first field and button of the form work. With the form around all four fields and buttons, the mapping doesn't work, but the coding of the second button DOES update the database on clicking Button2.
Can someone explain what I've done wrong here?
Thanks!
At first, don't use html helpers in ASP.NET Core.They work but it is not best practice. Instead use tag helpers wherever possible. Furthermore, don't use your db models as view models.
Regarding your Index action: You forgot to pass a view model to your view.
In your ProcessForm action you instantiate IActionResult and then assign it with a (action) function. Don't do that. Instead use return RedirectToAction("ActionName");.
In your case I would handle the DB updates inside the ProcessForm action or in a function, which doesn't return IActionResult.
In conclusion, I can only recommend you to read the ASP.NET Core documentation and then ask again if you still don't get it to work. I recommend you to start with reading this.
I want to add css class to body tag in yii2 advanced in frontend/views/layouts/main.php how can I do it?
You can do this dynamically like this:
<body class="<?= $this->context->bodyClass; ?>">
And in main Controller (all other controllers should extend this Controller) define property:
public $bodyClass;
or for default value:
public $bodyClass = 'custom-skin';
Ofc you can override this property in any extending controller by redefining it:
public $bodyClass = 'custom-skin-2';
In init:
public function init() {
parent::init();
$this->bodyClass = 'custom-skin-2';
}
In specific action:
public function actionView()
{
$this->bodyClass = 'custom-skin-3';
return $this->render('view');
}
You add your class simply to body tag
<body class="yourClass">
Another possible solution would be using the variable $params in your view.
Example
In your view you can define:
$this->params['bodyClass'] = 'yourclass';
And then, in your layout file, you'd go:
[.. head and other codes ..]
<body <? if(isset($this->params['bodyClass'])) echo 'class="' . $this->params['bodyClass'] . '"'; ?>>
<?php $this->beginBody() ?>
[.. rest of your code ..]
Notice that
I'm suggesting you to use the if so it only puts the class if it the $params['bodyClass'] is set in your view.
Also, you can use whatever name you want in the place of bodyClass.
This example will output <body class="yourclass">
Cheers.
I want to enable or disable a textarea depending on a condition that evalueates from the model, and I am using the textarea tag helper.
In other words, something like this:
<textarea asp-for="Doc" #(Model.MustDisable ? "disabled" : "")></textarea>
But I got the following design-time error: The tag helper 'textarea' must not have C# in element's attribute declaration area.
Then I tried:
<textarea asp-for="Doc" disabled='#(Model.MustDisable ? "disabled" : "")'></textarea>
which did not show any design time error but it renders like this:
Model.MustDisable==true renders disabled='disabled' AND Model.MustDisable==false renders disabled.
So the text area will always be disabled.
Then I tried (removing the 's):
textarea asp-for="Doc" disabled=#(Model.MustDisable ? "disabled" : "")></textarea>
which did not show any design time error but it renders the same as the previous one.
How can I implement this the right way?
It is actually very simple, the disable attribute is already working as you want - you can pass in a boolean value:
<textarea asp-for="Doc" disabled="#Model.MustDisable"></textarea>
if false the disabled attribute is not rendered:
<textarea></textarea>
if true the disabled attribute is set to "disabled":
<textarea disabled="disabled"></textarea>
I was facing the same issue with select tag helper, i tried few things and it worked.
Try this-
<textarea asp-for="Doc" disabled="#(Model.MustDisable ? "disabled" : null)"></textarea>
The textarea tag helper does not have direct support to conditionally render a disabled text area. But you can always extend the TextAreaTagHelper and add this feature.
So create a new class which inherits from the TextAreaTagHelper class.
[HtmlTargetElement("textarea", Attributes = ForAttributeName)]
public class MyCustomTextArea : TextAreaTagHelper
{
private const string ForAttributeName = "asp-for";
[HtmlAttributeName("asp-is-disabled")]
public bool IsDisabled { set; get; }
public MyCustomTextArea(IHtmlGenerator generator) : base(generator)
{
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (IsDisabled)
{
output.Attributes["disabled"] = "disabled";
}
base.Process(context, output);
}
}
In your _ViewImports.cshtml file, using the #addTagHelper directive, specify the assembly where the above class is defined so that our new tag helper is available in other razor views.
#addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
#addTagHelper "*,YourAssemblyNameHere"
Now in your views, you can use it like
#model YourSomeViewModel
<textarea asp-for="Doc" asp-is-disabled="Model.MustDisable"></textarea>
where SomeViewModel has a Doc and MustDisable property.
public class YourSomeViewModel
{
public string Doc { set;get; }
public bool MustDisable { set;get; }
}
I am posting this separately since I don't have enough reputation to add a comment to Shyju's answer.
If you inherit from one of the default tag helpers and then register both the default tag helpers and your custom tag helper in _ViewImports.cshtml, then both tag helpers will be executed for the specified tags.
For the following:
[HtmlTargetElement("textarea", Attributes = ForAttributeName)]
public class MyCustomTextArea : TextAreaTagHelper
{
private const string ForAttributeName = "asp-for";
...
With the following _ViewImports.cshtml:
#addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
#addTagHelper "*,YourAssemblyNameHere"
Both MyCustomTextArea and TextAreaTagHelper will be executed for each textarea tag.
I did not notice any problems with the output generated for textareas, but I have run into problems inheriting from other default tag helpers. The solution is to remove the default tag helper in _ViewImports.cshtml.
#addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
#addTagHelper "*,YourAssemblyNameHere"
#removeTagHelper "Microsoft.AspNet.Mvc.TagHelpers.TextAreaTagHelper, Microsoft.AspNet.Mvc.TagHelpers"