Parse and modify HTML from MVC Model using jQuery / Javascript - c#

This needs to be done on the client (please trust me, I'll save the explanation).
I have a razor view that renders partial views in a loop. viewModel is just another class.
#{
foreach (ObjectName objInstance in Model.Collection)
{
#Html.Parial(partialViewName, viewModel)
}
}
And the partial view ultimately renders HTML from a viewModel property (this obviously occurs many times since this partial view is rendered in a loop).
#Html.Raw(Model.GetPropertyTextValue())
The HTML from GetPropertyTextValue() can look something like this:
<ul>\r\n<li>2,3,6-Tri-O-methyl-D-glucose (<a target=\"_blank\" href=\"http://www.blahblah.com/pubmed/12345?name=value\">1</a>) , 2,3,6-tri-O-methyl-D-mannose, 3-O- blah blah</ul>
I need to take the value in Model.GetPropertyTextValue and parse through it, replacing the entire URL (to blahblah.com) with a different link (but keep any numbers like the "12345"). Such URLs could appear multiple times in the string.
I believe this should be done with jQuery/Javascript, but I'm not sure how to approach it. Can anyone please suggest how to go about this? Thank you.

If you absolutely have to do on the client side, you can try something like this. First wrap your generated content with an identifiable div, so that you will be able to act within that container only:
<div id="items-to-parse">
#{
foreach (ObjectName objInstance in Model.Collection)
{
#Html.Partial(partialViewName, viewModel)
}
}
</div>
Then create a simple script that searches for all links within that container and changes their href attribute:
$('#items-to-parse a').each(function() {
$(this).attr('href', 'http://the.new.url');
});

Related

MVC 5 Razor view not rendering HTML within if condition

