How to Simplify this Code Kludge - asp.net-mvc

Input:
Id, PartId, Name
1, 1, Head
1, 2, body
1, 3, Tail
2, 1, Head
2, 2, Leg
Output Display:
- Head, Body, Tail [Delete(1)]
- Head, Leg [Delete(2)]
My Code:
<ol>
<%
int prev = -1;
foreach (var item in t)
{
if(prev != item.ResponseId){
if (prev != -1)
{%>
<%= Html.ActionLink("[replacethis]", "RemoveResponse", new { id = item.ResponseId })
.Replace("[replacethis]", "<img src=\"../../Content/images/delete_icon.gif\" class=\"borderlessImage\" title=\"Remove\"/>")%>
</li>
<%} %>
<li>
<% }
else {
%>, <%
} %>
<%= Html.Encode(item.ResponsePartValue) %>
<% prev = item.ResponseId;
} %>
<%= Html.ActionLink("[replacethis]", "RemoveResponse", new { id = prev })
.Replace("[replacethis]", "<img src=\"../../Content/images/delete_icon.gif\" class=\"borderlessImage\" title=\"Remove\"/>")%>
</li>
</ol>
Questions:
What are the ways to refactor this?
Any MVC tricks I am missing?

Well, first of all you could create an HtmlHelper that renders image links for you, instead of generating the anchor tags first and then replacing their content with an image.
Take a look at here.
Also you don't have to use <%= every time you need to ouput some text. If you already have opening code blocks (ie <%), then you can just use Response.Write method to output what you want. In cases like yours, that'll most likely look better than %> <%=.
Though, I admit I don't exactly know what you are listing here and how you want it to display. But following your algorithm, I guess this is what I would have done :
<ol>
<%
int prev = -1;
foreach (var item in t) {
if(prev != item.ResponseId) {
if (prev != -1) {
Response.Write(Html.ImageLink("../../Content/images/delete_icon.gif", "RemoveResponse", new { id = item.ResponseId, #class ="borderlessImage", title = "Remove" }) + "</li>");
}
Response.Write("<li>");
}
else {
Response.Write(", ");
}
prev = item.ResponseId;
Response.Write(Html.Encode(item.ResponsePartValue));
} %>
<%= Html.ImageLink("../../Content/images/delete_icon.gif", "RemoveResponse", new { id = prev, #class ="borderlessImage", title = "Remove" }) %>
</li>
</ol>

I would put everything into a dictionary, it would make your logic more logical :-)
Something like:
IDictionary<int, List<Part>> partsDictionary = new Dictionary<int, List<Part>>();
Where the int key is your id and then the value of type List would be your individual parts.
Then put the logic into a HtmlHelper extension.
E.g. (Although I don't know what you're doing, view code doesn't match you db model at the top. This should give you an idea)
public static string PartsList(this HtmlHelper html, IDictionary<int, List<Part>> partsDictionary)
{
if (partsDictionary.Count == 0)
return "";
StringBuilder toReturn = new StringBuilder("<ol>");
foreach (KeyValuePair<int, List<Part>> kvp in Model.PartsDictionary)
{
toReturn.Append("<li>");
//Individual part links
IList<string> partsLinks = new List<string>();
foreach (Part part in kvp.Value)
partsLinks.Add(html.ActionLink(part.PartName, "ActionName", new { #id = part.Id }));
toReturn.Append(string.Join(", ", partsLinks.ToArray()));
//Part category(?) link
toReturn.Append(html.ActionLink("Delete", "ActionName", new { #id = kvp.Key }));
toReturn.Append("</li>");
}
toReturn.Append("</ol>");
return toReturn.ToString();
}
Or something like that ;-)
HTHs
Charles

Related

Is it possible to create dynamic label name for nested attributes created by cocoon gem?

I created a form with nested attribute using cocoon gem. When link_to_add_association was clicked, it will append the label and text_field. However, I would want the label name to be dynamic in the sense that the label name for every label will be different. For instance, I would like to have 'A:' as label name for first label, 'B:' for the second label, and so on and so forth. Is it possible to achieve this?
/_mcq.html.erb
<%= f.fields_for :mcq_options do |option| %>
<%= render 'question_paper_generations/mcq_option_fields', f: option %>
<% end %>
<div><%= link_to_add_association 'Add Option', f, :mcq_options %></div>
/_mcq_options_fields.html.erb
<%= f.label :option %>
<%= f.text_field :option, class: 'form-control' %>
Assuming your nested fields are inside a div "#options"
$(document).ready(function() {
var next_label = "A";
def next_char(c) {
next = "A";
if (c != "Z") {
next = String.fromCharCode(next.charCodeAt(0) + 1);
};
return next;
};
$('#options').on('cocoon:after-insert', function(e, added_option) {
added_option.find("label").first().text(next_label+":");
next_label = next_char(next_label);
});
});
EDIT
To use roman numbers: (taken from: https://stackoverflow.com/a/32851198/3372172). Note. There are comments saying this could not work because objects have no order. You should test it, or choose other implementation (you can find similar solutions in the same link).
$(document).ready(function() {
var next_label = 1;
function romanize(num) {
var lookup = { M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1};
var roman = '';
var i;
for ( i in lookup ) {
while ( num >= lookup[i] ) {
roman += i;
num -= lookup[i];
}
}
return roman;
};
$('#options').on('cocoon:after-insert', function(e, added_option) {
added_option.find("label").first().text(romanize(next_label)+":");
next_label++;
});
});

How do I manipulate model data in the background for a data visualization in a view?

I'm currently using Rails with D3.js to create a data visualization using model data. What I've done in the view is to extract the model data, aggregate it into categories, and set it as an array of hashes that D3.js can read.
However, I think this slows down page load and I'd like to bring this logic into the background somehow while maintaining instant syncing between model data and the graph (which means when someone updates the model data, the visualization automatically updates itself). Is there a way to do it that gels with the Rails framework?
Ultimately, my goal is really to speed up page load for scale as in the future the dataset will get larger. So any alternative suggestions are welcome.
This is the code pertaining to the data manipulation:
<% #startup_fundings.each do |startup_funding| %>
<% if startup_funding.startup %>
// check if startup belongs to Southeast Asia
<% if countries.any? { |country| startup_funding.startup.locations.include?(country) } == true && startup_funding.amount != nil %>
// split string by individual keywords
<% funding_round_split = [] %>
<% funding_round_split = startup_funding.startup.product_markets.split(", ") if startup_funding.startup.product_markets %>
// check if vertical exists in funding round
<% funding_round_split.each do |funding_round| %>
<% [ ['Hr', 'HR'], ['Bi', 'BI'], ["Crm", "CRM"], ["Lbs", "LBS"], ["Saas", "SaaS"]].each do |replacement|
funding_round.gsub!(replacement[0], replacement[1])
end %>
var fundingRound = "<%= funding_round %>";
var exists = false;
for (i = 0; i < graphData.length; i++) {
if (graphData[i].vertical == fundingRound) {
exists = true;
var order = i;
}
}
// if vertical does not exist, add new vertical
if (exists == false) {
graphData.push({'vertical': fundingRound,
'amount': <%= startup_funding.amount %>,
'deals': [{'company': '<%= startup_funding.startup.company_name %>',
'url': '<%= startup_funding.startup.company_name.downcase.delete(' ').gsub('.', '-') %>',
'amount': <%= startup_funding.amount %>}]
});
} else {
// if vertical exists, add up funding amount
graphData[order].amount = graphData[order].amount + <%= startup_funding.amount %>;
var duplicateRound = false;
// cycle through all funding rounds in a vertical
for (j = 0; j < graphData[order].deals.length; j++) {
// if company in funding round matches iterative company name, indicate it exists
if (graphData[order].deals[j].company == '<%= startup_funding.startup.company_name %>') {
duplicateRound = true;
var order1 = j;
}
}
if (duplicateRound == false) {
graphData[order].deals.push({
'company': '<%= startup_funding.startup.company_name %>',
'url': '<%= startup_funding.startup.company_name.downcase.delete(' ').gsub('.', '-') %>',
'amount': <%= startup_funding.amount %>
});
} else {
graphData[order].deals[order1].amount = graphData[order].deals[order1].amount + <%= startup_funding.amount %>;
}
}
<% end %>
<% end %>
<% end %>
<% end %>

Char equals to String

I am trying to check if a value from database table is equal to Char, in the view I tried
the following:
<% if (Model.MethodOfPayment.ToString().Equals("W") == true)
{
%>
Wire
<%} %>
<%else
{ %>
<% if (Model.MethodOfPayment.ToString().Equals("C") == true)
{
%>
Cheque
<%} %>
<%} %>
Did not work!
In the controller to send the output to PDF Form: I tried the following:
string MyString = order.MethodOfPayment.ToString();
if (MyString == "W")
{
pdfFormFields.SetField("MethodOfPayment", "W");
}
else
{
if (MyString == "W")
{
pdfFormFields.SetField("MethodOfPayment", "C");
}
}
Did not work either.
Thanks in advance.
How about:
if (Model.MethodOfPayment == 'W')
If this doesn't work it simply means that the MethodOfPayment property doesn't equal to the W character. Try debugging your code to see exactly to which value it equals.

MVC- Not found page http://local.../Articles/1

Can you help me? I used contoller HomeConrtoller.cs for view, edit and delete articles. All work, but When I go to the next page I get error don't found http://local.../Articles/1
public ActionResult Articles(int? page)
{
var viewData = mybaseRepository.FindAllArticles();
const int pageSize = 10;
var paginatedArticle = new PaginatedList<Article>(viewData, page ?? 0 , pageSize);
ViewData["Page"] = paginatedArticle.PageIndex;
return View(paginatedArticle);
}
In Global.asax.cs
routes.MapRoute(
"Articles",
"Articles/{page}",
new { controller = "Home", action = "Aticles", page = (int?)null }
);
paging in Articles.aspx
<% if (Model.HasPreviousPage) { %>
<%= Html.RouteLink("предыдущая <<<",
"Articles",
new {page = (Model.PageIndex - 1) })%>
<% } %>
<% if (Model.HasNextPage) { %>
<%= Html.RouteLink(">>> следующая",
"Articles",
new {page = (Model.PageIndex + 1) })%>
<% } %>
<div>
You wrote "Articls" in the URL but "Articles" when defining the route. Would that be it?

What's the most elegant way of using a partial view to render a comma delimited set of items?

I need to render a list of Person objects, say, in a comma delimited format using a Partial View in ASP.NET MVC. My problem is that when rendered using the following code:
<% foreach (var person in Model) { %>
<%= Html.ActionLink<PersonController>(c => c.Edit(person.PersonID), Html.Encode(person.Name)) %>,
<% } %>
I get a trailing comma after the last item. What's the most elegant/least stupid way to have this list of persons rendered without the last comma?
My two options so far, in no order, would be:
Use JavaScript to remove the trailing comma on the client side
Manually create the list using code, instead of markup, in the partial view
Neither of these options appeal to me - any ideas?
Thanks!
How about:
<%=String.Join(
",",
Model.Select(
person=>
Html
.ActionLink<PersonController>(
c => c.Edit(person.PersonID),
Html.Encode(person.Name)
)
)
.ToArray()
)%>
(untested)
<% bool first = true;
foreach (var person in Model) {
if (first) first = false; else Response.Write(","); %>
<%= Html.ActionLink<PersonController>(c => c.Edit(person.PersonID), Html.Encode(person.Name)) %>
<% } %>
I think, instead of a foreach, you're going to have to iterate through the persons collection using a conventional for loop. That way, you can detect the last iteration through the loop and avoid the last comma.
<% { int count=Model.Persons.Count();
for (int i=0; i< count; i++) { %>
<%= Html.ActionLink<PersonController>(c => c.Edit(Persons[i].PersonID), Html.Encode(Persons[i].Name)) %>
<% if (i < count) { Response.Write(","); }
} %>
Uses LINQ Aggregate to concatenate comma-delimited links without appending a trailing comma.
<%= Model.Select(person => Html.ActionLink<PersonController>(c => c.Edit(person.PersonID), Html.Encode(person.Name))
.Aggregate((links, link) => links + ", " + link) %>

Resources