I have hard time parsing a structure using simple html dom
`<div class="class1">class</div>
`<div class="class2">data2</div>
`<div class="class2">data3</div>
`<div class="class1">text</div>
`<div class="class2">...</div>
Since structure is not nested so I have hard time parsing it.
I want database be grabbed as class1 as main heading and class2 as sub. So for every class1, I want to read all class2 tags until another class1 arrives. Hope it make sense.
You can check class elements to see if you reached another class1 element. Something like this explains the general idea:
$str =<<<'html'
<div class="class1">class</div>
<div class="class2">data2</div>
<div class="class2">data3</div>
<div class="class1">text</div>
<div class="class2">...</div>
html;
$html = str_get_html($str);
// Store all results in a multi dimensional array
$result = [];
$group = -1;
foreach ($html->find('div') as $elem) {
// if element class is class1, create a new array to gather data
if($elem->class == 'class1') {
$group++;
$result[$group] = [];
}
$result[$group][] = $elem->plaintext;
}
print_r($result);
So everytime we encounter a new element with class1 we start to gather information in a new array, so the final result is a nested array that groups elements the way you described.
Related
My collection contains escape HTML entities such as <, > etc. when i retrieve and display it, it escapes and prints the respective characters but does not interpret the HTML.
Eg:
Template.Help.helpers({
faq_content: function () {
var x = Help.findOne({});
return x.faq;
}
})
assuming x takes the following value:
x =
{
faq: <div class=\"help_articles\">\\n
....some_content....\\n
</div>
tips: <div class=\"help_articles\">\\n
....some_content....\\n
</div>
articles: <div class=\"help_articles\">\\n
....some_content....\\n
</div>
}
Even when i place this helper using three curly braces i.e,
{{{faq_content}}} ,
The content that gets displayed on the Browser is
<div class="help_articles">....some_content....</div>.
This gets displayed as text but i want it to be interpreted into HTML.
Please suggest some solution
Looks like you need to _.unescape() the string once before presenting inside the {{{}}} braces:
Template.Help.helpers({
faq_content: function () {
var x = Help.findOne({});
return _.unescape(x.faq);
}
})
Be careful that the FAQ content is fully controlled by you and not submitted by your users without cleansing the content.
Using Meteor, I am trying to loop through and display a list of notes from a database with an option to delete each note.
Here is the HTML (using Handlebars.js)
<template name="Notes">
{{#each NoteArr}}
<div class="Note">
<h2>{{Title}}</h2>
<p>{{Body}}</p>
<span class="deleteNote">Delete</span>
</div>
{{/each}}
</template>
And here is the client Javascript
Template.Notes.events = {
"click .deleteNote" : function(){
noteID = $('.deleteNote').parent().attr("id");
Notes.remove({ID:noteID});
}
};
This grabs the first instance of .deleteNote, so unless I'm trying to delete the first one, that won't help. How can I grab the parent of the particular instance of .deleteNote that was clicked, not just the first one it finds?
The reason why the first element is deleted is.. in your .click event, you are accesssing the div directly as $('.deleteNote').parent() which grabs the first node in the html which has a class .deleteNode.
Now to remove the specific notes, from the collection: Every document in the collection has a unique _id attribute which is generated automatically. assign that unique _id of the document to the span element as <span id= "{{_id}}" class="deleteNote">Delete</span>.
So the cilck event will look like:
Template.Notes.events = {
"click .deleteNote" : function(e){
var noteID = e.currentTarget.id;
Notes.remove({_id:noteID});
}
};
And the template will look like:
<template name="Notes">
{{#each NoteArr}}
<div class="Note">
<h2>{{Title}}</h2>
<p>{{Body}}</p>
<span id= "{{_id}}" class="deleteNote">Delete</span>
</div>
{{/each}}
</template>
Untested code, but hope this will help solving your issue.
The _id of a note is stored in 'this' as well. In addition, the remove function accepts '_id' as a string. So this should work as well:
Template.Notes.events = {
'click .deleteNote': function(){ return Notes.remove(this._id)}
}
A few benefits here. Less querying the DOM for information. Less jQuery. Fewer lines of code to think about. Cleaner templates.
So I'm trying to display via a template some data from a JSON request. The data has some nested objects (of varying amounts) similar to this:
data: "Some data",
nested: [
{
nested_data: "foo",
bar: "foobar"
}, ...
],
...
I've managed to parse the JSON fine and store it in a WinJS.Binding.List object, and bound the result to a template. The problem I've got is actually displaying the nested JSON data in my template. The template looks something like this:
<div class="appTemplate">
<div class="innerTemplate">
<h1 data-win-bind="innerText: data">
<h2 data-win-bind="innerText: nested">
</div>
</div>
When I run the program those, the 'nested' part of the template is just a bunch of [object Object] rather than the data I want to display.
Is there some way I can create the template such that it can handle the nested data? Would it be easier to define a template function? Not sure what to do here...
There is no built in way to do this -- the only built in control for iterating over data is the List View. However, you cannot nest List Views.
There are two ways to solve this problem, depending on your desired outcome:
1) Stringifying the nested data: You can do this by writing a WinJS.Binding.converter that will convert your array of data into a single string value. Example
Code:
WinJS.Namespace.define("Examples.Converters", {
nestedDataConverter: WinJS.Binding.converter(function(input) {
// Assume input is array
var result = "";
input.forEach(function(data) {
result += data.nested_data + ", " + bar + ";";
});
result result;
}),
});
Template:
My recommended solution would to build your own control that will take your array (or WinJS.Binding.List) and create the elements / layouts needed. I have done this in a project I work on, and it's super simple.
Example Template:
<div class="appTemplate" data-win-control="WinJS.Binding.Template">
<div class="innerTemplate">
<h1 data-win-bind="innerText: data">
<h2 data-win-bind="innerText: nested Examples.Converters.nestedDataConverter">
</div>
</div>
Now, the h2 will have the single-string version of that data.
2) Create a control to display the data richly: To do this you need to create a new WinJS control and use it in your template.
Control example:
WinJS.Namespace.define("Examples.Controls", {
Stamper: WinJS.Class.define(function(element, options) {
WinJS.UI.setOptions(this, options);
this.domElement = element;
}, {
domElement: nullm
_data: null,
data: {
set: function(val) {
this._data = val;
updateLayout();
}
},
updateLayout: function() {
this.domElement.innerHTML = "";
if(!this._data) {
return;
}
this._data.forEach(function(item) {
var el = document.createElement("div");
el.textContent = "Data: " + item.nested_data + ", " + item.bar;
this.domElement.appendChild(el);
}.bind(this));
}
}),
});
Template:
<div class="appTemplate" data-win-control="WinJS.Binding.Template">
<div class="innerTemplate">
<h1 data-win-bind="innerText: data"></h1>
<div data-win-control="Examples.Controls.Stamper"
data-win-bind="winControl.data: nested"></div>
</div>
</div>
This control can be extended to render nested templates, and other items. It's all a question of how complex you want to get.
Try data-win-bind="innerText: nested.nested_data"
This will be much easier with a template function.
The built in bindable templates are great for templates that are logicless, but fall short when you need the template to make decisions based on data values, and in your case, deeper properties.
Is there any way to get the object of the current iteration in Handlebars?
code:
<script id="HandleBarTemplate1" type="text/x-handlebars-template">
{{#each objArr}}
<img src="{{objField1}}"/>
<strong>Name:</strong> {{objField2}}
<input type="button" onclick="processObject({{.}});"/>
{{/each}}
</script>
I've mentioned processObject({{.}}) That is incorrect. That's where I need a replacement/Solution.Hope you get what I'm tryin' to say.
The contents of objArr might look like
var objArr = [{objField1:"smith.jpg",objField2:"Smith"},{objField1:"jane.jpg",objField2:"Jane"},...]
Template compilation code is:
var source = document.getElementById("HandleBarTemplate1").innerHTML;
var compiledTemplate = Handlebars.compile(source);
var html = compiledTemplate({objArr:objArr});
If I could get the reference to the Object, then It's so easy to process the data. Rather than passing a field to the function and searching through entire array to get the required object and then process it.
I Prefer a solution without a custom block helper/custom expression helper but if none exists, I'd rather go for a custom block helper. Any solution without searching through the entire array is welcome!
I would suggest a different route.
Knowing that you already have a reference to objArr, make a global or name-spaced variable pointing to it.
For example: window.objArr = objArr;
Create your click handler that does whatever you wish:
function processObject(key){
}
call that with your key inside your template:
< script id="HandleBarTemplate1" type="text/x-handlebars-template">
{{#each objArr}}
<img src="{{objField1}}"/>
<strong>Name:</strong> {{objField2}}
<input type="button" onclick="processObject({{objField2}});"/>
{{/each}}
</script>
Other alternatives:
Customer Handler.
Other alternatives:
If you can't create a reference to objArray, you might could store the properties of the object within data- attributes if you're using html5. processObject could then retrieve them.
I do this with a Handlebars helper and an array of context objects.
Somewhere we have an array of context objects, which will store a reference to each context object we need to reference:
var ContextObjects = [];
We need to register a Handlebars helper function that stores the current context object in the array for us when we place "{{obj}}" in the template. The helper returns the index of the object in the array, which is what gets rendered:
// Must be function and not lambda to preserve the correct 'this'
Handlebars.registerHelper("obj", function ()
{
var contextObject = this; // the current context object
var index = ContextObjects.indexOf(contextObject);
if (index < 0)
{
index = ContextObjects.length;
ContextObjects[index] = contextObject;
}
return index;
});
Next we need a function to retrieve the object from the index on the element:
// Get the context object for the current event (e.g. click or context).
function GetContextObject(event)
{
var $e = $(event.currentTarget);
var index = $e.attr("data-obj");
return ContextObjects[index];
}
We tie them together in the template something like this:
{{#each Items}}
<div data-obj="{{obj}}" onclick="DoSomething(GetContextObject(event));">...</div>
{{/each}}
Which may render something like this:
<div data-obj="0" onclick="DoSomething(GetContextObject(event));">...</div>
<div data-obj="1" onclick="DoSomething(GetContextObject(event));">...</div>
<div data-obj="2" onclick="DoSomething(GetContextObject(event));">...</div>
...
I am looking for a way to work with collections of elements in Selenium with PHPunit. Let's say I have the below code:
<div class="test">aaa</div>
<div class="test">bbb</div>
<div class="test">ccc</div>
I would like to be able to work on all the <div> elements inside an each loop, let's say by selecting the elements based on their class with //div[#class='test']
$divs = ... //
foreach ($divs as $div) {
// do something
}
The function to get the content of your entire page is :- getHtmlSource()
So your final function call to load HTML would be something like ..
$dom->loadHTML($this->getHtmlSource());
In PHP, if you want to work with some HTML data, a great solution is using the DOMDocument class -- which means being able to work with DOM methods -- via its DOMDocument::loadHTML() method.
Loading your HTML with DOMDocument :
$dom = new DOMDocument();
$dom->loadHTML('HERE YOUR HTML STRING');
You can then instanciate a DOMXpath object :
$xpath = new DOMXPath($dom);
Which will allow you to extract data from the HTML, using XPath Queries, with DOMXPath::query() :
$entries = $xpath->query('//div[#class="test"]'); // notice quotes
if ($entries->length > 0) {
foreach ($entries as $entry) {
// Work on the $entry -- which is a DOM node/element
}
}