How to create a declarative HTML helper in MVC 5 - asp.net-mvc-5

my scenario:
I am finally getting around to creating my own blog, and I am trying to learn as much as possible with regards to MVC while doing so. I am trying to display my tags as a custom declarative helper in my "PostView.cshtml" file but my problem is that it isn't in the current context and I don't know how to make it so.
I have had a look at the following 2 questions on SO:
this one is for previous version of MVC (<= 4) and
this one was answered by the guy who asked the question and isn't very explanatory.
I tried the above advice but with no success, hopefully someone can help me out. Here is my code:
Tags.cshtml (in ~/Views/Helpers/):
#helper Tags(System.Web.Mvc.HtmlHelper htmlHelper,
ICollection<MyNamespace.Objects.Tag> tags)
{
foreach (var tag in tags)
{
<div class="tags-div">
#MyNamespace.Extensions.ActionLinkExtensions.TagLink(htmlHelper, tag):
</div>
}
}
ActionLinkExtensions.cs (in ~/Extensions/ActionLinkExtensions/)
namespace MyNamespace.Extensions
{
public static class ActionLinkExtensions
{
public static MvcHtmlString TagLink(this HtmlHelper helper, Tag tag)
{
return helper.ActionLink("", ""); //logic removed for simplicity
}
}
}
PostView.cshtml (in ~/Views/Shared/) //where i want to use my custom helper:
#model MyNamespace.Objects.Post
<!--extra html removed for simplicity-->
<div>
<span>Tags:</span>#Tags(Html, Model.Tags) // '#Tags' doesn't exist in current context
</div>
I also tried adding namespaces to '~/Views/web.config':
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="MyNamespace" />
<add namespace="MyNamespace.Extensions" />
</namespaces>
</pages>
My "full name" for my "Tag.cs" class is MyNamespace.Objects.Tag and "Post".cs" is MyNamespace.Objects.Post.
Any explanations and advice with an answer would be greatly appreciated too, thank you very much in advance.

I decided to try use MVC3 way, I added the App_Code folder manually and followed steps from this great article.
And it worked, I needed to restart Visual Studio for my Intellisense to work (which prolonged finding my solution).
I deleted the folder '~/Views/Shared/'
I Added a file MyHelpers.cshtml into the App_Code folder, inside the file I added my helper method:
#helper Tags(System.Web.Mvc.HtmlHelper htmlHelper,
ICollection<MyNamespace.Objects.Tag> tags)
{
foreach (var tag in tags)
{
<div class="tags-div">
#MyNamespace.Extensions.ActionLinkExtensions.TagLink(htmlHelper, tag)
</div>
}
}
And called it in my PostView.cshtml like so:
#MyHelpers.Tags(Html, Model.Tags)
And Viola works as expected... hopefully this helps someone else who ends up in this situation...

I believe the better and simpler way would be to define a display template for you Tags collection that would be placed in ~Views/Shared/DisplayTemplates:
#model ICollection<MyNamespace.Objects.Tag>
foreach (var tag in Model)
{
<div class="tags-div">
#MyNamespace.Extensions.ActionLinkExtensions.TagLink(htmlHelper, tag)
</div>
}
In your PostView.cshtml you would then just write:
#Html.DisplayFor(model => model.Tags)

Related

Kentico 9 cms:cmsTextBox placeholder localization

I've duplicated the search box webpart so i can make changes. I'm trying to add a localization string to the placeholder attribute.
This isn't working:
<cms:CMSTextBox ID="txtWord" runat="server" EnableViewState="false" MaxLength="1000"
ProcessMacroSecurity="false" placeholder="<%= CMS.Helpers.ResHelper.GetString("kff.Search--PlaceHolderCopy")%>" />
nor does this:
<cms:CMSTextBox ID="txtWord" runat="server" EnableViewState="false" MaxLength="1000"
ProcessMacroSecurity="false" placeholder='<%= CMS.Helpers.ResHelper.GetString("kff.Search--PlaceHolderCopy")%>' />
I have a JS Snippet that does work, but i'm hoping to avoid copy in JS files.
var $searchField = $('.searchTextbox');
if ($('body').hasClass('ENCA')) {
// search field placeholder copy
$searchField.attr('placeholder', 'Search For Stuff');
}
else {
$searchField.attr('placeholder', 'Recherche');
}
Can I add the localization string to the server tag, or should it be done in the code behind. I'm not sure the best location in the code behind for this either, I can't see a Page_Load block.
You could add the following line in the SetupControl method in the codebehind:
txtWord.Attributes.Add("placeholder", ResHelper.GetString("kff.Search--PlaceHolderCopy"));
You cannot really use the <%= syntax to set properties of server-side controls.
Also, please note that the CMSTextBox control has a WatermarkText property, which might be what you are looking for. It uses the TextBoxWatermarkExtender control from the AjaxControlToolkit library.
There is no need to duplicate the webpart and have duplicate code just for something this simple. Just create a different webpart layout for that webpart and add the following code above the Panel:
<script runat="server">
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
txtWord.Attributes.Add("placeholder", ResHelper.GetString("yourstring"));
}
</script>

