Brand new to React today so apologies in advance.
I have googled but for whatever reason can't seem to find the answer which I know must be out there!
I'm trying to build a TEST component just to learn.
The component is basically going to consist of a header and a number of name value pairs set out in div blocks. So I'm starting with the header and trying to make the component generic by passing in a data attribute.
I have a cshtml page with this node (solution is a .NET Core MVC project in VS2019):
<div id="detailsHeaderText" data-headerText="Details"></div>
I have set up a jsx file which looks like this:
class Header extends React.Component {
render() {
return (
<div className="col-md-12 col-sm-12"><h5>{document.getElementById("detailsHeaderText").getAttribute("data-headerText")}</h5></div>
);
}
}
ReactDOM.render(<Header />, document.getElementById('detailsHeaderText'));
This works perfectly and returns a header with the word "Details" in it.
I now want to make it generic so I can do this elsewhere on the page:
<div class="detailsHeaderText2" data-id="2" data-headerText="Header2"></div>
<div class="detailsHeaderText3" data-id="3" data-headerText="Header3"></div>
<div class="detailsHeaderText4" data-id="4" data-headerText="Header4"></div>
etc
How can I output the header text based on a data-attribute input?
The idea being that I connect the React render output to the element along the lines of this pseudocode: document.getElementById("detailsHeaderText" + data-id)
I've looked at constructors and super(props) but nothing seems to work as most of the examples are to do with handlers and hence access the event target prop.
I've found many links to passing props between components.
But none for passing in data from the parent element on a cshtml page.
An answer or a pointer to a detailed answer on passing variables into React would be most helpful.
Thanks in advance.
So I'm 12 hours further down the line in terms of learning React and Googling.
And solved the problem.
Working code is:
function Header(props) {
return <div className="col-md-12 col-sm-12"><h5>{props.headertext}</h5></div>;
}
let elems = document.getElementsByClassName("headerText");
function renderToElements(toRender, elements, dataset) {
for (var i = 0; i < elements.length; i++) {
let passText = elements[i].dataset[dataset];
let renderEl = React.createElement(toRender, { headertext: passText })
ReactDOM.render(renderEl, elements[i]);
}
}
renderToElements(Header, elems, 'headertext')
Which renders all dom nodes of the following construct:
<div class="headerText" data-headertext="Details"></div>
It may seem like a pointless exercise to some in terms of what it is achieving but hopefully this may help others in grasping some basics as I/they can now build on this to construct more complex components.
Related
I need a piece of advice.
We have an existing system based on .Net core/MVC. And we're moving all frontend parts to Blazor. For now we have one part where we're not sure how to better solve the task.
We have a module, which controls rooms. It's a web page which loads an SVG scheme of a floor from the database. Each room in SVG is an < G > element with ID. Like < G room-id="15" >...
In the existing app, we have a javascript code, which runs after the page is loaded and this script appends to each element an "onclick" event with "room-id" parameter.
Also this script changes fill color of the element if there is no "room-id" to show inactive rooms.
Now, we have to move these functions to Blazor. But Blazor can't manipulate DOM directly. We can keep Javascript, of course. But it would be the worst solution.
We're ready to update backend as well. For example, one of our ideas is to use HtmlAgility package, parse SVG (which is XML by fact), and insert CSS class to inactive rooms. By this way we can solve our second problem (Maybe).
With OnClick events the only idea is again to parse SVG inside the Blazor component, and then rebuild the picture with code and add needed events.
But maybe somebody could find a better way to do these tasks.
Thanks a lot!
Dmitry
Finally, how I solved the task.
SVG elemens have options like "onMouseOver", "onClick" and so on.
So, couldn't completely avoid Javascript. But it's now minimal and easy to maintain.
xScheme = XDocument.Parse(roomScheme);
elements = xScheme.Descendants("{http://www.w3.org/2000/svg}g");
foreach (var el in elements)
{
var roomId= el.Attribute("data-room-id")?.Value;
if (roomId!= null)
{
Guid.TryParse(roomId, out var roomGuid);
var room = allRooms.FirstOrDefault(s => s.Id == roomGuid);
if (room != null)
{
el.SetAttributeValue("class", "thover");
el.SetAttributeValue("title", $" ...Hint details ...");
el.SetAttributeValue("data-placement", "top");
el.SetAttributeValue("data-html", "true");
var attr = new XAttribute("onClick", $"window.location.href = '/RoomDetails/{roomGuid}'");
el.Add(attr);
attr = new XAttribute("onMouseover", " $(function() {$('.thover').tooltip();});");
el.Add(attr);
}
else
{
el.SetAttributeValue("class", "inactive");
}
}
}
And then inside Razor part:
#((MarkupString)xScheme.ToString())
Ive just started using NonFactors.Grid.Mvc6 -Version 6.2.4. Ive got its basic functionality working and Im able to retrieve data from my serverside code (.net core 5). I want to implement paging but Im only able to get it to page through the dataset ive received on the clientside. For example, Ive returned 5 rows from the database, the grid only allows me to page through those 5 items. I cant find any documentation (or examples) of using ajax calls to retrieve the data in pages (specifying the current page and number of rows). This is of no use in the real world so the grid must be capable of this somehow (hopefully), but there is nothing documented. Has anyone managed to do this ? Id really appreciate some examples, the documentation is pretty poor
I have the same problem, if I want to use the paging from the backend
Code example
.Pageable(pager =>
{
pager.ShowPageSizes = true;
pager.PageSizes = Model.Paging.PageSizes;
pager.PagesToDisplay = Model.Paging.PagesToDisplay;
pager.CurrentPage = Model.Paging.CurrentPage;
pager.RowsPerPage = Model.Paging.RowsPerPage;
pager.TotalRows = Model.Paging.TotalRows;
})
You can set the TotalRows value, but it will be recalculated for the _Pager.cshtml view based on the Rows.
public virtual IQueryable<T> Process(IQueryable<T> items)
{
TotalRows = items.Count();
My workaround, add/modify the following line to the _Grid.cshtml file:
#if (Model.Pager != null)
{
Model.Pager.TotalRows = Model.Pager.PagesToDisplay * Model.Pager.RowsPerPage;
#await Html.PartialAsync(Model.Pager.PartialViewName, Model.Pager)
}
[EDITED] Code and solution edited as per suggestions from forum members. I've corrected the name of the controller. It was incorrect. The corrected name is shown in the updated solution file snapshot. I'm getting a new error. The new error is shown in the updated snapshot. It's still a 404-typpe error.
As for the "post your routing edits" type of comments, please note the original statement that Visual Studio has done all the wireup for me. It's my understanding that if I'm right-clicking and adding controllers, views, etc. from the IDE that I do not need to go in and do additional wireup behind the scenes. This seems to defeat the whole purpose of an easy-to-implement interface. Please correct me if I misunderstand. Thanks.[END EDIT]
I asked for help with this problem earlier and nobody was able to solve. I've retooled the solution, deleting everything involved and starting over, in hopes it would relieve the problem. I'm still stuck. This is a very simple thing according to all I've read on MVC but I have been dead in the water for over a week. Would appreciate help on this. My retooled code appears below. I've looked at similar errors reported in these forums and found no help.
Please note that all the wireup was done completely by Visual Studio: I have not modified any generated code behind the scenes other than adding an Action method to the controller. In other words, everything was generated by right-clicking in VS and selecting "Add" and following the prompts for views, controllers, etc.
I have a view in one area, "UserAccount", that uses Html.ActionLink() to generate a link to a view in another area, "DocumentUploaderNew". A screen snapshot of the markup is below. When I click the rendered button in IE though, I get an error that seems like a "404" error. The view IS there though.
This has GOT to be an embarrassingly easy answer. I'm ready to eat humble pie. What is it???
That's because your controller is actually named FileUpload, yet you're trying to use the controller name FileUploaderNew (looks like you're getting confused with your action and controller names).
Try this:
#Html.ActionLink("Upload New", "FileUploaderNew", new { controller = "FileUpload", area = "DocumentUploaderNew" }, new { #class = "btn btn-info" })
The reason this is failing is because your view FileUploaderNew.cshtml is at the root of the Views folder and not in a folder itself (either one with the name of the controller or the shared view folder). If you look at the paths that MVC is checking in your error screenshot, you can see it never checks the path ~/Areas/DocumentUploaderNew/Views/FileUploaderNew.cshtml
There are numerous fixes you can do to fix this problem (choose one, not all three).
Create a folder in your Views folder called FileUploaderNew and move your FileUploaderNew.cshtml file into that folder (basically, you create a subfolder in the views folder with the name of a matching controller. The view engine will look for that folder when returning Views from that controller).
Move the FileUploaderNew.cshtml file into the Shared folder inside the Views folder (you can see it in your screenshot).
Specify exactly where your view is when you call Return View():
return View("~/Areas/DocumentUploaderNew/Views/FileUploaderNew.cshtml");
Depending on what your route is, it'll either be
#Html.ActionLink("Upload New", "FileUploaderNew", new { controller = "FileUpload/FileUploaderNew"}, new { #class = "btn btn-info" })
or
#Html.ActionLink("Upload New", "FileUploaderNew", new { controller = "DocumentUploader/Controllers/FileUpload/FileUploaderNew"}, new { #class = "btn btn-info" })
The jist is you have to post to the controller's path (not name) and when you don't it can't find it & you get a 404. In a default MVC file structure, I believe this is automated (controllers in the controllers directory auto map without paths), but that's not your file structure.
Also, I'm answering this never having used areas, not sure if those factor into the equation, but a quick read seems to indicate they're more for locking down paths than finding them.
I am getting error in a Umbraco 7.0.3 macro which traverse through all the childs and returns little more than a list. To start with I have used Umbraco back office to build the basic navigation and side menu items. Meanwhile once the local environment is setup I started working locally.
The issue is, the code I had build using back office for navigation works fine but same code does not work if the items are created in VS2012 Ultimate version, even i just paste the same code from original working navigation macro.
I am getting following error : 'Umbraco.Web.Models.DynamicPublishedContentList' does not contain a definition for 'Any', suggesting that the page list is dynamic. To my amusement, the same code work for existing navigation, then why not with new items? Is there any setting in VS2012 where it is marking the file unusable with UTF8 editors or non-valid html?
My question is how can I find count or Any items in a razor macro? I have already tried Enumerable items count and any methods, but no use.
Any pointers on how to find number of items exists will be helpful.
I am providing some more information on Paulo's request. Erroring on "startNode.Children.Where("Visible").Any()" line. Following is the macro code:
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#*
=== Macro Parameters To Create ===
Show:True Alias:nodeId Name:Node ID Type:Content Picker
*#
#if (Model.MacroParameters["startNodeID"] != null)
{
#* Get the start node as a dynamic node *#
var startNode = Umbraco.Content(Model.MacroParameters["startNodeID"]);
if (startNode.Children.Where("Visible").Any())
{
<div class="container">
<div class="row">
#foreach (var page in startNode.Children.Where("Visible"))
{
<div class="col-sm-3 col-md-3">
<div class="thumbnail">
<img src="~/images/Tiles/300x200.jpg" alt="#page.Name">
<div class="caption">
<h3>#page.Name</h3>
<p>#page.GetPropertyValue("summary").Substring(0, 100)</p>
Read More
</div>
</div>
</div>
}
</div>
</div>
}
}
I have just came across the same issue, and the following worked for me: simply cast your umbraco property to a DynamicPublishedContentList variable (assuming the property is indeed of such type).
Example:
var homePage = CurrentPage.AncestorsOrSelf().Where("DocumentTypeAlias == #0", "yourPageAlias").FirstOrDefault();
var umbracoFolder = homePage.yourUmbracoFolderName;
var umbracoFolderItems = Umbraco.Content(umbracoFolder.ToString());
Umbraco.Web.Models.DynamicPublishedContentList yourList = umbracoFolderItems.yourItems as Umbraco.Web.Models.DynamicPublishedContentList;
yourList.Count() will work.
Update: The above works, but you can make your life easier by working against a List instead of DynamicPublishedContentList, so you have not only the regular IEnumerable extension methods, but also indexers.
List<dynamic> items = new List<dynamic>(umbracoFolderItems.yourItems);
I'm guessing you're missing a using System.Linq directive.
Hey on a windows 8 app I want to automatic click on a hyperlink after web page is loaded in the webviewer.. The code for the hyperlink looks like this:
<li class="first"><a class="user-signup ctools-bp-modal" href="http://webpage.com/register"> … </a></li>
the link is to a json file that will open on the page.
now I tried doing it by using both document.getElementByClassName and document.getElementByClass like:
signupWebView.InvokeScript("eval", new string[] { string.Format("document.getElementByClassName('user-signup ctools-bp-modal').click();") });
I have also tried with .submit
Is there any way to do this?
Thanks
It's getElementsByClassName (plural) and since multiple elements could have the same class, it will return an array of objects. The following code will work, but presumes you know which specific item you want among a potential list of elements with the same given class(es). Perhaps using an id attribute would be safer?
signupWebView.InvokeScript("eval", new string[] { "document.getElementsByClassName('user-signup ctools-bp-modal')[0].click();" });
Note too, the String.Format call is unnecessary.