Powershell and Razor Template - powershell

I'm using PowerShell and Render-RazorTemplate.ps1 to create a html page
I have a 'System.Collections.Hashtable' passing to my Model and i'm not getting this to work
#foreach (var s in Model)
{
<div class="BTC-setting"><i class='fa #(s["Icon"])'></i><span>#s["Name"]</span></div>
}
If I use like this
class='fa #(s["Icon"])'
I have an error like
The name 'WriteAttribute' doesn't exist on the actual context
>>> WriteAttribute("class", Tuple.Create(" class=\'", 225), Tuple.Create("\'", 248)**
If I remove the #(s["Icon"]) from inside of the property class it works and shows me the value
Someone have any ideas about this?

Just found the solution to my issue, just put #: at the begging of the html tags that uses variables inside quotes
ISSUE
#foreach (var s in Model)
{
<div class="BTC-setting"><i class='fa #(s["Icon"])'></i><span>#s["Name"]</span></div>
}
SOLUTION
#foreach (var s in Model)
{
#:<div class="BTC-setting"><i class='fa #(s["Icon"])'></i><span>#s["Name"]</span></div>
}

Related

Blazor: How to use the onchange event in <select> when using #bind also?

I need to be able to run a function after a selection in made in a <select>. The issue is I'm also binding with #bind and I get a error when I try to use #onchange stating that it is already in use by the #bind. I tried using #onselectionchange, but that does nothing(doesn't run the function). I could forget the #bind and just assign #onchange to a function, but I'm not sure how to pass the selected value to the function.
I have the following code:
<select #bind="#SelectedCustID" # #onchange="#CustChanged" class="form-control">
#foreach (KeyGuidPair i in CustList)
{
<option value="#i.Value">#i.Text</option>
}
</select>
Thanks.
#bind is essentially equivalent to the having both value and #onchange, e.g.:
<input #bind="CurrentValue" />
Is equivalent to:
<input value="#CurrentValue" #onchange="#((ChangeEventArgs e) => CurrentValue = e.Value.ToString())" />
Since you've already defined #onchange, instead of also adding #bind, just add value to prevent the clash:
<select value="#SelectedCustID" #onchange="#CustChanged" class="form-control">
#foreach (KeyGuidPair i in CustList)
{
<option value="#i.Value">#i.Text</option>
}
</select>
Source: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-3.1
<select #bind="MyProperty">
<option>Your Option<option>
</select>
#code {
private string myVar;
public string MyProperty
{
get { return myVar; }
set
{
myVar = value;
SomeMethod();
}
}
private void SomeMethod()
{
//Do something
}
}
Just add #bind-value and #bind-value:event="oninput" with #onchange as below. Note the lower case letters. This works for me without any issue.
<select #bind-value="variablenametokeepselectedvalue" #onchange="yourmethodname" #bind-value:event="oninput">
<option value="1">Test</option>
</select>
This seems to be a popular confusion. Firstly you cant use #onchange since it would internally be used by #bind. You should be able to access the selected value from the setter of your CustChanged property. Based on what you are trying to do with your CustChanged, you may not even need to manually check when this value is updated. For instance, if your intent is to use CustChanged in your UI directly or indirectly (within Linq or something), the UI would automatically update with CustChanged value when your <select> is changed. Hence, for most use cases I don't see the need to check when it was updated.
To use #onchange, you can bind it to a function something like this:
public void OnUpdated(ChangeEventArgs e)
{
var selected = e.Value;
}
You can avoid #bind altogether (if you're using a foreach):
<select #onchange=#(handleChange)>
#foreach (var option in _options){
<option value=#option.Id selected=#(SelectedId == option.Id)>#option.Name</option>
}
</select>
#code {
public record Person(int Id, string Name);
public int SelectedId { get; set; }
public List<Person> _options = new List<Person>() {
new Person(1,"A"),
new Person(2,"B"),
new Person(3,"C")
};
public void handleChange(ChangeEventArgs args) {
Console.WriteLine(args.Value);
SelectedId = Int32.Parse(args.Value.ToString());
}
}
Some sordid details: I was getting some weird behavior when trying to use F# with server-side Blazor. In short, setting the List of options to the result of an Entity Framework query (mapped to a list of records) wouldn't #bind properly, but using a dummy list of options that were C# classes and not F# records did work. It wasn't due to it being records though, because if I set the list to the EF query and then immediately set it to a dummy list of records, it still didn't #bind properly - but it did work if I commented out the EF line.
Please check this example. It is using #bind but upon setting the value it triggers #onchange event
<div class="form-group">
<label for="client">Client Name</label>
<select id="client" #bind="CheckSelected" class="form-control">
<option value="selected1">selected1</option>
<option value="selected2">selected2</option>
</select>
</div>
#code {
private string selectedItem {get; set;}
private string CheckSelected
{
get
{
return selectedItem;
}
set
{
ChangeEventArgs selectedEventArgs = new ChangeEventArgs();
selectedEventArgs.Value = value;
OnChangeSelected(selectedEventArgs);
}
}
private void OnChangeSelected(ChangeEventArgs e)
{
if (e.Value.ToString() != string.Empty)
{
selectedItem = e.Value.ToString();
}
}
}
Please check below code for reference how we use select tag with bind value and call function onselection value change in blazor
<InputSelect class="form-control mb-0" ValueExpression="#(()=>request.Id)" Value="request.Id"
ValueChanged="#((string value) => SelectedValueChange(value))">
#if (dropdownResponses != null)
{
#foreach (var item in dropdownResponses)
{
<option value="#item.Id.ToString()">#item.Name</option>
}
}
</InputSelect>
use <InputSelect> tag instead of <select> tag and use ValueChanged method for getting call on select value change
here is the code of ValueChanged function
internal void SelectedValueChange(string value)
{
string NewValue = value;
}
Starting from .NET 7 Preview 7, the recommended way to handle this issue is to use the bind:after modifier.
Here is a small example (partially borrowed from the docs):
<p>
<label>
Select one or more cities:
<select #bind="SelectedCities" multiple #bind:after="DoSomething">
<option value="bal">Baltimore</option>
<option value="la">Los Angeles</option>
<option value="pdx">Portland</option>
<option value="sf">San Francisco</option>
<option value="sea">Seattle</option>
</select>
</label>
</p>
<span>
Selected Cities: #string.Join(", ", SelectedCities)
</span>
#code {
public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };
// Run your logic here after a binding event
private void DoSomething()
{
Console.WriteLine(string.Join(',', SelectedCities));
}
}
My recommendation is, if possible, use an EditForm wrapper around your form elements. Then you can detect a change of any of the form elements, in one place. This is good for, for example, a bunch of search filters. Any change in any of the filters should trigger another query of the data, etc.
Example of how to trigger event on form changes is here:
blazor editform change events
According to Microsoft's documentation, this is their preferred way of handling this problem:
<InputText Value="#NewPaymentAmount" class="mdl-textfield__input"
ValueExpression="() => NewPaymentAmount"
ValueChanged="(string value) => ValidateAmount(value)" />
private void ValidateAmount(string amount)
{
NewPaymentAmount = amount;
// Do validation or whatever
}
Or the async way:
<InputText Value="#NewPaymentAmount" class="mdl-textfield__input"
ValueExpression="() => NewPaymentAmount"
ValueChanged="async (string value) => await ValidateAmountAsync(value)" />
private async Task ValidateAmountAsync(string amount)
{
NewPaymentAmount = amount;
// Do validation or whatever
}
<div class="form-group">
<label for="client">Client Name</label>
<select class="form-control" #onchange="#((e) => { myVar = e.Value.ToString(); MyMethod(); })">
<option value="val1">val1</option>
<option value="val2">val2</option>
</select>
</div>
This is how I was able to set the property while calling for a method in the #onchange event.
-Blazor Server
-Dotnet Core 3.1
I use oninput to essentially have bind and onchange.
oninput triggers when the input value is changed whereas bind/onchange triggers when the element loses focus and the input has changed.
This might not fit every scenario however as it wll trigger on every input while typing, depending on your need or for inputs such as selects, radios, etc. it should be suitable.

