I'm trying to make an HTML helper that will allow for any text inside a using to be consumed and not written to the final page.
The Razor:
<div>
<p>
Before Inline
</p>
#using (Html.IncludeInlineScript("testScript"))
{
<script type="text/javascript">
alert("hello world");
</script>
}
<p>
After Inline
</p>
</div>
The resulting HTML:
<div>
<p>
Before Inline
</p>
<script type="text/javascript">
alert("hello world");
</script>
<!-- Top of Dispose -->
<!-- Bottom of Dispose -->
<p>
After Inline
</p>
</div>
The Helper Extension Method:
public static ScriptWrapper IncludeInlineScript(this HtmlHelper helper, string scriptName)
{
return new ScriptWrapper(helper);
}
The Wrapper:
public class ScriptWrapper : IDisposable
{
private HtmlHelper _helper;
private TextWriter _originalWriter;
private StringBuilder _scriptContents;
public ScriptWrapper(HtmlHelper helper)
{
_helper = helper;
_originalWriter = _helper.ViewContext.Writer;
_scriptContents = new StringBuilder();
_helper.ViewContext.Writer = new StringWriter(_scriptContents);
}
public void Dispose()
{
_originalWriter.WriteLine("<!-- Top of Dispose -->");
_helper.ViewContext.Writer.Flush();
_helper.ViewContext.Writer = _originalWriter;
_originalWriter.WriteLine("<!-- Bottom of Dispose -->");
}
}
The problem here is that despite setting the ViewContext.Writer to a new TextWriter it still is writing to the original writer. Clearly the dispose is being invoked in the right order as the top of the dispose is after the script. While debugging after the new writer has been set the <script> block is not in the stream, however at the time of the dispose the <script>is now contained in the original writer.
Is the razor engine keeping a local copy of the writer and ignores the fact that it had been set to a different instance? This seems like a bug to me.
In case anyone wants a work around, since I'm impatient, I found a way to make this work. It's less than ideal, but it does the trick. If you modify the ScriptWrapper to be:
public class ScriptWrapper : IDisposable
{
private HtmlHelper _helper;
private string _originalString;
private StringBuilder _sb;
public ScriptWrapper(HtmlHelper helper)
{
_helper = helper;
_sb = ((StringWriter) _helper.ViewContext.Writer).GetStringBuilder();
_originalString = _sb.ToString();
_sb.Clear();
}
public void Dispose()
{
var contents = _sb.ToString();
_sb.Clear();
_sb.Append(_originalString);
}
}
You can accomplish the desired result of consuming anything inside the using(...) statement.
I would however still be curious to know if there is a way to do it without hacking a solution like this.
In your original (the question), it's tempting to think that because the markup is within the using block, somehow ScriptWrapper is consuming the markup
<script type="text/javascript">
alert("hello world");
</script>
but as you found out, it is not being consumed by the ScriptWrapper in any way. You are hoping that this markup would get "redirected" somewhere else, to the writer you create, but it isn't. Instead the writer you create is also used to write to the response stream, which is what the ViewContext.Writer is used for, whatever writer it happens to be.
So, rather than having ScriptWrapper consume the markup, Razor is interpreting it as markup like anything else in the view and is, after interpreting it, writing it to the Response stream. From Razor's perspective that markup is intended for the output, not for ScriptWrapper. Just having it enclosed in a using block, does not make the markup any less a part of the overall view.
You answer succeeds because you are not redirecting the markup to somewhere else (which doesn't work), but because you are editing what ends up in the response stream, effectively erasing the markup from the response stream.
I would argue that your solution is not a hack but a correct way of approaching this, given what you want to accomplish.
Related
I am struggling a bit with passing list of object to C# code from view. If I pass a simple string it works fine. but its not working for List. So I am sure I am missing something here.
View
<div class="row control-actions">
#{
List<MyObject> test = ViewBag.ObjectsList;
<button type="button" class="btn btn-primary btn-wide" onclick="addAllObjectsToExp('#test')">Add All</button>
}
</div>
<script type="text/javascript">
function addAllObjectsToExp(objList) {
$.post('#Url.Action("AddAllObjectsToExp", "ExportObjects")', { objectsList: objList},
function (result) {
$('#numbers').html(result);
});
}
</script>
Code
[HttpPost]
[OutputCache(Location = System.Web.UI.OutputCacheLocation.None, NoStore = false, Duration = 0)]
public int AddAllObjectsToExp(List<MyObject> objectsList)
{
foreach(MyObject obj in objectList)
{
//Do something here
}
//and return an integer
}
While debugging I can see that the #test variable is getting populated with the list of MyObject. But when I reach the code side its always null.
Please let me know if i am missing something here. Also tell me if more information is needed.
You're passing a C# object into a Javascript function. It doesn't know how to read that. You should serialize it into JSON before passing it in.
If you use Json.NET you can do it by
ViewBag.ObjectsList = JsonConvert.SerializeObject(yourlist);
Then you can continue as you were.
Some notes:
You should try to start using ViewModels instead of putting things in the ViewBag. On the Javascript side you should bind event handlers for things like clicking instead of using onclick as it would make your code much more manageable and reusable.
I have an asp.net mvc3 C# application. It uses a database. The database supports a soft-delete scenario due to requirements. The soft-delete scenario does not cascade. Records which have been flagged as deleted will be showed as grayed out. My issue is with how to do this without having to always surround each display from the model with some html markup and style.
For example:
<span #if(m.Box.isDeleted){
<text>style="background-color:gray"</text>
}>#m.Box.Name #m.Box.Description</span>
I can do that, but look at all the extra markup I have to do to literally every ViewModel object display.
What I would like to do is #m.Box.Name.AddMarkup() or something to that extent. Perhaps I could make a helper which took the item and then returned the correct markup such as
namespace place.Markup
public class Markup
{
public ModelItem(object o, property name)
{
//perhaps use reflection so the flag isnt passed all the time
//create markup with flag conditional decoration
//return markup
}
}
and then
#Markup.ModelItem(#m.Box.Description)
I am not sure how I should approach this, or if either of the suggested ways is how others approached this issue. What have you tried to display soft-deleted items? Is there a good tutorial for this somewhere? I couldn't really find much material on this subject.
Please note: the filtering of these objects is not an issue, they are intentionally being displayed and they need to be displayed in a manner which reflects the soft-delete.
I would make an htmlHelper based on one or many overloads of DisplayFor
Something like
public static MvcHtmlString DisplayDeleteCheckFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression, bool condition) {
var value = html.DisplayFor(expression).ToString();
var style=condition ? "style=\"background-color:gray\"" : string.Empty;
return MvcHtmlString.Create(
string.Format("<span {0}>{1}</span>", style, value));
}
The easier way to do it, is to take advantage of CSS. This is a really rough example.
<style>
.isDeleted .showGrayIfDeleted
{
background-color:gray;
}
</style>
In your Controller:
if (m.Box.isDeleted)
{
m.Box.listOfClasses.Add("isDeleted");
}
In your view:
<div class="#string.Join(" " , m.Box.listOfClasses)">
<span>blah</span><br/>
<span class="showGrayIfDeleted">This will be gray if deleted.</span><br/>
<span class="showGrayIfDeleted">#m.Box.Name #m.Box.Description</span><br/>
</div>
By taking advantage of the cascading affect of CSS you only need to apply a single class to a container html element to have all the internal elements have a different background.
What I've done for this is to have a CSS static helper class that is structured like so:
public static class ProductCssHelper
{
public static string IsDeleted(bool IsDeleted)
{
return IsDeleted ? "deleted" : String.Empty
}
public static string IsAction(bool IsActiveAction)
{
return IsAction ? "active-action" : "inactive-action"
}
}
In the razor:
<table>
<thead>
<tr>
<td>Date</td>
<td>Name</td>
<td>Is Deleted</td>
<td>Active</td>
</tr>
</thead>
<tr>
<td>Today</td>
<td>Bottle</td>
<td class="#(ProductCssHelper.IsDeleted(Model.IsDeleted))">Yes</td>
<td class="#(ProductCssHelper.IsActive(Model.IsActiveAction))">No</td>
</tr>
</table>
CSS definition not included but this will return the strings for the css classes defined.
I like this method as you can make a CSSHelper folder and inside that have as many helper classes as you need split up by controller or view/area so you have everything in one place.
Not originally my idea but I found the source where I got this idea:
http://www.arrangeactassert.com/asp-net-mvc-view-best-practices-keep-logic-out-of-your-views/
MAIN QUESTION
As the title says, i'm asking if anyone knows how to create a validation structure similar to silverlight's dataform. Any hints, materials , ideas would be welcome. I'd like to do a validation like this:
(You can check this out here)
DETAILS
I've been trying to something like the example above, but without results until now. Mostly because i dont' know how the validation message helper works. I've managed to a single validation message by taking the data-val-number attribute and set it into a link title (using a third party jquery plugin callled qTip to show a tooltip error message). However, i can't do the same thing if there are more than one validation.
So, is it possible to rewrite the validation message helper? I'd like to understand more how it shows the validation messages so i can put them on any html content. This would do the tooltip part and i could set as many messages as i want, with any formatting i desire.
And i'd like to be able to show the validation messages through any jquery events (mouseover, click, dblclick, ready, etc.). As far as i understand on it's actual implementation, the validation only occurs when the user changes focus from the actual input to another html element.
I highly recommend that you checkout the validation rules section in Professional ASP.NET MVC. It shows how to do exactly what you describe with ASP.NET MVC v1 and it works all the way up through v3.
The displayed user interface is slightly different; however, you can check the output and write your own CSS to make it look just like your screenshot.
For a quick example:
Action Result
try
{
// code
}
catch
{
foreach (var issue in std.GetRuleViolations())
{
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
}
Model
public IEnumerable<RuleViolation> GetRuleViolations()
{
if (!String.IsNullOrEmpty(this.Phone) && !Utility.IsValidPhoneNumber(this.Phone))
{
yield return new RuleViolation("Phone is invalid. Try this format: ###-###-####.", "HomePhone");
}
yield break;
}
Edit View
<%
using (Html.BeginForm())
{
%>
<fieldset>
<legend>Edit</legend>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<%= Html.TextBox("Phone", Model.Phone)%>
<%= Html.ValidationMessage("Phone", "*")%>
<!-- more fields go here -->
</fieldset>
<% } %>
rule violation class -- lifted from link
public class RuleViolation
{
public string ErrorMessage { get; private set; }
public string PropertyName { get; private set; }
public RuleViolation(string errorMessage)
{
ErrorMessage = errorMessage;
}
public RuleViolation(string errorMessage, string propertyName)
{
ErrorMessage = errorMessage;
PropertyName = propertyName;
}
}
I'm fairly new to web development.
Basically I'm using a HTML5 canvas to render content, and javascript functions which clear and render to the canvas.
I'm hoping to call a web service to get data that would affect content, which I believe is most easily done from C#, and was wondering how I could use this to periodically call a javascript function that would update values that are being rendered to the canvas?
To make this clearer, I have something like this:
Oh I'm also using jquery by the way, partly because I was told to.
Page.asp:
<body>
<form id="form1" runat="server">
<canvas id="canv" width="200" height="200">
Cannot display canvas because your web browser is a fail.
</canvas>
<script type="text/javascript">
var ctrls = new Array();
$(document).ready(function () {
var canvas;
var context;
canvas = $("#canv").get(0);
if (!canvas) {
alert("Failed to get canvas");
return;
}
context = canvas.getContext('2d');
if (!context) {
alert("Failed to get context");
}
var x;
x = new Object();
x.value = 0;
x.parent2d = context;
ctrls.push(x);
window.setInterval(Render, 25);
});
function Render() {
var i;
for (i = 0; i < ctrls.length; i++) {
ctrls[i].parent2d.clearRect(0, 0, 200, 200);
//Draw stuff.
}
}
function Update(newVal) {
var i;
for (i = 0; i < ctrls.length; i++) {
ctrls[i].value = newVal; //For example
}
}
</script>
</form>
</body>
What is the easiest (if it's even possible) way to call the Update(newVal) function from C# (Page.asp.cs), and how could this be set up to be done periodically?
What would be the limitations on what object(s) could be passed to this function?
Dictionary<String, Double>
would be very useful.
When exactly does update need to be called after page load? If it's every five seconds, it might be easier to carefully set up some sort of Javascript-based interval (making sure you are checking certain conditions to ensure that the interval quits upon any error conditions).
Depending on what data you have, you may want to setup a method in C# that given certain POST or GET parameters passed to it via a jQuery $.ajax() call.
For instance:
$(document).ready(function() {
var intervalId = setInterval("runUpdate()", 5000);
});
function runUpdate() {
//Make an ajax call here, setting up a variable called 'data'
update(data);//Data is newVal, which was setup in the ajax call above
}
That code would run the update function every five seconds.
Firstly from Javascript I would normally avoid using xml based web services. The returned data is XML which has lots of unessesary tags which can cause transfer to be slow.
Instead look at JSON. Its much faster. However not so easy for authentication. To help you understand its power both twitter and facebook use it in there APIs.
JQuery can consume JSON really easy
$.getJSON(someurl, function(jData) {
//use returned data
}
You might find it useful to read this:
http://encosia.com/2008/03/27/using-jquery-to-consume-aspnet-json-web-services/
Hope this helps.
I'm creating a tabcontainer that shows information from partials. The code that i've created is as follows:
//Entering extension method, m_helper is of type HtmlHelper
foreach (var tab in m_tabList)
{
sb.AppendLine("<div class='tabContent'>");
m_helper.RenderPartial(tab.PartialName);
sb.AppendLine("</div>");
}
//Returning sb.ToString to the caller method
This will not work because the renderpartial writes directly to the output stream. I cannot render the partial to a string either. to add it to the stringbuilder object.
Any suggestions?
use
m_helper.Partial(tab.PartialName);
This will return MvcHtmlString.