fancytree folders are never collapsed. All children and parents are displayed without correct nesting structure.
When I copy the exact same data that works in text data source, instead from a web2py (python) controller the folders will not collapse but just display permanently expanded. No js console errors in browser.
original data that works perfectly in text file
FancyTree copies data from python contoller like this
json_list = [{
"alexLink": "http://example.com/",
"kind": "tasks#task",
"id": "MTYwNzEzNjc2OTEyMDI1MzcwNzM6ODUwNjk4NTgzOjExMTkyODk2MjA",
"etag": "\"4qyCALf1j510T_-I20NAMbUHF2k/LTEzNTgzMTMzODg\"",
"title": "Task 01",
"updated": "2015-04-23T19:25:44.000Z",
"selfLink": "",
"position": "00000000002147483647",
"status": "needsAction"
}]
I convert to json: json_list = json.dumps(json_list)
Then use as source:
// Initialize Fancytree
$("#alexTree").fancytree({
checkbox: true,
selectMode: 3,
source: {{=XML(json_list)}},
postProcess: function(event, data){
data.result = convertData(data.response);
},
select: function(event, data) {
window.open(data.node.data.alexLink, "_blank");
Data looks same as in text file source. What could be causing the folders to not contract with children under them?
I can't see an obvious reason, why your sample is not working (I assume persistence extension is off?).
Except maybe for {{=XML(json_list)}}, that may do something unexpected.
I guess a debuggable demo is needed, to find out.
Anyway, your sample does this:
Generate tree data in a native (none-Fancytree) format
Generate a html page with an embedded <script> tag that in turn has this data embedded as string
On page-load the tree is created with native data and post processing is done client-side
Fancytree was designed with this pattern in mind (among others):
Have a static page with an empty <div id="tree"> element and include some JavaScript
On page-load initialize the tree and pass an ajax url for source.
This will immediately display the page for your users, while loading is deferred (showing a spinning icon)
A soon as the data arrives, the tree will update.
(You could also consider to do the conversion server-side and send valid Fancytree-compatible data.)
This would probably deliver a smoother user experience. It would also allow to refresh tree data without reloading the page, by calling tree.reload().
Related
In my UI5 app, I have a view with a table (sap.m.Table), populated by data coming from the back-end at onInit hook. The problem is that onInit is executed only once per view instance:
It is only called once per View instance, unlike the onBeforeRendering and onAfterRendering hooks.
And if a user decides to leave this view (e.g., back navigation) and to reopen it later, the onInit will not be recalled, and thus the data will not be retrieved again, and the table content will not reflect the possible changes.
To ensure that the data are retrieved every time the view is opened, I tried to get the data at onBeforeRendering, but this hook is also called just once. The only way, that I have found, to force onBeforeRendering to be called every time the view is opened, is to add the following code into onInit method:
onInit: function () {
this.getView().addEventDelegate({
onBeforeShow: this.onBeforeShow,
}, this);
}
My questions:
Why, without the code snippet above in onInit, is the onBeforeRendering not triggered every time the view is displayed?
What does exactly the code snippet above do?
The alternative technique: to use patternMatched and routeMatched. But which one of these three approaches is more common?
Why (...) is the onBeforeRendering not triggered every time the view is displayed?
I think there is a misconception of what "rendering" means. In UI5, when a control is "rendering", its corresponding HTML element is being modified or created in the DOM by the RenderManager. I.e. onBeforeRendering means literally "before the render function of the control (here: View) is called".
onBeforeRendering does not mean that it's called before the paint event from the browser (For that, modern browsers provide high-level APIs such as Intersection Observer).
Rendered controls can be in the DOM but not visible in the viewport at the same time.
Coming back to the question; the reason why on*Rendering is not triggered, is because the control has been already rendered before. This can be seen when user navigates via navTo and then back again. The corresponding view element is already in the DOM, so there is no need to call render again, meaning no on*Rendering triggered.
What does the code snippet exactly do?
this.getView().addEventDelegate({
onBeforeShow: this.onBeforeShow,
}, this);
addEventDelegate adds a listener to the events that are fired on the control (not by the control).
E.g.: The view contains events like afterInit, beforeExit, ...
Doing addEventDelegate({onAfterInit}) won't work since afterInit is fired by this control (view).
Doing addEventDelegate({onmouseover}) works since it's fired on this control.
The same applies to the onBeforeShow. The view doesn't contain any events like beforeShow, afterShow, etc.. Those are fired on the view by the NavContainer (e.g. by <App> on its child, view). Documentation about those events can be found in:
API reference: sap.m.NavContainerChild
Topic Events Fired on the Pages
See also a similar question https://stackoverflow.com/questions/44882085/why-does-onbeforefirstshow-work/44882676
The alternative technique: to use patternMatched (...). But which one of these three approaches is more common?
By "three approaches" I assume you mean:
on*Rendering (1st approach),
on*Show (2nd approach),
and the above mentioned routing events like patternMatched (3rd approach).
The answer is, as always, it depends on what you're trying to achieve. But usually, we:
Use the 2nd approach (NavContainerChild events) if the application does not have a routing concept (no sap.ui5/routing in manifest.json).
Use the 2nd approach with onAfterShow if the intent is to set initial focus after the view is displayed. See How to Set Initial Focus in a View?
Use the 3rd approach to get notified about the route pattern being matched. This approach is commonly used to do something every time the view (NavContainerChild) is displayed, for example, to do Context Binding after navigating to a detail page. How it works:
When router.navTo() is called, the next corresponding view and controller are created.
In onInit of the newly created controller, you assign a patternMatched handler.
On navigation, the URL hash value will change. The router (internally the HashChanger) notices the URL change, leading to Route firing patternMatched by which your handler will be invoked. See the TL;DR below.
⚠️ Personal opinion: avoid 1st approach as an application developer. Avoid doing anything in onBeforeRendering and in onAfterRendering since it's unpredictable how often the render function is called from the viewpoint of the application. For control developers, those hooks are absolutely necessary. But for application developers, there are often better alternatives.
TL;DR
Forget on(Before|After)Rendering. Use the (pattern)Matched event from the route instead:
{
// Controller
onInit() {
const myRoute = this.getOwnerComponent().getRouter().getRoute("routeName");
myRoute.attachPatternMatched(this.onMyRoutePatternMatched, this);
},
onMyRoutePatternMatched(event) {
// your code when the view is about to be displayed ..
},
}
You should use
onInit: function() {
let route = this.getOwnerComponent().getRouter().getRoute("yourroutename");
route.attachPatternMatched(this.onRoutePatternMatched, this);
// ...
},
This route event will be triggered every time the route pattern is matched in your routing config. In the above example, the function onRoutePatternMatched will be called every time the route is loaded through navigation.
onRoutePatternMatched: function(event) {
// this logic will repeat itself every time the route pattern is matched
},
Question #3:
patternMatched is going to get hit when your router is matched on the URL or on the router.navTo method.
routeMatched is going to get hit on the same occasion as patternMatched and when its child routes get navigated to.
Imagine, you have a master view on route A and it’s detail on route B. If the user navigates directly to route B, it makes sense to render the target associated to route B and also the target associated to route A.
To conclude:
patternMatched: direct route match
routeMatched:
The pattern of a route in this router.
The pattern of its sub-route.
The pattern of its nested route. When this occurs, the nestedRoute parameter is set with the instance of nested route.
The answers of Boghyon Hoffmann and Bernard are currently the best solution to the problem of needing an event to perform some action when the page is navigated to.
There is an option where it's possible to configure the target of a route to clear the control aggregation and force it to be re-rendered every time it is navigated to. With this configurantion the events onBeforeRendering and onAfterRendering will be called when navigating to the page. However, it is not a recommended solution as it will cause performance issues with the constant re-rendering and it should set to false when working with sap.m.NavContainer or it would cause transitions stop happening and other issues. If you are working on a legacy project that uses sap.ui.ux3.Shell, which is currently deprecated, it is a possible solution.
So to configure this option we need to set the option clearControlAggregation to true in the manifest.json, it is false by default when using sap.m.routing.Router.
In this link you can check all the options you have for the route targets:
https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Targets
"config": {
"routerClass": "sap.ui.core.routing.Router",
"viewPath": "TestApp.view",
"viewType": "XML",
"clearControlAggregation": true,
"controlId": "Main",
"controlAggregation": "pages",
"transition": "slide"
}
It is also possible to add this option only for a specific target like this:
"targets": {
"Test1": {
"viewType": "XML",
"viewName": "Test1",
"clearControlAggregation": true,
"title": "Test1"
},
"Test2": {
"viewType": "XML",
"viewName": "Test2",
"title": "Test2"
}
}
My webapge
I am building a webpage for a conference. The page contains multiple objects like speaker (Person), or session (Event).
My problem
I would like to add semantic data to the page, so that search engines and social networks can easily understand its content. I would like to both describe the page as a whole, and parts of it - like a single session
My question
How do I mark up both the entire page and inner objects in a standard way?
Can I use multiple JSON-LD in the same page, or should I use JSON-LD for the page as a whole and other markups for inner objects?
Clarification, following the comments
Thanks for pointing out the answer to JSON-LD Schema.org: Multiple video/image page. However, it I am not sure where should I put the different JSON-LD objects so that each of them refers to a specific part of the document. Should they be linked using div ids? Should the <script> be placed inside the relevant div?
You can include multiple structured-data elements on a page if you wish as per Google's guideline:
https://developers.google.com/search/docs/guides/sd-policies#multiple-elements-on-a-page
The best way to tell whether it can parse your expected JSON-LD objects and attributes is to use their Structured Data Testing Tool
https://search.google.com/structured-data/testing-tool
I am not sure where should I put the different JSON-LD objects so that each of them refers to a specific part of the document.
With JSON-LD, the structured data is not "coupled" to the HTML. (That would be possible with the syntaxes Microdata and RDFa, but even there, after extracting the structured data, it’s of no relevance anymore on which HTML elements it was specified.)
If you have a page about two persons, you would just provide one WebPage and two Person objects. To be able to make statements about these objects from other places, it’s a good practice to give them URIs, which can be specified with #id. For the WebPage, the URI would typically be the canonical URL of the page.
<script type="application/ld+json">
{
"#context": "http://schema.org",
"#type": "WebPage",
"#id": "",
"about": [
{
"#type": "Person",
"#id": "#person-1"
},
{
"#type": "Person",
"#id": "#person-2"
}
]
}
</script>
Theoretically, there could be a property which allows you to reference the HTML elements which describe the entity. Schema.org currently has properties like that in Pending, which are used for SpeakableSpecification (Pending): cssSelector (Pending) (using CSS selectors to reference the elements), xpath (Pending) (using XPath to reference the elements). But these can’t be used for other cases (as currently defined). So you would have to define your own property for this purpose.
Should they be linked using div ids?
You could provide the id value in the object’s url property. It doesn’t "link" the HTML to the structured data, though. It just conveys: You can find information about the entity described by this node object by following this URL.
Often each entity has its own page, but it’s perfectly fine to describe multiple entities on the same page, of course.
Expanding the example from above:
<script type="application/ld+json">
{
"#context": "http://schema.org",
"#type": "WebPage",
"#id": "",
"about": [
{
"#type": "Person",
"#id": "#person-1",
"url": "#about-person-1"
},
{
"#type": "Person",
"#id": "#person-2",
"url": "#about-person-2"
}
]
}
</script>
Should the <script> be placed inside the relevant div?
For the structured data, it makes no difference where in the HTML document the script elements are placed.
You could place all objects in one script element (like in the examples above), or you could use multiple script elements, but then you should reference their #id values so that you can still make use of properties like about (example in my answer linked above). It makes no difference semantically.
I was trying to understand how can we make drop-down values in dialog box easily authorable?
Select lists in dialogs can load their options dynamically from anywhere, so long as they are provided as an array of values in JSON format (from the docs):
[
{
value: "pink",
text: "Pink",
qtip: "Real Pink"
}
]
So one solution would be to:
Create a new template that would allow an editor to add/remove values from a list — make this editable for content authors as per any other content (e.g. using the page properties, or components that you can drag onto that template).
Create a Servlet that will parse those values & output them in the expected JSON.
Register that servlet, e.g. via a path (/bin/selectvalues).
Using the cqinclude xtype to load in your values:
i.e.
<select
type="select"
xtype="selection"
options="/bin/selectvalues"/>
If you are looking for a drop-in solution for this, take a look at http://adobe-consulting-services.github.io/acs-aem-commons/features/generic-lists.html. This supports easily authorable lists of name/value pairs which can be used (without writing additional code) in:
Classic UI Dialogs
Touch UI Dialogs
Touch UI Asset Metadata Editor
I'm using wysihtml5 with bootstrap-wysihtml5 to save html templates (and at some point I need to load them on wysihtml5). I want to be able to allow the input tag on the editor.
The user workflow should be this:
Create a template with a "input=text" and a "input=checkbox" and save it.
Load the saved template on wysiwyg5 (it will show the inputs so the user can check the checkbox, input some value on the other, add some another HTML, etc)
Save all data again (including the input values/checked status)
In the Bootstrap-wysihtml5 parserRules I added:
"input": { check_attributes: { "type": "alt", "name":"alt","id":"alt", "value": "alt" }},
And this works fine to add (on HTML source view) <input/> elements but I cannot save the value the user insert on the inputs.
Any hints on how to accomplish this?
Edit 1:
Here is a fiddle showing the problem: http://jsfiddle.net/KuA9Z/1/
For me this works:
$('#id-of-textarea').val()
This returns:
<input type="text"> Some text.
If this does not work, can you provide a fiddle with your code?
I am using my own fork https://github.com/Waxolunist/bootstrap3-wysihtml5-bower/ with bootstrap3.
For example, if you type something in upper-right google/yahoo search box in firefox there will be some kind 'suggested auto complete' sort of thing.
Another example is in youtube search box and Stackoverflow tags edit box just below this question preview. How do they work? What technology behind 'em?
What technology behind 'em?
In case you are wondering which data structure is being used underneath then its called "trie" and for using less space compared to tries you can use "DAFSA"
How do they work?
both are implemented as a tree, where each node of tree corresponds to one character in a string and the character which appears before is parent of character which appears later e.g. The strings "tap", "taps", "top", and "tops" stored in a Trie (left) and a DAFSA (right),so as you begin to type tap..the tree is traversed based on the characters typed and shows the suggestions based on some weight assigned to each word, weight may be assigned based on usage frequency of the word.
Looking up string in worst case is O(m) time where m is the length of string.
Image is being referenced from the wikipedia articel : DAFSA,trie
That's done with the use of AJAX, this site has a nice tutorial on it:
AJAX Suggest Tutorial, and the WaybackMachine version, as website seems down.
A database with keywords and a bit of code is all there is to it as far as I know.
I'm learning how to use it right now actually, for work. :)
Another resource is w3schools. They have covered it as well.
They use JavaScript to normally:
Look at a local array of all possible values
Request another page (i.e. /autocomplete.php?q=partialText) in the background.
Call a webservice.
When the JavaScript has the list of entries to show it modifies the page to show the autocomplete box.
If you want to put an autocomplete box on your website I have used and found the following to be very good. It is also based on the popular jQuery framework.
jQuery autocomplete plugin
It's quite simple.
Client side:
Grab keystrokes in form field
On keystroke make an AJAX request to server
If another keystroke is entered immediately, cancel current AJAX request as it is obsolete now
Make a new AJAX requested with updated characters in form field
Show server response to client
Server side:
All words are already bucketed alphabetically
If client request comes in for "ove" find all words starting with ove, ordered by popularity
Return top matches to client
There's an excellent open-source Country selector in the Smashing Magazine article (link below) which includes a discussion of the usability challenges with plain autocomplete solutions, and fixes them.
While I'm UX, not Dev, I'm certain a clever developer could adapt this open-source code to handle other kinds of selections—not just the names of countries. :)
The article that describes the usability issues that this selector resolves.
The demo and open-source download. Try it!
Disclaimer: I have no connection to the folks who made this Country selector. I just happen to know about it, and I like to share information about Usability with developers, FWIW.
There's as many answers to this as there are different implementations of them. Our AutoCompleter which you can see a sample of in Stacked works by raising an event which then is handled in the codebehind of the .ASPX page from which you populate a ControlCollection with whatever controls you wish. We're however in Stacked only using Literal controls with Text content being anchor links. But we could add up checkboxes or images if we wanted to...
If you're on ASP.NET our AutoCompleter is a great place to start. If you're on "something else" then probably ScriptAculous AutoCompleter is another nice place to start...
i also have been recently working on autocomplete feature and we used lucene to index the text to be shown in autocomplete. Searching is fast with lucene. Somethings to look at when working with autocomplete data:
Freshness of suggestions,
Dependency on the long term data,
Regional dependency,
Language dependency
Update 2022
The marked answer is a little outdated. Suggestions autocomplete seems like magic on the surface but really what it is under the hood is
fast asynch communication and
searching through a list of keywords
Send a string to your database then return response in JSON to loop/iterate through. Then repeat as user types.
One good example is done with YELP Fusion.
Below is example with small library autocomplete.js
$(function () {
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
$(".sbx-custom__input").autocomplete({
source: availableTags
});
});
<!--jqueryui-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<!--autocompletejs-->
<script src="https://cdn.jsdelivr.net/npm/#tarekraafat/autocomplete.js#10.2.6/dist/autoComplete.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#tarekraafat/autocomplete.js#10.2.6/dist/css/autoComplete.min.css">
<!--input-->
<input class="sbx-custom__input" autocomplete="on" required="required" placeholder="autocomplete...">
here is the simple example from my code(using jquery + jquery ui). first i requested all data with ajax that i prefixed to inbox then i clicked one of them and so it redirects to another action succesfully.
$("#Name").autocomplete({
source: function (request, response) {
var prefix = { Name: request.term};
$.ajax({
url: '#Url.Action("FilterMastersByName", "JsonResult")',
data: JSON.stringify(prefix),
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
success: function (data) {
response($.map(data, function (item) {
return item;
}))
},
error: function (response) {
alert(response.responseText);
},
failure: function (response) {
alert(response.responseText);
}
});
},
select: function (e, i) {
var abc=i.item.val;
let a = document.createElement('a');
a.href = `/Home/GetMasterById?masterId=${abc}`;
a.click();
},
minLength: 1
});
});
Dont forget setFilterMastersByName action to httppost and GetMasterById to httpget
Here is one for MooTools.