This is the MVC 5 razor view code: ForestView.cshtml
#model Forest.Tree
#{
var resultHtml = string.Empty;
}
<div id="divTreeSearch>
#(Html.Kendo().PanelBar().
Name("panelbar")
.Items(panelbar =>
{panelbar.Add()
.Content(#<text>#Html.Partial("_TreeSearch", Model, ViewData)</text>);}))
</div>
<div id="divTreeSearchResult">
#if(Model.TreeResultObj != null)
{
resultHtml = Html.ContentFromPartial("_TreeReport", Model.TreeResultObj);
#Html.Raw(resultHtml); -- Not working
#Html.Raw(HttpUtility.HtmlDecode(resultHtml)); -- Not Working
Html.Raw(resultHtml); -- Not working
Html.Raw(HttpUtility.HtmlDecode(resultHtml)); -- Not Working
Model.resultStringSaved = resultHtml;
#Html.DisplayText("resultStringSaved"); -- Not Working
#Html.Raw("<text>Test</text>") -- Even this is not working
}
#Html.Raw(Model.resultStringSaved) -- Not Working
#Html.Raw(HttpUtility.HtmlDecode(Model.resultStringSaved)) -- Not Working
#Html.DisplayText("resultStringSaved") -- Not Working
#Html.Raw("<text>Test</text>") -- This is Working
</div>
ForestView.cshtml - #model Forest.Tree
_TreeSearch.cshtml - #model Forest.Tree
_TreeReport.cshtml - #model Forest.SearchData.Results
The projerty TreeResultObj in the model Forest.Tree is of type Forest.SearchData.Results
The ForestView.cshtml is the main view which loads initially and displays the search inputs from the _TreeSearch partial
When search criteria entered and a 'search' button is clicked (all this is from the _TreeSearch) - a ajax call is make and the TreeSearch(id tree) action is called
The action again returns the main 'ForestView' - however now the model property 'TreeResultObj' is populated. so the code within the 'if conditon' in the 'ForestView' executed and calls another partial to get the content back as HTML string, which is saved in the 'resultHtml' variable
At this point I can see the Html Sting like "<Text>blah blah blah</text>"
However trying to display the HTML string below the search panel in the main 'ForestView' is not working - I have tried almost every possible way.
Any text within the if condition is not rendered - it is an ajax call so there is no page refresh - I can see the HTML string value and also save it as a Model property but cannot get to display it in the main view.
Any help would be much appreciated. Thanks in advance.
At that point, you are just invoking a method and ignoring the result. Try:
#: #Html.Raw(resultHtml)
The #: switches to output mode. Note: if you had used something that was clearly markup, it would have switched automatically. For example:
<div>#Html.Raw(resultHtml)</div>
I was also facing the same issue but I could make it to working. I wanted to open a div start and conditionally end it within a for loop.
Put any dummy HTML element before the #Html.Raw
<div id="divTreeSearchResult">
#if(Model.TreeResultObj != null)
{
resultHtml = Html.ContentFromPartial("_TreeReport", Model.TreeResultObj);
<span>dummySpan</span>
#Html.Raw(resultHtml);
#Html.Raw(HttpUtility.HtmlDecode(resultHtml));
#Html.Raw(resultHtml);
#Html.Raw(HttpUtility.HtmlDecode(resultHtml));
Model.resultStringSaved = resultHtml;
#Html.DisplayText("resultStringSaved");
#Html.Raw("<text>Test</text>")
}
#Html.Raw(Model.resultStringSaved)
#Html.Raw(HttpUtility.HtmlDecode(Model.resultStringSaved))
#Html.DisplayText("resultStringSaved")
#Html.Raw("<text>Test</text>")
</div>
After you place a dummy html element there all the below #Html.Raw will work
Happy Coding
Tarak
1 - And the content from render should be violating HTML syntax and Razor normally doesn't render wrong HTML content.
2 - Enclose the HTML.Raw(resultHtml) into dev.
After zillions of tests in a full-day:
Change use of:
#Html.Raw(Html.DisplayFor(modelItem => item.Mensagem))
by:
#Html.Raw(Html.DisplayTextFor(modelItem => item.Mensagem))
You will get the rendered HTML withou any <>!!!

Sending data to header of _Layout.cshtml regardless of what page the user is on

I have a notifications badge in the header of my _Layout.cshtml file. This is so I can constantly show the user their notifications regardless of what page they are on.
Notifications <span class="badge">#ViewData["NotificationCount"]</span><br>
However, I am wondering if there are better ways to do this. It seems like I need to do the following at the top of every single one of my views:
#{
ViewData["ReminderCount"] = Model.Notifications.Count();
}
This also means I need to have a list of notifications as part of every single page ViewModel throughout my application.
Am I missing something here? I feel like there has to be a better way to handle things like this in ASP.NET MVC.
The ideal thing would be to load the user's notifications one time as soon as they login and go to their dashboard and then continue to show that number across all pages unless they check the notifications, then the number clears.
What you can do is create an action and against that action create a partial view and in the _Layout.cshtml call it, this will save you from code duplication and ViewData :
You action code will look like:
public ActionResult Notifications()
{
var model = Model.Notifications.Count();
return View("_NotificationsPartial",model);
}
your partial view _NotificationsPartial.cshtml would be like:
#model System.Int32
<a href="#">Notifications
<span class="badge">#Model</span>
Now you just need to call it in your _Layout.csthml:
#Html.Action("Notifications","SomeController")

Best way to display a JQuery Dialog with ASP.NET MVC data bound View Model

Imagine a simple page with a list of users. Selecting a user displays a JQuery modal dialog with various details that can be edited. Something like:
#model IEnumerable<UserRole>
#if (Model.Any())
{
foreach (var user in Model.Users)
{
Details
}
}
I'll have more specific examples through the post but what I'm looking for is a general 'experienced' opinion on what's the best way to load and display a Model bound Partial View as a JQuery dialog box.
I know how to do it code-wise but I think there must be a better way. I believe the common known ways to do it are not very efficient.
My rule and what I would like is for all code associated to a partial view popup to be kept in that partial view. I would like my popup to be structured something like the following UserDetails partial view:
#model User
<script src="#Url.Content("~/Scripts/UserScripts.js")" type="text/javascript"></script>
<div id="placeholder">
...The modal dialog content...
</div>
This way when another developer gets to look at it one will easily be able to piece it all together.
So as far as I know there are two ways to display a partial view as a Dialog and I have a problem with both of them:
1) Use the Partial view structure I displayed above, pre-load the div dialog from the master page by using #Html.Partial("UserDetails", new User) and then, when I need the dialog to be displayed populated with user data execute an Ajax call to an ActionMethod that will re-populate the partial view's model with needed data and re-render it with JQuery .html() method.
Once the partial view/dialog is loaded with data I simply display it with JQuery .dialog('open')
Great, this works but there are a few problems with this logic:
a) I'm loading the same partial view twice ( first blank , second loaded with data )
b) Content of the Placeholder DIV flashes on the screen when the master page is being loaded. Setting DIV to display:none won't work here before when .html() method triggers it will load the partial view with that display:none tag in it and the popup will be presented as a blank window.
c) When the page is requested, if large, it takes some time for the page to show
2) Instead of having in the partial View I can place a blank <div id="placeholder"></div> on the master page and then load the partial view content into that div with ajax or as I'm doing it now with JQuery :
var url = "/MyController/MyAction/" + Id;
$("#palceholder").load(url).dialog('open');
Again, it works but there are a few big problems I see with this way:
a) It breaks my "keeping it all together rule". When looking at , without some searching around another developer will have no idea what partial view will be loaded in this Div.
b) All Javascripts for the partial view popup will now need to be referenced in the master page, instead of a the partial view itself.
c) When the page is requested, if large, it takes some time for the page to show
The bottom line question is what do you think is the best way to display the model-bound populated partial view as a Modal Dialog while keeping the code organized ?
My perfect scenario would be to pre-load all partial view fields and then, when the request is made for the dialog to show populated with Data somehow a model bind pre-loaded partial view to the new JSon set of data, without loading/re-loading all partial view fields.
Is there a way ?
P.S. One of the things I tried is to pre-load my partial view fields with #Html.Partial("UserDetails", new User) and then use JQuery .replaceWith() method to replace Div contents but I couldn't get it to work unfortunately.
Any thoughts are appreciated. No ideas as are bad ideas.
Thanks.
Nothing wrong with having part of your code load in partial, and then just updating the partial container with a return from action.
<div id="ParitalContainer">
#Html.Partial("_PartialView", Model.PartialModel)
</div>
Or, you can consider a scenario to work with JSON data. Namely, have all your data loaded async by calling a $.ajax or $.getJSON. Your action result would return JsonResult and then you can just update the elements you want.
Furthermore, you could look into using Knockout.js if you want more robust solution. This is what I would do if I wanted "keeping it all together" approach.

