I am surfing a page to get anchors containing a particular text. What I want to do is if no anchor satifies the criteria in that case the method should return a new HtmlAnchor pointing to the page I passed as a parameter.
public HtmlAnchor getAnchor(HtmlPage page, String criteria)
{
if any anchor satisfies this criteria return that anchor
else create a new anchor pointing to the current page and return it //how to do this
}
FYI : I am using htmlunit 2.9
It doesn't appear you can instantiate an HtmlAnchor directly, but you can create an instance of its parent, HtmlElement. Set its "href" attribute to the url of that page, and you should have what you want.
What do you intend to do with it, though?
Related
It seems Aurelia is not aware when I create and append an element in javascript and set a custom attribute (unless I am doing something wrong). For example,
const e = document.createElement('div');
e.setAttribute('custom-attr', 'some value');
body.appendChild(e);
Is there a way to make Aurelia aware of this custom attribute when it gets appended?
A little background: I am creating an app where the user can select their element type (e.g. input, select, checkbox etc.) and drag it around (the dragging is done in the custom attribute). I thought about creating a wrapper <div custom-attr repeat.for="e of elements"></div> and somehow render the elements array, but this seemed inefficient since the repeater will go through all the elements everytime I push a new one and I didn't not want to create a wrapper around something as simple as a text input that might be created.
You would have to manually trigger the Aurelia's enhance method for it to register the custom attributes or anything Aurelia related really. And you also have to pass in a ViewResources object containing the custom attribute.
Since this isn't as straight forward as you might think, I'll explain it a bit.
The enhance method requires the following parameters for this scenario:
Your HTML as plain text (string)
The binding context (in our scenario, it's just this)
A ViewResources object that has the required custom attribute
One way to get access to the ViewResources object that meets our requirements, is to require the custom attribute into your parent view and then use the parent view's ViewResources. To do that, require the view inside the parent view's HTML and then implement the created(owningView, thisView) callback in the controller. When it's fired, thisView will have a resources property, which is a ViewResources object that contains the require-d custom attribute.
Since I am HORRIBLE at explaining, please look into the example provided below.
Here is an example how to:
app.js
import { TemplatingEngine } from 'aurelia-framework';
export class App {
static inject = [TemplatingEngine];
message = 'Hello World!';
constructor(templatingEngine, viewResources) {
this._templatingEngine = templatingEngine;
}
created(owningView, thisView) {
this._viewResources = thisView.resources;
}
bind() {
this.createEnhanceAppend();
}
createEnhanceAppend() {
const span = document.createElement('span');
span.innerHTML = "<h5 example.bind=\"message\"></h5>";
this._templatingEngine.enhance({ element: span, bindingContext: this, resources: this._viewResources });
this.view.appendChild(span);
}
}
app.html
<template>
<require from="./example-custom-attribute"></require>
<div ref="view"></div>
</template>
Gist.run:
https://gist.run/?id=7b80d2498ed17bcb88f17b17c6f73fb9
Additional resources
Dwayne Charrington has written an excellent tutorial on this topic:
https://ilikekillnerds.com/2016/01/enhancing-at-will-using-aurelias-templating-engine-enhance-api/
Here's the scenario: I invoke an action method which returns PartialViewresult via ajax. The result is loaded into another view. Then I Navigate to another page and after this if I press browser back button the previously loaded PartialViewResult appears naked. By naked I mean no container view and layout is loaded, just plain html elements are rendered. Like this:
Has anyone faced and tackled this problem?
EDIT: To provide a better understanding of the situation here's my action method:
public ActionResult Inbox(int pageNumber = 1, int pageSize = 10, string filter = null)
{
var inboxItems = GetInboxItems(filter);
PagedList<InboxItem> pagedList = inboxItems.ToPagedList(pageNumber, pageSize);
pagedList.DisplayPage(pageNumber);
InboxViewModel vm = new InboxViewModel(pagedList);
if (!Request.IsAjaxRequest())
return View(vm);
return PartialView(vm);
}
In the Inbox.cshtml file I load layout based on if the request is ajax request:
#model Dokcentra.Models.InboxViewModel
#if (!Request.IsAjaxRequest())
{
Layout = "~/Views/Shared/_Cabinet.cshtml";
}
I think the last piece of code is what causes this problem because the layout file _Cabinet.cshtml has all the html, css and js and when I press browser back button that layout file doesn't load.
After lots of digging and hair pulling I realized that this only happens in Chrome. I found out that I need to provide a different url from the full html document. So when sending a reuest through AJAX to this action method I just append any arbitrary query string value to "?Cabinet/Inbox", like this:
var url= "/Cabinet/Inbox?anythingYouWant"
How does one Route from one Area to another?
In the default project, there are no area's, but once you add an Area, how could I redirect from the default Home page to a page inside my new Area?
I don't see a RedirectTo* method which takes a parameter for an Area name anywhere.
Unless I'm missing the point of Area's completely?
Inside our views, we don’t need to specify the area route data value when generating
links to other controller actions inside that area. We only supply the action name, because the controller and area name will come from the existing route data for the current request. If we want to link to an outside area, we’ll need to supply that route data explicitly.
return RedirectToAction("yourAction", "YourController", new { area = "yourArea" });
The "area" route value needs to match the AreaName used in the AreaRegistration class for the URL to generate correctly.
This code in your view will link you to the Admin area, regardless of which area you are currently in:
#Html.ActionLink("Click Me", "ActionName","ControllerName",new { Area = "AreaName"}, null )
e.g. (poor made up example)
#Html.ActionLink("Administer User", "Home","UserAdmin",new { Area = "Admin"}, null )
The final null in the call corresponds to HtmlAttributes, and I usually leave it as null.
I have a Partial View that renders WebGrid. My controller looks like
public ActionResult Index()
{
return View();
}
public ActionResult GetUserList(int? page, string sort, string sortdir)
{
var model = UserModel.getList(page,sort,sortdir);
return PartialView("_UserList",model);
}
Index.cshtml :
....
#Html.Action("GetUserList")
The problem is that every time I click on grid navigation or sort links it calls Index method. How can I make Webgrid to execute a different action (GetUserList in this case)? I'm sure I can prepend GetUserList to all links in grid using jquery, but I believe it should be a better way.
It's also possible that what I'm doing is completely wrong, so thanks for your suggestions.
After lot of monkeying around and digging (and even fiddling with Reflector with WebGrid's source code), I came to the conclusion that with WebGrid, you cannot control/change the Header link action.
To create the header link URL, the path is taken from HttpContext.Request.Path, so there is no way to customize it to point to a different route.
One very ugly hack would be to tap into to jQuery Ajax's events (since the header link uses jQuery.load to sort) and overwrite the URL:
Album Id
Better solution would be to use:
Telerik Grid which lets you specify custom routes and also offers much more flexibility in rendering your layout
or MvcContrib Grid (not sure if this lets you modify header links but definitely offers more flexibility than WebGrid)
#MrChief had the idea above about the ugly hack...I put that together. Here is the main code that I used to do this. It does, indeed, hijack the ajax call before it is put on the wire. The key is to modify the URL that is getting sent because the grid will grab that URL from HttpContext.Request.Path. and plug it into the onclick for the anchor element.
I put this into my main common.js and will simply attach a function to capture the ajaxSend event which happens just before the data is sent.
// Used to hijack the sending of all AJAX calls. Before it sends the call to the server, it checks to see if the
// active element (the element that prompted the call) is marked with a given class. If so, then it will perform
// the given operation.
$(document).ajaxSend(function (event, jqXHR, ajaxOptions) {
var activeElement = document.activeElement;
if ($(activeElement).attr('redosorturl') != null) {
// If this is a sort anchor link from a grid that needs to have the sort link redone, do it here.
// the code is in the eipGrip.js file.
if ($(activeElement).attr('redosorturl').toString() == 'redoSortURL') {
var newURL = RedoGridSortURL(activeElement, ajaxOptions.url.toString());
ajaxOptions.url = newURL.toString();
}
}
return false;
});
When rendering the page, I have marked the tag in column header that contains the incorrect URL with a class named "redosorturl', so I know when I hijack the ajax call, the operation has to be done on this element. I then call a custom function that gives me the correct URL, then the ajaxOptions.url is then rewritten with that new URL.
I have to pass the activeElement to that rewrite function so I can traverse up the DOM to get the grid information, where I have put data like the controller and action method that is used along with and IDs and other info that I use for the URL. Likewise, I pass in the current url string because the grid will inject a token at the end of the url that I parse off and put on the new url.
Your conclusion isn't right. You just need to wrap your webgrid in a Get form:
using (Html.BeginForm("GetUserList", "ThingaMaBob", System.Web.Mvc.FormMethod.Get))
{
var grid = new WebGrid(
...
));
Html.Hidden(grid.SortFieldName, grid.SortColumn);
Html.Hidden(grid.SortDirectionFieldName, grid.SortDirection == SortDirection.Ascending ? "ASC" : "DESC");
}
The hiddens are so that the sort dir and sort field end up in parseable form in the querystring. You end up with urls like localhost/ThingaMaBob/GetUserList?someotherfields=whatever=&sort=city&sortdir=ASC
If you remove [HttpPost] attribute and let the route come to the same function. you'll find the Request["page"] value in your method. this will allow you to put a check on Request["Page"] value.
I'm having trouble determining where to place navigation for an MVC app. For example, say you have the following structure:
Conferences
South Eastern Conference
Florida Gators
Georgia Bulldogs
Arkansas Razorbacks
Pac-10
USC
Hawaii
Big East etc...
How would you best create a structure for implementing a 'main' navigation and subsequent 'sub' navigation? Using the hypothetical example, You'd have specific sub navigation for each conference, showing its respective colleges (and only that conferences colleges).
Is this something you'd handle in the main view and just hide the non-selected conference?
Or would you create a menu helper (or yet another partial) and call that from each individual college's view?
Best way is to use multiple, nested master pages. e.g. Site.master would contain your top-level nav (list of conferences?) then you'd have a different master page for each conference that would 'extend' site.master. You can, in theory, have as many nested master pages as you want. Finally, Florida Gators etc would be 'real' views (i.e. non-master pages).
The tricky part is telling any parent master page which navigation item is currently selected. Because you can't bind master pages to the ViewModel you'll have to use the View Dictionary e.g. View["SelectedMainNavItem"].
Why not use some global layout template that always displays the main navigation, and relies on some helper to render the subnav? (The helper may be superfluous -- you might just output the subnavigation inline in the layout template)
Your controller passes current category/sub-category, and some data structure describing the current subnavigation options, to the view.
After contemplating this issue for a while along with the suggestions, I came up with this solution. Since my subnavigation will always be below the main navigation, I decided to go with the Convention over Configuration method.
In my Site.Master, I have the following two render partials. One displays the main navigation and the other makes a call to BuildSubNavigation to display get the name of a partial to render:
<% Html.RenderPartial("_MainNavigation"); %>
<% var submenu = ViewContext.BuildSubNavigation();
if (submenu != null) {
Html.RenderPartial(submenu);
}%>
Granted, this could be thrown into a Helper, and I intend to do that, this is more explicit and aids in the understanding of the issue.
What this does is call the BuildSubNavigation method. It goes with the convention that if a controller is to have a specific sub navigation, there will be a partial in the form of "_Navigation" So in the spirit of the example, one partial would be "_SouthEasternConferenceNavigation" What I do is then check to see if the current view actually exists. If it does, I return the name, where it's then used to render the partial.
public static string BuildSubNavigation(this ViewContext vc) {
var controller = vc.RouteData.Values["controller"] ?? "";
var viewName = "_" + controller + "Navigation";
if (ViewExists(vc.Controller.ControllerContext, viewName, null)) {
return viewName;
} else {
return null;
}
}
And this is the method that checks whether the View actually exists against the current View Engine:
public static bool ViewExists(ControllerContext cc, string viewName, string masterName) {
if (ViewEngines.Engines.FindView(cc, viewName, masterName).View != null) {
return true;
} else { return false; }
}
I'm unsure if this is the best way to do this, but it's working rather well for a small project I'm currently working on.
Thanks for the answers!