The value "" is invalid

Why does the Edit screen show the value I'm editing but when I try to save, the Edit HTTPPOST has a null object?
Getting a an error I've tracked down but don't see the cause of. I have a class used for a drop list, so it only has AdvisoryTypeID and AdvisoryType. But now I'm creating screens for users to see all in a grid (Index), add new (Create), and change existing ones (Edit). The index screen reads from the DB no problem. So does Edit. Create doesn't need to.
But both Edit and Create give the same error on HTTPPOST. The error is "The Value 'whatever I typed in' is invalid". Debugging the code turned up why: the "advisoryTypes" variable is null for reasons I don't understand:
CONTROLLER (for Edit post)
[Authorize]
[HttpPost]
public ActionResult Edit(AdvisoryTypes advisoryType)
{
try
{
if (ModelState.IsValid) //never get past this because it's null above
etc....
}
I have compared this to another screen that does Edit and Create fine and they're identical (except field names, for example). I'm stumped.
MODEL
public class AdvisoryTypes
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AdvisoryTypeID { get; set; }
[Display(Name = "Type")]
[Required]
public string AdvisoryType { get; set; }
}
CONTROLLER (for Edit Get)
[Authorize]
public ActionResult Edit(int id = 0)
{
AdvisoryTypes advisoryType = db.AdvisoryType.Find(id);
if (advisoryType == null)
{
return HttpNotFound();
}
return View(advisoryType);
}
VIEW
model TheNewSolution.Models.AdvisoryTypes
#{
ViewBag.Title = "Edit Advisory Type";
}
<h2>Edit Advisory Type</h2>
#*<br />*#
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Advisory</legend>
<table>
<tr>
<td>
#Html.HiddenFor(model => model.AdvisoryTypeID)
<div class="editor-label">
#Html.LabelFor(model => model.AdvisoryType)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AdvisoryType)
#Html.ValidationMessageFor(model => model.AdvisoryType)
</div>
</td>
</tr>
</table>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
PART TWO
Now some odd background: this is the first model/controller/view I created since updating EF from 5 to 6 alpha3 (had to because I had POCO and edmx in same project and this solved that bug). I created the model manually. When I tried to create the Controller via the wizard, the wizard gave an error "Unable to retrieve metadata for ProjectName.Models.AdvisoryProviders'. Unable to cast obect of type 'System.Data.Entity.Core.Objects.ObjectContext' to type 'System.Data.Objects.ObjectContext'.".
The result was having to manually create the controller by copying/pasting another one and making changes, then creating my views the same way. I'm suspicious this is all related but maybe not. If this proves unrelated I'll remove this and post as another question.
the error from EF of "Unable to retrieve metadata for ProjectName.Models.AdvisoryProviders i have seen before, but this has only been because i was using MySQL, the only way i found around this kind of error and to make sure everything worked was to use http://visualstudiogallery.msdn.microsoft.com/72a60b14-1581-4b9b-89f2-846072eff19d to create models from the database, and then use http://blog.stevensanderson.com/2011/01/13/scaffold-your-aspnet-mvc-3-project-with-the-mvcscaffolding-package/ to create the controllers, with view, rather than the buggy version of create view for EF.
I posted about these problems a while back with EF5 and its a real pain, MVCScaffolding seems to handle that pain alot better than the built in TT templates with MVC 4
hope this helps
I am not sure why this resolved, but here's what I did. I needed to create the screens mentioned above (index with grid, create, edit) for three different things (types, providers, categories) in my app. I did the first, type, resulting in the above issues.
I decided to create the same for "providers" by copying and pasting the controllers and views from "type" screens, then changing the model and field names as needed, expecting the screen to have the same bugs. But the screens all worked. I did it again for "categories", which also worked. Then I deleted my failing Create and Edit screens for "type", and recreated them from the "providers" screens. And they worked.
I have no explanation.