Html.Bootstrap() not resolving with FluentBootstrap

I have installed FluentBootstrap v3.3.5 and Bootstrap v3.3.5 in my project, but this in this View, Html.Bootstrap() gives me a compilation error:
#using FluentBootstrap;
<h2>Test</h2>
<div>
<hr />
#using (var form = Html.Bootstrap().Form().Begin())
{
using (var inputGroup = form.InputGroup().Begin())
{
#inputGroup.InputGroupAddon("#")
#inputGroup.Input().SetPlaceholder("Username")
}
<br />
using (var inputGroup = form.InputGroup().Begin())
{
#inputGroup.Input()
#inputGroup.InputGroupAddon(".00")
}
<br />
using (var inputGroup = form.InputGroup().Begin())
{
#inputGroup.InputGroupAddon("$")
#inputGroup.Input()
#inputGroup.InputGroupAddon(".00")
}
}
</div>
I have also included FluentBootrap in the namespace section of the ~/Views/web.config.
What am I missing?
A bit of a necro-post I know but for those who also have this question:
You'll need to install FluentBootstrap.Mvc as well which has the Html helpers for Asp.Net MVC
If you include 3rd party libraries in your project that contain HTML helpers, you typically have to compile first before Visual Studio will recognize them. Until you compile, intellisense will show this error.
Unload/reload your project. VS will ask you to install 3rd party librairy after reload.

enctype="multipart/form-data" does not submit data with Seam multipart-filter

