ng-repeat only displays final element - html

I am using ng-repeat in my html to display the title of each 'workbook' object in an array that is on the scope. For some reason, the third title is being displayed three times. There are three objects in this test list, so it is iterating through the correct number of times. Here is the template html code:
<div id="list-of-workbooks" ng-controller="TableauListCtrl" class="ng-scope" >
<ul>
<li ng-repeat="workbook in workbooks" ng-click="clickWorkbook(workbook)" ng-mouseover="mouseoverWorkbook(workbook)" class="ng-binding"> {{ workbook.getTitle() }} </li>
</ul>
</div>
The output looks like this:
Workbook 3 Title
Workbook 3 Title
Workbook 3 Title
Is my error in the syntax? Thanks!
EDIT: It is also possible that the error occurs when the list is made. Each title is read from an XML Document (that long chain of calls returns the correct title) and is used to set the title of a Workbook (a custom service), which is then added to the list. Are there any error in this code? It is from the application module.
var title;
var workbooks = [];
for (var i = 0; i < workbooksData.length; i++) {
var workbook = Workbook;
title = workbooksData[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
workbook.setTitle(title);
workbooks[i] = workbook; // add a workbook with each title to the array of Workbooks
}
return workbooks;
Could it have something to do with the way angular listens to changes in the model, as is suggested in this question's accepted answer, in the second bullet point? That is pretty beyond my scope of understanding, so if someone could point me in the right direction to learn more I would appreciate it!

Try removing spaces from your div's id attribute "List of Workbooks".
You could have:
<div id="list-of-workbooks" ...>
Also, take a look at:
What are valid values for the id attribute in HTML?

Related

How to extract the href value from links in HTML data based on then element's text?

I have been tasked with the coding of a web crawler that goes through several URLs (around 400, but the list could grow), each with a completely different html structure and extract the links containing certain information. The only thing the program knows beforehand is what are the keywords it should search for, but the html structure and any semantic cues as to where to look for those keywords is unknown.
So far, I have used the request-promise module for Node.js to send a request to the URL where the search for keywords will take place:
const htmlResult = await request.get(url);
htmlResult stores the response as a string, and I can save it both as an .txt or .html if needed.
The problem I have is that I don't know how to instruct the program how to extract a URL based on words that aren't necessarily present in the url string. An example might help clarify:
<a href="site/with/no/keywords-just-a-random-string" title="Keywords might be here, but title attribute might be absent"><span class="img"><img data-cfsrc="/thumbpdf/618a8nb4.jpg" alt="" style="display:none;visibility:hidden;"><noscript><img src="/thumbpdf/8bfa84.jpg" alt=""></noscript></span>
<h2>KEYWORDS ARE IN THIS TAG, WHICH IN TURN IS INSIDE THE <a> TAG</h2>
<span class="date--type">2 Nov 2021 </span>
<span class="tag">
oher stuff with no keywords in it</span>
</a>
As you can see, this tag has a complex structure. The keywords I need to parse are inside an h2 tag which, in turn, is inside the a tag. But he a tag could also be like this:
KEYWORDS TO PARSE
Here the keywords are simply within the a tag.
My question, thus, is how do I parse htmlResult (either as a string or saved as a .txt/.html file), and, once I get a match, instruct the program to extract the url that is in the bounds of the a tag wherein I go the match of keywords?
As I am using Node.js I open to using any tool available.
Could someone offer some advice on how to tackle this challenge?
Thanks so much in advance.
This is very quick and dirty, and I'm sure it can be further streamlined, but it should get you at least closer to where you need to be.
This assumes a bunch of <div> elements, each containing one of your your <a> elements, all in one document (see link below). It uses xpath to locate the data:
function xpathEval(xpath, context) {
return document.evaluate(xpath, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}
desiredHrefs = []
let targets = xpathEval("//div[#class='container']", document);
for (let i = 0; i < targets.snapshotLength; i++) {
let attribs = xpathEval('.//*/#*', targets.snapshotItem(i)),
texts = xpathEval('.//*/text()', targets.snapshotItem(i));
for (let k = 0; k < attribs.snapshotLength; k++) {
attribData = attribs.snapshotItem(k).textContent
if (attribData.includes("trainer") & attribData.includes("dog")) {
//either
//console.log(targets.snapshotItem(i).querySelector('a').getAttribute('href'))
//ot
let href = xpathEval('.//a/#href', targets.snapshotItem(i));
desiredHrefs.push(href.snapshotItem(0).textContent)
}
}
for (let j = 0; j < texts.snapshotLength; j++) {
data = texts.snapshotItem(j).nodeValue.trim().toLowerCase()
if (data.includes("trainer") & data.includes("dog")) {
//either
//console.log(targets.snapshotItem(i).querySelector('a').getAttribute('href'))
//or
let href = xpathEval('.//a/#href', targets.snapshotItem(i));
desiredHrefs.push(href.snapshotItem(0).textContent)
}
}
}
for (let href of [...new Set(desiredHrefs)])
console.log(href)
You can see it in action here.

SwiftSoup - Extracting specific div tags/elements

I'm not the most knowledgeable when dealing with scraping/getting data from a website, so apologies in advance. I have loaded in the HTML file locally, into my project so that I can have a reference and breakdown of the elements:
<div class="price">99</div>
<div class="size">M</div>
I want to select both these div classes, name and price and extract the value(s) which are 99 and M accordingly, how can I do this? I looked at SwiftSoups
let elements = try doc.select("[name=transaction_id]") // query
let transaction_id = try elements.get(0) // select first element
let value = try transaction_id.val() // get value
But that gave me an error. I can see you can select <P> tags, which are paragraphs, but how do I select the specific div class?
Once again, apologies if this is a beginner question.
Thank you.
Edit - The data I wish to parse:
var pstats = {att1:85,att2:92,att3:91,att4:95,att5:38,att6:65,acceleration:91,agility:91,balance:95,jumping:68,reactions:94,sprintspeed:80,stamina:72,strength:69,aggression:44,positioning:93,tactaware:40,vision:95,ballcontrol:96,crossing:85,curve:93,dribbling:96,finishing:95,fkacc:94,headingacc:70,longpass:91,longshot:94,marking:32,penalties:75,shortpass:91,shotpower:86,slidetackle:24,standingtackle:35,volleys:88,composure:96};
Edit 2 - New data I want to parse:
<div style="display: none;" id="player_stats_json">{"test":0,"ppace":85,"pshooting":92,"ppassing":91,"pdribbling":95,"pdefending":38,"pphysical":65,"acceleration":91,"sprintspeed":80,"agility":91,"balance":95,"reactions":94,"ballcontrol":96,"dribbling":96,"positioning":93,"finishing":95,"shotpower":86,"longshotsaccuracy":94,"volleys":88,"penalties":75,"interceptions":40,"headingaccuracy":70,"marking":32,"standingtackle":35,"slidingtackle":24,"vision":95,"crossing":85,"freekickaccuracy":94,"shortpassing":91,"longpassing":91,"curve":93,"jumping":68,"stamina":72,"strength":69,"aggression":44,"composure":96}</div>
If these tags have unique classes you can use getElementsByClass(_:) function and then get the first item, like this:
let price = try doc.getElementsByClass("price").first()?.text()
let size = try doc.getElementsByClass("size").first()?.text()

React: How to provide procedurally generated <li> elements distinct HTML id values?

I'm rendering a map of items retrieved from a database and filtered via the value state of an input field and attempting to then set the state of the input field as the value stored in some list item on click. I figured that using document.getElementById().innerHTML would allow me to retrieve the content stored within the appropriate tag and then set it to state which does work, the issue I'm facing is that it will only retrieve the innerHTML of the first item rendered in the map.
I've tried solutions ranging from applying UUID to making the mapped content available to the window and transfering the state of the individual objects but each disparate solution only moves the value of the first item to state - any ideas?
Rendered Content:
window.filteredItems = this.state.items.filter(
(item) => {
return item.companyNameObj.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
}
);
<div className="fixed-width">
<div className="search-container">
<form>
<input type="text" name="search" className="search-bar" placeholder="Search: " onChange={this.handleChange} value={this.state.search} />
</form>
<ul className="search-results">
{window.filteredItems.map((item) => {
return (
<div className="distinct-result-container">
<li key={item.id}>
<div className="image-container">
<img src={item.imageObj} alt={item.companyNameObj + " logo."}/>
</div>
<div className="company-container">
<span onClick={this.stateTransfer}><h3 id={"ID"}>{item.companyNameObj}</h3></span>
<p>Owned by: {item.ownerNameObj}</p>
</div>
</li>
</div>
)
})}
</ul>
</div>
<Footer />
</div>
);
stateTransfer()
stateTransfer(id) {
var search = this.state.search;
var uniqueID = document.getElementById("ID").innerHTML;
this.setState({
search: uniqueID
});
}
The current content of stateTransfer() doesn't represent any significant attempts at approaching a solution to this issue, it's just the minimum required implementation to move the innerHTML content to the input fields value.
EDIT: I've further clarified on the task at hand and a potential solution in the comments below (which follow this), I'm just hoping someone is able to help me with the actual implementation.
#DILEEPTHOMAS The list is comprised of data pulled from a Firebase Realtime Database and is rendered via mapping the filteredList and a search query; that functoionality works fine - what I need is to be able to click the element of any distinct li and have the innerHTML (the text stored in that li's item.companyNameObj) be moved to the value of the input field (so users can navigate the search content with re-typing).
#JoshuaLink I can't necessarily configure the items of the list any
further as it's just data pulled from an external database - I believe
the appropriate solution is to somehow provide a unique HTML ID value
to each newly rendered li and have that selected ID moved to
stateTransfer() where it can be set as the input fields value, I'm
just struggling with the actual implementation of this.
EDIT 2: I've managed to figure out a solution to both parts of the problem as described above - I'll post it as an answer below.
I managed to solve both parts of my problem:
The key issue, which was moving the text stored in each distinct li to the input value, which was apparently easily solved by making my stateTransfer() function accept an event and passing the .innerText value of the h3 through the event (I assumed I would have to use .innerHTML, which would require me to provide each distinct li with a unique generated ID) as follows:
stateTransfer(e) {
var search = this.state.search;
var innerText = e.target.innerText
this.setState({
search: innerText
})
}
The secondary issue, (which I incorrectly assumed was integral to implementing a solution to my question), assigning unique HTML id values to my procedurally generated li's was solved by implementing a for-loop in a componentDidUpdate() function which iterates through the current total length of the list and and assigns an id with the loop iterator concatenated to the end of the string as follows:
componentDidUpdate() {
var i;
var searchCompanyNames = document.querySelectorAll('.comapnyNames');
for(i = 0; i < searchCompanyNames.length; i++) {
searchCompanyNames[i].id = 'companyName-' + i;
}
}
Whilst I didn't need to assign unique ID's to the li's in the correct implementation, it's a useful trick worth noting nonetheless.

Handling checkboxes and getting values

I'm pretty new to MVC and I'm having a hard understanding how to get the values (basically the IDs) to checkboxes that I'm generating. Here are my checkboxes:
<div id='myCheckboxDiv'>
<input type="checkbox" onclick="checkAll(this)">Check All
#foreach (var form in #Model.DetailObject.DoaFormGroupDocuments)
{
<br>
var checkBoxId = "chk" + form.DocumentId;
#Html.CheckBox(checkBoxId, new { value = form.DocumentId, #checked = true });
#form.DocumentName;
}
</div>
Essentially what I want to do is get the ID to which ever checkbox is checked and save it in to a list after I click a save button at the bottom of the page.
I have run across something like this to handle everything but I'm not quite sure how to use it really...
var values = $('#myCheckboxDiv').find('input:checkbox:checked').map(function () {
// get the name ..
var nameOfSelectedItem = this.attr('name');
// skip the ‘chk’ part and give me the rest
return nameOfSelectedItem.substr(3);
}).get();
The only thing you need to think about is the value of the name attribute your checkbox(es) will have. The way you're handling it right now, your post body is going to have a fairly randomized collection of chkN-named parameters, where N is some number. The modelbinder will need something similarly named as a parameter to your action method in order to bind the posted values to something useful. That's a tall order for something that will be some what variable (the DocumentId values).
The best option would be to set up your checkboxes, instead, as a collection, which means giving them names chk[0], chk[1], etc. Then in your action you can accept a parameter like List<string> chk, and that will contain a list of all the values that were posted.

Displaying Mediapicker field in blogpost.list.cshtml in Orchard CMS

I'm trying to edit the page when my blog displays the Parts.Blogs.BlogPost.List.cshtml. I went to ContentItems under content and added a MediaPicker Field called BlogPostImage to my BlogPosts, and I also made an alternate to Parts.Blogs.BlogPost.List.cshtml in my theme (which is the file I'm editing. The code that I have in there is:
#using Orchard.ContentManagement;
#{
IEnumerable<object> blogPosts =
Model.ContentItems;
}
#if (blogPosts == null || blogPosts.Count() < 1)
{
<p>#T("No posts.")</p>
}
else
{
int count = 0;
<div id="Blog">
<div id="slides">
<div class="slides_container">
#foreach (dynamic post in blogPosts)
{
count++;
string title = post.ContentItem.TitlePart.Title;
ContentItem item = post.ContentItem;
string text = post.ContentItem.BodyPart.Text;
string postImageUrl = post.BlogPostImage.Url;
<div class="slide">
<img src="#postImageUrl" width="625" height="400" alt="Slide #count.ToString()">
<div class="caption" style="bottom: 0">
<h4>#title</h4>
</div>
</div>
}
</div>
</div>
</div>
}
I cannot however figure out how in the world to call that mediapicker field into my list. Any way I try it comes back blank with some kind of querystring in the href. Something like "?23423455657". I took off the variable and that querystring still shows up. What I'm basically trying to accomplish is to put these into a slideshow, with the jquery to start the slideshow in the layout.cshtml. TIA
//SOLUTION:
After days and days of reading and researching I finally found a working solution.
Replacing:
string postImageUrl = post.BlogPostImage.Url;
With:
string postImageUrl = ((ContentItem)post.ContentItem).Parts.SelectMany(p => p.Fields).Where(f => f.Name == "BlogPostImage").First().Storage.Get<string>(null);
Got it from here: http://blog.cloudconstruct.com/post/Creating-a-rotating-image-gallery-in-Orchard-CMS.aspx
I hope it helps anyone stuck in the same boat. I am still curious why I couldn't simply call it the way I had it, but it's working now! :)
You can get the image url using dynamic:
dynamic postItem = post.ContentItem;
var postImageUrl = (string)postItem.BlogPost.BlogPostImage.Url;
Content item, when used as a dynamic object, exposes all its parts (here we're using the part that has the same name as the type, and where the fields are added when adding them from the admin). The part itself has dynamic members for each field (here, BlogPostImage), and from there you can get to the field's properties (Url here).
I tried the above answer with version 1.7 with no joy. I ended up having to download a copy of the source so I could delve into the objects. The below code got me the information I needed and also will help in future when I add extra fields to other content. I used part of the above answer to get to my solution...
IEnumerable<object> blogPosts = Model.ContentItems.ContentItems;
foreach (dynamic post in blogPosts) {
dynamic q = ((ContentItem)post.ContentItem).Parts.SelectMany(p => p.Fields).Where(f => f.Name == "BlogPostImage").First();
string postImageUrl = q.MediaParts[0].MediaUrl;
<img src="#postImageUrl" />
}
Hope this helps somebody.