foreach statement cannot operate on variables of type '' because '' does not contain a public definition for 'GetEnumerator'

I Got an Error Like This
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1579: foreach statement cannot operate on variables of type 'EventListing.Models.EventInfo' because 'EventListing.Models.EventInfo' does not contain a public definition for 'GetEnumerator'
Source Error:
Line 106: </th>
Line 107: </tr>
Line 108: <% foreach (var model in Model)
Line 109: { %>
Line 110: <tr>
Model
public static List<EventInfo> EventList()
{
var list = new List<EventInfo>();
Dbhelper DbHelper = new Dbhelper();
DbCommand cmd = DbHelper.GetSqlStringCommond("SELECT * FROM WS_EVENTINFO");
DbDataReader Datareader = DbHelper.ExecuteReader(cmd);
while (Datareader.Read())
{
EventInfo eventinfo = new EventInfo()
{
EVENT_ID = Convert.ToInt32(Datareader[
View Page
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<EventListing.Models.EventInfo>" %>
<% foreach (var model in Model)
{ %>
<tr>
<td>
<%= Html.ActionLink(Model.TITLE,"Detail", new {id = Model.EVENT_ID})%>
How can solve this Issue, i'm Using MVC2 Framework.
You need to bind a collection as a model. Check this post for details: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
View
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IList<EventListing.Models.EventInfo>>" %>
Controller
public ActionResult List()
{
return this.View(EventList());
}
Or use some another type for model and define property List<EventInfo> Events in it. Then iterate in following way:
<% foreach (var model in Model.Events)
{ %>
Also, Visual Studio can do it for you:
Presumably what you are trying to do is:
foreach(var model in Model.EventList()) {..}
Although it is difficult to be sure.
If you require the syntax you used then Model will have to be an object of a class with a GetEnumerator() method.

ASP .NET MVC 2 - How do I pass an object from View to Controller w/ Ajax?

I have an object MainObject with a list of objects, SubObjects, among other things. I am trying to have the user click a link on the View to add a new SubObject to the page. However, I am unable to pass the MainObject I am working with into the Action method. The MainObject I currently receive is empty, with all its values set to null. How do I send my controller action the MainObject that was used to render the View originally?
The relevant section of the view looks like this:
<div class="editor-list" id="subObjectsList">
<%: Html.EditorFor(model => model.SubObjects, "~/Views/MainObject/EditorTemplates/SubObjectsList.ascx")%>
</div>
<%: Ajax.ActionLink("Add Ajax subObject", "AddBlanksubObjectToSubObjectsList", new AjaxOptions { UpdateTargetId = "subObjectsList", InsertionMode = InsertionMode.Replace })%>
The relevant function from the controller looks like this:
public ActionResult AddBlanksubObjectToSubObjectsList(MainObject mainobject)
{
mainobject.SubObjects.Add(new SubObject());
return PartialView("~/Views/MainObject/EditorTemplates/SubObjectsList.acsx", mainobject.SubObjects);
}
I ended up with the following:
View:
<div class="editor-list" id="subObjectsList">
<%: Html.EditorFor(model => model.SubObjects, "~/Views/MainObject/EditorTemplates/SubObjectsList.ascx")%>
</div>
<input type="button" name="addSubObject" value="Add New SubObject" onclick="AddNewSubObject('#SubObjectList')" />
Control:
public ActionResult GetNewSubObject()
{
SubObject subObject= new SubObject();
return PartialView("~/Views/TestCase/EditorTemplates/SubObject.ascx", subObject);
}
And, finally, I added this JQuery script:
function AddNewSubObject(subObjectListDiv) {
$.get("/TestCase/GetNewSubObject", function (data) {
//there is one fieldset per SubObject already in the list,
//so this is the index of the new SubObject
var index = $(subObjectListDiv + " > fieldset").size();
//the returned SubObject prefixes its field namess with "[0]."
//but MVC expects a prefix like "SubObjects[0]" -
//plus the index might not be 0, so need to fix that, too
data = data.replace(/name="\[0\]/g, 'name="SubObject[' + index + "]");
//now append the new SubObject to the list
$(subObjectListDiv).append(data);
});
}
If someone has a better way to do this than kludging the MVC syntax for nested objects onto a returned View using JQuery, please post it; I'd love to believe that there is a better way to do this. For now, I'm accepting my answer.

Where and how to load dropdownlists used in masterpage

I'm new to MVC!
I am trying to use two DropDownLists (Cities, Categories) in a PartialView that will be used in MasterPage, meaning they will be visble all the time.
I tried to load them in HomeCOntroller, but that didn't work. I got an Exception.
I read something about making a baseController that the other controllers will inherit from, I have tried that, kind of, but I guess i'm doing something wrong.
This is the only code I got today:
Masterpage
<% Html.RenderPartial("SearchForm"); %>
PartialView (SearchForm.ascx)
<% using (Html.BeginForm("Search", "Search")) { %>
<% } %> // dont know why I need two BeginForms, if I dont have this the other form won't trigger at all! Weird!
<% using (Html.BeginForm("Search", "Search", FormMethod.Get)) { %>
<%= Html.DropDownList("SearchForm.Category", new SelectList(ViewData["Categories"] as IEnumerable, "ID", "Name", "--All categories--")) %>
<%= Html.DropDownList("Search.City", Model.Cities, "--All cities--") %>
<input name="search" type="text" size="16" id="search" />
<input type="submit" id="test" title="Search" />
<% } %>
Two question:
Where and how to load the DropDownLists is the problem. I have tried to load it in the HomeController, but when go to another page then it says that the DDLs is empty and I get a Excecption.
Why do I have to use two forms for the ActionMethod to trigger ?
Hope anyone can help me out!
It sounds like you're only setting the property for a single action result. The Model.Cities data will have to be populated for every single view that needs to use it.
One solution would be to move the population of it to an ActionFilter
public class CityListAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext) {
var result = filterContext.Result as ViewResult;
result.ViewData.Model = //populate model
base.OnActionExecuted(filterContext);
}
}
and then add the filter to your controller
[CityList]
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
}
As for the two forms issue, there should be no reason that i can think of that you need an empty form.
Take a look at the html that's being output and make sure it's ok. Also check the action is being generated correcly
Better way to do this, is to create something like MasterController and have action method on it like this:
[ChildActionOnly]
public ActionResult SearchForm()
{
//Get city data, category data etc., create SearchFormModel
return PartialView(model);
}
I recommend you create strongly typed view (SearchForms.ascx of type ViewUserControl<SearchFormModel>). Also it may be a good idea to have a model like this:
public class SearchViewModel
{
public IList<SelectListItem> Cities { get; set; }
public IList<SelectListItem> Categories { get; set; }
}
and use a helper like this: http://github.com/Necroskillz/NecroNetToolkit/blob/master/Source/NecroNet.Toolkit/Mvc/SelectHelper.cs to convert raw data to DDL friendly format beforehand.
In any case, you now use Html.RenderAction() instead of Html.RenderPartial() and specify you want "SearchForm" action from "MasterController".