Ever since I add the following config to the components.xml to customize an editor plugin, every form with enctype="multipart/form-data" in my app does not submit data.
I can't find any source saying that this is conflicting.
<web:multipart-filter create-temp-files="true"
max-request-size="1000000"
url-pattern="*" />
I'm working with Seam 2.2.2 and Jsf 1.2
Update: I thought I could stop using forms with enctype="multipart/form-data". But I can't. I need some help and there goes more info.
First: the problem above only aplies to a4j forms and a4j commandButtons.
As I said before, I add the above web:multipart-filter config at components.xml to make this editor plugin works (which is done via apache commons ServletFileUpload).
I was taking the enctype config off the project forms to make everything work but there is one scenario that it was not possible. When I have to upload an image. But when I use url-pattern="*.seam":
<web:multipart-filter create-temp-files="true"
max-request-size="1000000"
url-pattern="*.seam" />
Then the upload works, but the ServletFileUpload doesn't.
If I don't use any web:multipart-filter this also occurs. (image upload ok, and plugin fails)
Now it is like this:
<h:form id="editPhoto" enctype="multipart/form-data">
<div id="photoAttribute" class="attribute-relationship spacer-top-bottom">
<div class="label">
<h:outputText>#{messages['person.photo.alter']} </h:outputText>
</div>
<s:fileUpload id="photoPerson"
data="#{person.profilePhoto}"
fileName="#{person.profilePhotoName}"
fileSize="#{person.profilePhotoSize}"
accept="images/*">
</s:fileUpload>
</div>
<h:commandButton id="editPersonButtonTop"
value="#{messages['button.savePhoto']}"
action="#{personController.prepareSavePhoto(person)}"
styleClass="button btn-info"
onclick="showAjaxStatus()"/>
</h:form>
It seems I am missing some ajax config here but I can't tell what it is. And also Why can't I have both the ServletFileUpload and the image upload together?
The ServletFileUpload is from commons-fileupload-1.3.1, and it works like this:
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
processItems(items);
saveOpenAttachment();
...
private void processItems(List<FileItem> items)
{
// Process the uploaded items
Iterator<FileItem> iter = items.iterator();
while (iter.hasNext()) {
FileItem item = iter.next();
if (!item.isFormField())
{
processUploadedFile(item);
}
}
}
private void processUploadedFile(FileItem item)
{
setFileName(FilenameUtils.getName(item.getName()));
try
{
InputStream fileContent = item.getInputStream();
byte[] bytes = IOUtils.toByteArray(fileContent);
setFileData(bytes);
}
catch (IOException e)
{
e.printStackTrace();
}
setFileContentType(item.getContentType());
}
I appreciate if somebody can tell how I manage to make ServletFileUpload works with the previous components config or How to have the form to submit the data.
My components.xml:
<core:init debug="true" jndi-pattern="#jndiPattern#"/>
<web:rewrite-filter view-mapping="*.seam"/>
<web:multipart-filter create-temp-files="true"
max-request-size="1024000" url-pattern="*.seam"/>
<core:manager concurrent-request-timeout="10000"
conversation-timeout="3720000"
conversation-id-parameter="cid"
parent-conversation-id-parameter="pid"
default-flush-mode="MANUAL"/>
<persistence:managed-persistence-context name="entityManager"
auto-create="true"
persistence-unit-jndi-name="java:/SinapseEntityManagerFactory"/>
<security:identity authenticate-method="#{authenticatorContext.authenticate}" remember-me="true" />
<international:time-zone-selector time-zone-id="GMT-3"/>
<international:locale-selector name="defaultLocale" locale-string="pt" scope="application" />
<international:locale-selector locale-string="pt" />
<event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}"/>
</event>
<event type="org.jboss.seam.security.postAuthenticate">
<action execute="#{redirect.returnToCapturedView}"/>
</event>
<async:quartz-dispatcher/>
<cache:jboss-cache-provider configuration="ehcache.xml" />
<transaction:ejb-transaction/>
just a lucky guess, by adding
<web:multipart-filter create-temp-files="true"
max-request-size="1000000"
url-pattern="*.seam" />
Seam registers a Multipart Filter and wrap all the request as Seam MultipartRequest and I think commons-fileupload can not process it.
for more information you can visit org.jboss.seam.web.MultipartFilter.java line 78
To solve my problem, I changed the apache-commons FileUpload solution to handle the request like Seam multipart filter does:
ServletRequest request = (ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
try
{
if (!(request instanceof MultipartRequest))
{
request = unwrapMultipartRequest(request);
}
if (request instanceof MultipartRequest)
{
MultipartRequest multipartRequest = (MultipartRequest) request;
String clientId = "upload";
setFileData(multipartRequest.getFileBytes(clientId));
setFileContentType(multipartRequest.getFileContentType(clientId));
setFileName(multipartRequest.getFileName(clientId));
saveOpenAttachment();
}
}
This way, I could take off the web:multipart-filter config that was breaking the other requests.

WCF MaxItemsInObjectGraph setting not working

I have been getting the following error trying to access my WCF service.
'Maximum number of items that can be serialized or deserialized in an object graph is '65536'. Change the object graph or increase the MaxItemsInObjectGraph quota
Doing some research, it looks like all I need to do is update this setting to be a higher value. This is what I am trying to do, but the setting does not seem to be getting read from the configuration. I keep getting the same exception with the 65536 value in it.
I followed the instructions found at this Link, but am having no luck.
Here is what I have configured on the WCF Service's Web.Config.
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
<serviceDebug includeExceptionDetailInFaults="false" />
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
</serviceBehaviors>
</behaviors>
This is what is in the Client's app.config:
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior >
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
</endpointBehaviors>
</behaviors>
And lastly, I have the following attribute on the WCF service itself:
[ServiceBehavior(MaxItemsInObjectGraph = 2147483646, IncludeExceptionDetailInFaults = true)]
Despite the configurations above, I still get an Exception complaining about the 65536 value. Why aren't any of these settings being used by the applications? Is there something else that needs to be set somewhere?
You were on the right track!
All you had to do was add a name to the behavior
<behavior name="MyBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
And then on the end point add
<endpoint .... behaviorConfiguration="MyBehavior"/>
Had to go nuclear and update that machine.config;
Directions Here
The gist of it is to add the following to the "system.serviceModel" section.
<commonBehaviors>
<endpointBehaviors>
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</endpointBehaviors>
<serviceBehaviors>
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</serviceBehaviors>
</commonBehaviors>
I wrote a program to modify the machine configs for this, because support. It works for me, but I haven't done tons of testing.
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace FixMachineConfigBehavior
{
class Program
{
public static XElement IfNotExistsAdd(XDocument xd, XElement rootElement, string childName, XElement newChild)
{
if (rootElement.Elements(childName).Count() == 0)
{
Console.WriteLine(" adding " + childName + " node...");
rootElement.Add(newChild);
}
return rootElement.Element(childName);
}
static void Main(string[] args)
{
foreach (var file in Directory.EnumerateFiles(Environment.GetEnvironmentVariable("windir") + #"\Microsoft.NET\","machine.config",SearchOption.AllDirectories))
{
Console.WriteLine("fixing: " + file);
TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
double ms = t.TotalMilliseconds;
File.Copy(file, file + "." + ms + ".bak", true);
var xd = XDocument.Load(file);
XElement i = xd.Root;
i = IfNotExistsAdd(xd, i, "system.serviceModel", new XElement("system.serviceModel"));
i = IfNotExistsAdd(xd, i, "commonBehaviors", new XElement("commonBehaviors"));
i = IfNotExistsAdd(xd, i, "endpointBehaviors", new XElement("endpointBehaviors"));
i = IfNotExistsAdd(xd, i, "dataContractSerializer", new XElement("dataContractSerializer", new XAttribute("maxItemsInObjectGraph", Int32.MaxValue)));
xd.Save(file);
}
Console.ReadLine();
}
}
}
I had the same problem and tried several options but I found the solution here:
https://msdn.microsoft.com/en-us/library/ms732038.aspx
In "Controlling the serialization process".
Adding ...
[ServiceBehavior(MaxItemsInObjectGraph=100000)]
class My Service ...
good luck
I had the same issue , There was some enums in returning class. What found out they cannot be null. Check whether you have any Enums that are to be returned.

How to 'force' Magento to load a different layout

Background: I need to be able to load upsell / crosssell products in a lightbox complete with add-to-cart functionality.
My idea for achieving this was to 'force' Magento to load products in a different layout. I thought of using an observer on the controller_action_layout_generate_xml_before event (code below).
Unfortunately what I have is not working. Any pointers (or completely different / better ideas) are much appreciated.
<?php
class My_ForceLayout_Model_Observer
{
public function changeLayoutEvent($observer)
{
$action = $observer->getEvent()->getAction();
$layout = $observer->getEvent()->getLayout();
if($action->getRequest()->getControllerName() == 'product'
&& $action->getRequest()->getActionName() == 'view')
{
$update = $layout->getUpdate();
$update->load('popup'); // for testing only
$layout->generateXml();
}
}
}
I managed to get this to work exactly as I first intended. Thanks to #Jonathan Day for making me realize the reason it was not working was trivial.
Config.xml:
<config>
....
<frontend>
<events>
<controller_action_layout_generate_blocks_before>
<observers>
<forcelayout>
<type>singleton</type>
<class>forcelayout/observer</class>
<method>changeLayoutEvent</method>
</forcelayout>
</observers>
</controller_action_layout_generate_blocks_before>
</events>
</frontend>
....
</config>
Observer.php:
class Unleaded_ForceLayout_Model_Observer
{
public function changeLayoutEvent($observer)
{
$action = $observer->getEvent()->getAction();
$layout = $observer->getEvent()->getLayout();
if($action->getRequest()->getControllerName() == 'product'
&& $action->getRequest()->getActionName() == 'view')
{
$template = $action->getRequest()->template;
if (isset($template) && $template != '')
{
$update = $layout->getUpdate();
$update->load($template);
$layout->generateXml();
}
}
}
}
Local.xml:
<popup translate="label">
<label>Catalog Product View Lightbox</label>
<remove name="right"/>
<remove name="left"/>
<reference name="root">
<action method="setTemplate">
<template>page/popup.phtml</template>
</action>
</reference>
<reference name="content">
<remove name="product.info.upsell"/>
</reference>
</popup>
Product url in .phtml file:
echo $this->getProductUrl($_item) . '?template=popup';
Why don't you want to use just regular layout udates?
<catalog_product_view translate="label">
<label>Catalog Product View (Any)</label>
<!-- Mage_Catalog -->
<remove name="right"/>
<remove name="left"/>
<reference name="content">
<block type="new_catalog/product_view"
name="new.product.info"
template="new/catalog/product/view_popup.phtml">
...
</block>
</reference>
</catalog_product_view>
If you want to change the design of your product page depends on some conditions, you could use layout handler functionality. It means that you have to check your parameters in controller and add handler for layout updates, then you could use it in layout file as any other handler. For example:
if ($this->check_parameters()) {
$update->addHandle('new_magic_handler');
$this->loadLayoutUpdates();
}
And in layout:
<new_magic_handler translate="label">
<label>New Magic</label>
...
</new_magic_handler>
Check for details Mage_Catalog_ProductController::_initProductLayout()

Resources