How to output block with handlebars dot net conditional helper? - handlebars.net

I have the following helper for handlebars.net:
Handlebars.RegisterHelper("#is",
(writer, context, args) =>
{
string val1 = args[0].ToString();
string val2 = args[1].ToString();
if (val1 == val2)
{
//how to get block output
}
});
I am trying to test it on the following html, but I am not sure how to write out the content between {#is} and {/is} if it is true:
<div style="text-align: right;">
{{#each TeamMembers}}
{{#is this.Title 'Manager'}}
{{ this.Name }}<br />
{{ this.PersonalEmail }}<br />
{{ this.Phone }}<br />
{{/is}}
{{/each}}
</div>

Figured it out using a different signature:
Handlebars.RegisterHelper("is",
(writer,options, context, args) =>
{
string val1 = args[0].ToString();
string val2 = args[1].ToString();
if (val1 == val2)
{
options.Template(writer, (object)context);
}
});

Related

knockout array radio button and/or droplist based

Right now I am populating radio button from the observable array,
<div data-bind="foreach: cars">
<div>
<input type='radio' data-bind="checked: $root.vehicle, checkedValue: id, value:id"><span data-bind="text: model"></span>
</div>
</div>
Model data,
public class MyViewModel
{
public List<Cars> cars {get; set;}
}
public class Cars
{
public int Id{get; set;}
public string Make {get; set;}
public string Model {get; set;}
}
Say if I want to populated radio button for Make 'Honda' and dropdown list for make 'Toyota'. How to do that?
Basically, I am trying populate two different list of controls from same observable array.
If I understand what exactly you want to do, here is an example through what you could do :
Example :https://jsfiddle.net/kyr6w2x3/85/
HTML :
<div data-bind="foreach: Cars">
<input type='radio' data-bind="checked :$parent.VehicleSelected ,checkedValue: Make, value:Id">
<span data-bind="text: Make"></span>
<select data-bind="foreach:Models,visible:IsSelected" >
<option data-bind="text:Name , value:Id"></option>
</select>
<br />
</div>
JS:
var data = [
{Id:1,Make :"TOYOTA",Model: [{Id:1,Name:"COROLLA"},{Id:2,Name:"CAMERY"},{Id:3,Name:"PERADO"}]},
{Id:2,Make :"HONDA",Model:[{Id:4,Name:"CIVIC"},{Id:5,Name:"ACCORD"},{Id:6,Name:"PILOT"}]},
{Id:3,Make :"NISSAN",Model:[{Id:7,Name:"SENTRA"},{Id:8,Name:"ALTIMA"},{Id:9,Name:"JUKE"}]}];
function AppViewModel() {
var self = this ;
self.Cars = ko.observableArray($.map(data, function (item) {
return new CarItemViewModel(item);
}));
self.VehicleSelected = ko.observable();
self.VehicleSelected.subscribe(function(newValue){
ko.utils.arrayForEach(self.Cars(), function(item) {
if (item.Make().toUpperCase() === newValue.toUpperCase()) {
return item.IsSelected(true);
}else{
return item.IsSelected(false);
}
});
})
}
var CarItemViewModel = function (data){
var self = this ;
self.Id = ko.observable(data.Id);
self.Make = ko.observable(data.Make);
self.Models = ko.observableArray($.map(data.Model, function (item) {
return new ModelsItemViewModel(item);
}));
self.IsSelected = ko.observable(false);
}
var ModelsItemViewModel = function (data){
var self = this ;
self.Id = data.Id;
self.Name = data.Name;
}
var appVM = new AppViewModel()
ko.applyBindings(appVM);
My interpretation of your question:
You have one variable that holds all your data
You want to render multiple UI elements that represent sub-sets of these data
A good way to do this is by using ko.computeds that return the filtered result of an observableArray. An example:
var allCars = ko.observableArray([
{"name":"CIVIC","make":"HONDA"},
{"name":"SENTRA","make":"NISSAN"}];
var hondas = ko.computed(function() {
return allCars().filter(function(car) {
return car.make === "HONDA";
});
});
Because the computed uses the allCars observable, it is notified when the source data changes. The filter method is re-evaluated. In the snippet below you can see that you can push new data to your source and is automatically put in the right subset!
var allCars = ko.observableArray([
{"name":"CIVIC","make":"HONDA"},
{"name":"ACCORD","make":"HONDA"},
{"name":"PILOT","make":"HONDA"},
{"name":"SENTRA","make":"NISSAN"},
{"name":"ALTIMA","make":"NISSAN"},
{"name":"JUKE","make":"NISSAN"}]);
var hondas = ko.computed(function() {
return allCars().filter(function(car) {
return car.make === "HONDA";
});
});
var nissans = ko.computed(function() {
return allCars().filter(function(car) {
return car.make === "NISSAN";
});
});
var otherBrands = ko.computed(function() {
return allCars().filter(function(car) {
return car.make !== "NISSAN" &&
car.make !== "HONDA";
});
});
var inputVM = {
name: ko.observable(""),
make: ko.observable(""),
add: function() {
allCars.push({
name: this.name(),
make: this.make().toUpperCase()
});
}
};
ko.applyBindings({
hondas: hondas,
nissans: nissans,
allCars: allCars,
otherBrands: otherBrands,
inputVM: inputVM
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h2>Honda</h2>
<ul data-bind="foreach: hondas">
<li data-bind="text: name"></li>
</ul>
<h2>Nissan</h2>
<ul data-bind="foreach: nissans">
<li data-bind="text: name"></li>
</ul>
<h2>Other brands</h2>
<ul data-bind="foreach: otherBrands">
<li data-bind="text: name"></li>
</ul>
<h2>Add car</h2>
<div data-bind="with: inputVM">
<label>
Name:
<input type="text" data-bind="value: name">
</label>
<label>
Make:
<input type="text" data-bind="value: make">
</label>
<button data-bind="click: add">Add</button>
</div>

How to sort HTML table in MVC based on value selected in dropdown

I have a requirement where I have to sort the HTML table in MVC on the basis of value selected in dropdownlist.
The dropdownlist is having 2 values: Sort by Number and Sort By Description.
Following is the code:
#{
Layout = T4MVC.SharedController.ViewNames._Ctx;
}
#section brandcrambs{
<div id="breadcrumbs">#{Html.RenderPartial(T4MVC.SharedController.ViewNames._ProductCatalogBrandCrambs, Model);}</div>
}
#Html.DropDownList("SortBy", new List<SelectListItem>
{
new SelectListItem{ Text="Sort By Number", Value="0", Selected=true},
new SelectListItem{ Text="Sort By Description", Value = "1" }
})
#{
var s = #ViewData["currentNode"];
}
<div style="font-size: 14px; font-weight: bold">
#Convert.ToString(s)
</div>
#foreach (SubSectionNodesVM n in Model.Nodes)
{
<h4>
#if (n.CurrentNode.Key.Contains("Specification"))
{
Html.RenderPartial(T4MVC.SharedController.ViewNames._PrintButtons, n.CurrentNode);
}
</h4>
var matchValue = n.Nodes as IEnumerable<SiteMapNodeBase>;
var resultSet = from dtRow in matchValue.AsEnumerable()
group dtRow by dtRow.Prefix into newGroup
orderby newGroup.Key
select newGroup;
var datamodel = resultSet as IEnumerable<SiteMapNodeBase>;
var k = 0;
<div id="#n.CurrentNode.Key.Replace("Specification|", "").Replace("Section|", "")" >
<table>
#foreach (var item in resultSet)
{
k++;
var match = item.Select(m => m.DefaultValue);
string resultDefaultValue = string.Empty;
if (match != null)
{
if (match.Count() > 0)
{
resultDefaultValue = match.ToList()[0];
}
}
<tr style="background-color:#(k % 2 == 1 ? "white" : "rgb(246,246,246)")">
<td colspan="4" style="font-size: 12px; font-weight: bold">#resultDefaultValue</td>
</tr>
var i = 0;
foreach (var inneritem in item)
{
k++;
<tr style="background-color:#(k % 2 == 1 ? "white" : "rgb(246,246,246)")">
<td style="width: 250px">#inneritem.Number</td>
#if (#inneritem.RangeStart == null || #inneritem.RangeStart == "")
{
<td style="width: 250px"></td>
}
else
{
<td style="width: 250px">From: #inneritem.RangeStart</td>
}
#if (#inneritem.RangeEnd == null || #inneritem.RangeEnd == "")
{
<td style="width: 250px"></td>
}
else
{
<td style="width: 250px">To: #inneritem.RangeEnd</td>
}
<td style="width: 800px">#inneritem.Description</td>
</tr>
}
}
</table>
<script>
$(document).ready(function () {
$('#SortBy').change(function () {
var value = $("#SortBy option:selected").val();
alert(value);
});
})
</script>
I strongly recommend that you read this article:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
It will explain how to sort (and filter etc) data from Entity Framework based on input. The sample does not use a Dropdown to demonstrate this, but if you understand the sample, you should be able to get it to work with a DropDown.
This is a server side approach, which I would always recommend when it comes to searching, sorting and paging.
If you chose to sort things on the client side, then it requires a different approach. There are many plugins for it, you can also do it yourself with pure javascript, or jQuery
Examples for client side sorting plugins:
https://datatables.net/
http://tablesorter.com/
http://joequery.github.io/Stupid-Table-Plugin/
...

Getting error converting dart2js on polymer project

Unsupported operation: Can't use ownerName in reflection because it is not included in a #MirrorsUsed annotation.
ownerName is just an published attribute on the polymer element. I understand there are a few things out there (on web, not on here) like this but none have a solid answer...
I also get this below it:
NoSuchMethodError : method not found: 'Symbol("title")'
Anyone got any ideas. Been wrestling with this for 3 hours and ready to dump polymer. Though It was fun in dartium, if it cannot convert to JS I see no real use in it.
import 'package:polymer/polymer.dart';
import 'package:google_postendpoint_v1_api/postendpoint_v1_api_browser.dart';
import 'package:google_postendpoint_v1_api/postendpoint_v1_api_client.dart';
import 'dart:math';
//import 'package:intl/intl.dart';
/**
* A Polymer post element.
*/
#CustomTag('post-element')
class SoberSky extends PolymerElement {
#published int count = 0;
#published String ownerName = "Casey";
#published int ownerId = 888;
#published int postId = 333;
#published String content = "yo ho ho, and a bottle of rumsky!";
#published String postContent = "This is an example of post content";
#published int getOwnerId = 333;
#published CollectionResponse_Comment listComment;
#published CollectionResponse_Post listPost;
#published String listCommentHTML;
#published Post currentPost;
#published bool yes = false;
Postendpoint endpoint = new Postendpoint();
var rng = new Random();
var commentList;
SoberSky.created() : super.created() {
endpoint.rootUrl = "http://localhost:8888/"; // set the root URL
}
void getListComment([int tempPostId]) {
if (tempPostId == null) {
tempPostId = postId;
}
endpoint.listComment( tempPostId ).then((CollectionResponse_Comment commentCollection){
this.listComment = commentCollection;
}).catchError((e) => handleError(e));
}
void getPost() {
endpoint.getPost( postId ).then((Post loadedMessage) {
currentPost = loadedMessage;
getListComment(currentPost.key);
}).catchError((e) => handleError(e));
}
void submitComment() {
int id = rng.nextInt(1000);
Comment comment = new Comment.fromJson({});
comment.id = id;
comment.content = content;
comment.postId = postId;
comment.ownerName = ownerName;
comment.ownerId = ownerId;
endpoint.insertComment( comment ).then((Comment savedComment) => endpoint.getComment(id)).
then((Comment loadedMessage) {
print(loadedMessage.content);
getListComment(loadedMessage.postId);
}).catchError((e) => handleError(e));
}
void submitPost(){
postId = rng.nextInt(1000);
Post post = new Post.fromJson({});
post.key = postId;
post.ownerName = ownerName;
post.ownerId = ownerId;
post.title = postContent;
endpoint.insertPost( post ).then((Post savedPost) => endpoint.getPost(postId)).
then((Post loadedMessage) {
print(loadedMessage.title);
getPost();
getListComment(loadedMessage.key);
}).catchError((e) => handleError(e));
}
void handleError(Error e) {
print("We had an error: ");
print(e.toString());
}
#override
void attached() {
}
}
The HTML Polymer element
<polymer-element name="click-counter" attributes="count">
<template>
<form action="javascript:void(0);" >
<div class="entry">
<label>Enter Post:</label>
<input id="subject" type="text" value="{{postContent}}" size="45" maxlength="255">
<button on-click="{{submitPost}}" class="btn btn-success" >Submit Post</button> <br>
</div>
</form>
<div class="post">
<h3>{{ currentPost.ownerName }}</h3>
<p>{{ currentPost.title }}</p>
<p>This is the postId: {{ currentPost.key }}</p>
<p class="timestamp">{{ currentPost.uploadTime }}</p>
<template repeat="{{comment in listComment.items}}">
<div class="comment">
<h3>{{ comment.ownerName }}</h3>
<p>{{ comment.content }}</p>
<p>This is the commentId: {{ comment.id }}</p>
<p class="timestamp">{{ comment.formatDate }}</p>
</div>
</template>
<form action="javascript:void(0);">
<label>Enter Comment:</label>
<input id="subject" type="text" value="{{content}}" size="45" maxlength="255">
<button on-click="{{submitComment}}" class="btn btn-success">Submit Comment</button> <br>
</form>
</div>
</template>
<script type="application/dart" src="clickcounter.dart"></script>
</polymer-element>
I have not yet seen your Unsupported operation message. Maybe some recent change. Your NoSuchMethodError is common when a property of a class is only referenced by a polymer expression (HTML) because tree-shaking drops all code that is not referenced and polymer expressions are not evaluated for this yet. The #MirrorsUsed annotation helps bridging this gap.
The problem is in your Post class because it's properties are only referenced in polymer expressions
<div class="post">
<h3>{{ currentPost.ownerName }}</h3>
<p>{{ currentPost.title }}</p>
<p>This is the postId: {{ currentPost.key }}</p>
<p class="timestamp">{{ currentPost.uploadTime }}</p>
To get your view updated when the properties in your currentPost change you should make your Post class like
class Post extends Object with Observable {
#observable String ownerName;
#observable String title;
...
}
If you have an annotation like #reflectable, #published, or #observable you don't need #MirrorsUsed.

Custom validation error message location

I've been struggling with how to change the following to fit my needs. This is for a form I have with simple inputs. The way the code is now it simply displays a label, the field and a validation message vertically, but I want to make it horizontal (using a <table>).
Currently I have this code:
var wrapper = new TagBuilder("div");
wrapper.AddCssClass("field-wrapper");
var table = new TagBuilder("table");//This is my test to insert a table
var label = new TagBuilder("div");
label.AddCssClass("field-label");
if (metadata.IsRequired && !metadata.IsReadOnly)
{
if (metadata.ModelType != typeof (bool))
{
label.InnerHtml += "* ";
wrapper.AddCssClass("required");
}
}
label.InnerHtml += html.LabelFor(expression);
wrapper.InnerHtml += label;
var input = new TagBuilder("div");
input.AddCssClass("field-input");
input.InnerHtml += html.EditorFor(expression);
if (!string.IsNullOrEmpty(metadata.Description))
{
input.InnerHtml += html.TooltipFor(expression);//declared elsewhere to show a tooltip
}
input.InnerHtml += html.ValidationMessageFor(expression);
wrapper.InnerHtml += input;
wrapper.InnerHtml += table;//This is my test to insert a table
return MvcHtmlString.Create(wrapper + "\r\n");
I tried inserting a <table> but this is the structure of the HTML with that code:
<div class="field-wrapper">
<div class="field-label">
<label for="Name">Name</label>
</div>
<div class="field-input">
* <input class="text-box single-line input-validation-error" data-val="true" data-val-required="The Name field is required." id="Name" name="Name" type="text" value="">
<!--tooltip would ordinarily show up here-->
<span class="field-validation-error" data-valmsg-for="Name" data-valmsg-replace="true">
<span for="Name" generated="true" class="">The Name field is required.</span>
</span>
</div>
<table></table><!--This is my table-->
</div>
This is what it looks like after validation-error:
What I am going for is this structure:
<div class="field-wrapper">
<table border="0">
<tbody>
<tr>
<td width="40%">
<div class="field-label">
<label class="mylabelstyle" for="FirstName" title="Enter first name.">First Name:</label>
</div>
</td>
<td width="60%">
<div class="field-input">
<input data-val="true" data-val-required="First Name required." id="FirstName" name="FirstName" type="text" value="" />
<!--tooltip here as necessary-->
<span class="field-validation-valid" data-valmsg-for="FirstName" data-valmsg-replace="true"></span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
Visually, this is what I am after:
A couple of things to note:
The "*" (required code) and tooltip are shown under certain conditions.
Styling will not be a problem - I am focused on structure here.
I used different helpers for the inputs, so the code inside each (and the name, i.e., Name v. FirstName) is different and not the problem.
The <table> is inside a <div> that's floated left. I have some static content on the right which is not the problem (it shows up under the validation in the second image), so that's also not important.
I am not good with the TagBuilder and have just been making a mess of everything. Any help is greatly appreciated. Thanks!
There you go:
public static IHtmlString MyEditorFor<TModel, TProperty>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression
)
{
var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, html.ViewData);
var wrapper = new TagBuilder("div");
wrapper.AddCssClass("field-wrapper");
var table = new TagBuilder("table");
table.Attributes["border"] = "0";
var tbody = new TagBuilder("tbody");
var tr = new TagBuilder("tr");
var td1 = new TagBuilder("td");
td1.Attributes["width"] = "40%";
var label = new TagBuilder("div");
label.AddCssClass("field-label");
if (metadata.IsRequired && !metadata.IsReadOnly)
{
if (metadata.ModelType != typeof(bool))
{
label.InnerHtml += "* ";
wrapper.AddCssClass("required");
}
}
label.InnerHtml += html.LabelFor(expression);
td1.InnerHtml = label.ToString();
var td2 = new TagBuilder("td");
td2.Attributes["width"] = "60%";
var input = new TagBuilder("div");
input.AddCssClass("field-input");
input.InnerHtml += html.EditorFor(expression);
if (!string.IsNullOrEmpty(metadata.Description))
{
input.InnerHtml += html.TooltipFor(expression);
}
input.InnerHtml += html.ValidationMessageFor(expression);
td2.InnerHtml = input.ToString();
tr.InnerHtml = td1.ToString() + td2.ToString();
tbody.InnerHtml = tr.ToString();
table.InnerHtml = tbody.ToString();
wrapper.InnerHtml = table.ToString();
return new HtmlString(wrapper + Environment.NewLine);
}

How To avoid that an ascx template overwrites the original style, class, maxlength, etc. attributes?

Main issue: Adding to all(!) textboxes in a MVC2 solution/project a js/jquery method.
Basics: How to call an ascx template?
Solved, see here
(Thanks to Darin Dimitrov!)
BUT...
Means the ascx template "Test.ascx" will be called like here shown:
<%: Html.EditorFor(model => model.Firstname, "Test", new { style = "float: left; width: 4.1em;", maxlength = "4" })%>
The "Test.ascx" template adds the js/jquery function.
Works fine, np so fare.
But: The "original" attributes style="..." maxlength="..." will be lost/overwritten.
My current solution / my "Test.ascx" file:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<input type="text"
id="<%: ViewData.TemplateInfo.GetFullHtmlFieldId(string.Empty) %>"
name="<%: ViewData.TemplateInfo.HtmlFieldPrefix %>"
value="<%: ViewData.TemplateInfo.FormattedModelValue %>"
style="<%: ViewData["style"] %>"
class="<%: ViewData["class"] %>"
maxlength="<%: ViewData["maxlength"] %>"
onkeyup="Foo(this); return false;"/>
This works really fine and returns / renders it perfectly:
<input type="text"
id="Firstname"
name="Firstname"
value=""
style="float: left; width: 4.1em;"
class=""
maxlength="4"
onkeyup="Foo(this); return false;"/>
Note that here are the style="..." and maxlength="..." attributes are well handled!
I use currently the "standard" < input ... /> tag because I didnt get this to work:
<%
String tmpConfig = " new {onkeyup=\"Foo(this); return false;\"";
String style = this.ViewData["style"] as String;
if (!String.IsNullOrEmpty(style))
tmpConfig += ", style=\"" + style + "\"";
String #class = this.ViewData["class"] as String;
if (!String.IsNullOrEmpty(#class))
tmpConfig += ", class=\"" + #class + "\"";
String maxlength = this.ViewData["maxlength"] as String;
if (!String.IsNullOrEmpty(maxlength))
tmpConfig += ", maxlength=\"" + maxlength + "\"";
tmpConfig += " } ";
%>
<!-- doesnt work: -->
<%: Html.TextBox( string.Empty,
ViewData.TemplateInfo.FormattedModelValue,
// new { onkeyup = "Foo(this); return false;" } // the style, class, maxlength attributes will be lost / not rendered!
// new { tmpConfig } // result would be the attribut: tempConfig=" onkeyup..."
// new tmpConfig // result: error, tmpConfig isnt object
Html.Encode(tmpConfig) // doesnt work ...
)%>
This renders finally the same but WITHOUT the style="..." and maxlength="..." attributes!
But I want to use the <%: Html.TextBox(...) %> instead of the < input ... /> tag!
Because this would give me the possibility to avoid an empty attribute like class="" and like before shown.
Where is my mistake?
You have to supply the Html.TextBox with a dictionary of String,Object to hold all attributes
<%
var dict = new Dictionary<string, object>();
String style = this.ViewData["style"] as String;
if (!String.IsNullOrEmpty(style))
dict.Add("style", style);
String #class = this.ViewData["class"] as String;
if (!String.IsNullOrEmpty(#class))
dict.Add("class", #class);
String maxlength = this.ViewData["maxlength"] as String;
if (!String.IsNullOrEmpty(maxlength))
dict.Add("maxlength", maxlength);
dict.Add("onkeyup", "Foo(this); return false;");
%>
<%: Html.TextBox( string.Empty,
ViewData.TemplateInfo.FormattedModelValue,
dict
)%>

Resources