Applying knockout to a filled form without wiping the form's data because of an empty viewmodel?

I have a script that initializes knockout by applying an empty viewmodel to my form.
When the user enters data in the form, the viewmodel is updated accordingly - that is the expected behavior.
JavaScript
var viewModel = {
myField : ko.observable(),
init : function (somedata) {
...
ko.applyBindings(this, container);
},
...more stuff...
}
Partial
<%:Html.TextBoxFor(x => x.MyField, new Dictionary<string, object> { { "data-bind", "value: myField, valueUpdate: 'keyup'" } })%>
When i call my javascript to apply knockout in an existing form, the value already in MyField (and then in my input field) is wiped with the empty data in my knockout viewmodel (that viewmodel is part of my javascript residing in another file).
When the page is loaded, MVC makes sure that the MyField input element is filled with the previously entered data from MyField. When init is called, that field is overwritten with the value in the viewmodels myField which is empty, as it's a static JavaScript file.
How do I solve this problem?
I have tried two approaches to handling something like this:
1- If you have good control over your server-side (which it looks like you do), then you could consider outputting javascript in your view that sets the observables to the proper values.
Would be somewhat similar to this: http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/. At least this part of it:
<script type="text/javascript">
var initialData = <%= new JavaScriptSerializer().Serialize(Model) %>;
</script>
You could either do that and integrate the initialData before calling applyBindings or possibly just emit the javascript that directly sets your observables to the proper values.
2- If you don't have good control over your server-side (a 3rd party control or just something that you are unable/unwilling to change at this point), then you could consider initializing your viewModel after the page has loaded, but before you call applyBindings. You would find the elements that you are interested, read meta-data or values off of them, and set your observables prior to letting the bindings do their work.
Maybe one complication for you is that sounds like currently all of your javascript is in a separate file.
I am not sure but I think ko.observable requires a parameter.

How can I add extra partial views, depending on a dropdownlist selection, in ASP.NET MVC and jQuery?

I have a form to which I want to add extra fields depending on the value of a dropdown list. They would be sets of fields and I was thinking on dynamically changing a div's content with the html from a render partial, which would render a PartialView with the fields I want.
Right now the code for the drop down list is the following
<p>
<label for="CatalogItem.Type"> Type: </label>
<%=Html.DropDownList("CatalogItem.Type",Model.Types, "Choose Type") %>
</p>
<div id = "ExtraInfo">
</div>
And I want to put the extra stuff (fields specialized for the type) in the ExtraInfo div. What would the jQuery code be for this?
Thanks!
#Tony has the right approach but instead of putting your RenderPartial html right into the ".html("add html code inside div here")" you may want to do an ajax call. That way the user isn't downloading a bunch of html he/she may not even see.
something like so:
if ( myval == "someValue")
{
$("#ExtraInfo").load("/dynamic-stuff/1")
}
else if ( myval == "someOtherValue")
{
$("#ExtraInfo").load("/dynamic-stuff/2")
}
This also assumes you have a route set up to handle a url like "/dynamic-stuff/2" and responds with the correct partial view.
First add a css class selector to your dropdown, lets call it 'mydropdown' for now
use something like this:
<script language=”javascript” type=”text/javascript” >
function addtoDiv()
{
$(document).ready(function() {
var myval=$(”#mydropdown”).val(); // get value of dropdown
if ( myval == "somevalue") // check myval value
{
$("#ExtraInfo").html("add html code inside div here"); // add code based on value
}
}}
</script>
Do you need to dynamically add fields? You can add fields with JQuery by doing:
$("").attr("id", "test").addClass("FormLabel").appendTo("#parentElement");
$("").attr("id", "testinput").attr("type", "text").appendTo("#parentElement");
In this way, you can create the fields programmatically.
As an alternative, you can create a JQuery partial view. Create an action method that returns an instance of this partial view, and call that action method using
$.get("/<controller>/<actiontoreturnpartialview>", function(data) {
$("#ExtraInfo").html(data);
});
It makes it easier because then you can rely on server-side logic to render the UI, though I tend to use the client-side approach.
Alternatively, you can create your own HTML helper to do this all, but that would be a lot of work.
HTH.

Categories