SAPUI5: DOM change commands interfere with each other - javascript

My List Binding:
<StandardListItem
title="{NewProposalTitle_DE}"
description="Stream: {Stream}, Time: {starttime}"
press="handlePress"
>
I want to change the title's binding to title="{NewProposalTitle_EN}" from the oData model, when the user is english.
I looked into expression binding but this doesn't seem to work with dynamic data:
title="{= {language} === "de" ? {NewProposalTitle_DE} : {NewProposalTitle_EN} }
So I tried a different approach where I iterate through all items from a SAPUI5 List and want to change the (german) title attribute to the english one if the language of the user is english and add a number to the Item's counter attribute.
var Items = this.getView().byId("idList").getItems();
for (var i = 0; i < Items.length; ++i) {
if(language === "en"){
Items[i].setTitle(oData.results[i].Title_EN);
}
Items[i].setCounter(numbers[i]);
}
If the language is german, everything works like a charm and the counter is set correctly, but if the language is english, things get fishy.
When the Title gets updated with the english title, the counter is set back to 0 (default). Even more strange, if the german and the english version of the title are equal (so no change needs to be done) the counter is still shown (i assume SAPUI5 is smart enough to recognize, that no change needs to be done).
I encountered the same problem, when I tried to add a Style Class for the Item:
Items[i].addStyleClass("blue");
again, as long as the title doesn't get changed it works.
I tried changing the order in every possible way without success.
I also tried to setCounter and addStyleClass and omitted the setTitle command but only one affected the DOM.

Please don't translate this way! You might end up overwriting data in your other models. Use the i18n resource models to do your translations! It's the go-to, built in way to translate any text in your application to any other language.
SAPUI5 loads the right ones by default.
If your browser's language is set to German, it will try to load i18n_de_DE.properties first, (not sure of the exact name for German, you'll find out if you look at the network tab of the browser. Could be just _de.properties if there's such a thing as _de_AT or something) before falling back to the default i18n.properties.
If you start an application from any of the usual IDE's like webIDE or eclipse, the model is already created for you.
The idea is to set the title to an identifier, like
<Label text={i18n>MY_TRANSLATABLE_TEXT} />
In your i18n.properties file, you might default to English:
MY_TRANSLATABLE_TEXT=Customer
While your German properties file has
MY_TRANSLATABLE_TEXT=Kunde
The translation is then done for you without trying to mess with the titles directly.

Thank you all for your answers! I figured out a solution for my problem:
In the controller, I check for the users language and update the property binding:
Items[i].bindProperty("title", "NewProposalTitle_EN");
It may not be the most elegant or correct way, but it works for me (at the moment).
I hope you all have a great day!

title="{= {language} === "de" ? {NewProposalTitle_DE} : {NewProposalTitle_EN} }
There are some syntactical issues in your expression binding. Try with this:
title="{= ${language} === 'de' ? ${NewProposalTitle_DE} : ${NewProposalTitle_EN} }"
Besides that, I don't think it's a good idea to put translatable texts into the entity set as properties. I'd recommend to define field label (e.g. for the NewProposal property) translated in each language as a UI text annotation
(such as sap:label).
When an ODataModel is created, the service metadata will be requested immediately with the corresponding language query sap-language =<automatically detected>.
Thus the property labels come already translated from the backend system, contained in the metadata, which you can bind in the view via special property binding syntax. E.g.:
In Metadata
<EntityType Name="BusinessPartner" sap:content-version="1">
<Key>
<PropertyRef Name="BusinessPartnerID" />
</Key>
<Property Name="BusinessPartnerID" Type="Edm.String"
sap:label="Business Partner ID"
sap:creatable="false"
sap:updatable="false"
/>
...
</EntityType>
Property Metadata Binding
<StandardListItem
title="{myModelName>BusinessPartnerID/##sap:label}"
press=".handlePress"
/>

Related

How can I get combinationId from array of id_attributes

I use the Cart::updateQty() function in my module for prestashop. I'd like also to use the $id_product_attribute parameter, but I'm unable to find the desired value for this when I have array of id_attributes only.
I need behavior similar to the default selecting product attributes on the product page and then adding it to cart, but I'd like to do this on the server side.
What I was able to find out is that when I'm changing a product attributes on the product page, the html input with id="idCombination"
#idCombination
is being filled with a proper value and then it's being POST'ed with ajax to the server. Unfortunately, I cannot work out how the actual searching is being made and how to do this in js or php code.
Any help would be appreciated.
In a default product page these combinations are transferred with a variable $combination within a ProductController.php with a method assignAttributesGroups() and then defined a js variable inside product.tpl via condition
{if isset($combinations) && $combinations}
{addJsDef combinations=$combinations}
{/if}
after that everithing going on in a product.js file which do all switch job and substitute id="idCombination" after each changing.
So you can to repeat a default approach to implement your requirements even though it looks a bit complicated. Also, I think you don't need such a completed $combination variable with all that information and you can add only that what you need. I hope it clarifies you a direction to work.

Angular ngGrid Tree Control: Make a round trip on group expand

I am trying to use ngGrid to make somewhat of a "tree-control" which I can build dynamically by calling API's. ngGrid allows for grouping on rows, yet the nature of it requires that all rows be present at the beginning. This is unfortunate for the fact that an API to pull back all generation data for a File Integrity Monitoring system would be insanely slow and stupid. Instead, I wish to build the "tree" dynamically on the expansion of each generation.
I am trying to inject children (ngRows) into a group-row (ngAggregate) on a callback, yet I do not think that I am calling the correct constructor for the ngRows for the fact that the rows are ignored by the control
Through the use of the aggregateTemplate option on the gridOptions for ngGrid, I have been able to intersept the expansion of a group quite easily.
(maybe not easily, but still)
I've replaced the ng-click of the default template:
ng-click="row.toggleExpand()"
with:
ng-click="$parent.$parent.rowExpanded(row)"
I know that it's a bit of a hack, but we can get to that later. For now, it gets the job done.
The way that I discovered how to work my way up the $scope to my rowExpanded function was by setting a breakpoint in ngGrid's "row.toggleExpand" function and calling it from the template as so:
ng-click="row.toggleExpand(this)"
Once I retrieve the group I want, I call an API to get the children for said group. I then need to make the return as children of the row. I decided to do this by calling ngGrid's ngRow factory:
row.children = [];
for(var i = 0; i < childData.length; i++)
{
row.children[row.children.length] = row.rowFactory.buildEntityRow(childData[i], i);
}
row.toggleExpand();
... yet this does not appear to be working. The rows are not showing up after I do the expand! Why won't my rows show up?
Here's my current Plunker!
By the way
I've placed a debugger statement within the group-expand callback. As long as you have your debugger open, you should catch a breakpoint on the expansion of a group.
Thanks everybody!
I found my answer, I'm an idiot....
I got this control working, and then realized that it was a total hack, that I could have used the control the way it was meant to be used and it would have worked much better, had much better work-flow, and it would have saved me an entire day of development. If you are wondering how you use the control this way, the answer is that you don't.
I got the stupid thing to work by updating my data structure after the round trip and forcing the grid to refresh, pretty obvious. I had to set the grid options so that groups were always expanded and I had to control the collapser icon logic myself, outside of ngGrid. I never called row.toggleExpand. I also hid any rows with null values by a function call within an ng-if on my rowTemplate. After all that was said and done, I put my foot in my mouth.

Reference management with HTML/CSS/Javascript (BibTeX style)

HTML+CSS+Javascript tools offer a great way to create beautiful presentations (e.g. reveal.js + MathJax). However, I usually need to add citations to my presentations, and I would like to do that in a systematic way (so the bibliography is organized and the references are well-formatted). This is something that get's handled quite easily in LaTeX through BibTeX.
The best solution I've found so far comes from a library called bibtex-js. It seems to do a good job on rendering BiBTeX files in HTML as a bibliography listing, which is partially what I want. However, I don't only need to render bibliography listings, but also, I need to refer to entries in that bibliography by some index, and get a uniformly formatted reference marker. Take, for example, how LaTeX usually handles this problem:
%In thebibliography.bib
#article{darwin1859origins,
title={On the origins of species by means of natural selection},
author={Darwin, Charles},
journal={London: Murray},
year={1859}
}
%In mydocument.tex
As \cite{darwin1859origins} sustains in his ground-breaking book...
The previous code would be rendered as something like "As Darwin(1859) sustains in his ground-breaking book". Moreover, the formatting in which the citation is rendered could also be customizable (e.g. "Darwin,1859", "(Darwing,1859)", "[DWN59]", "[1]", etc.).
So the question is, how do you handle a similar task on a HTML document?
Thank you all in advance!
Yes, there is an emacs extension called org-mode, which is text processing with a markdown like syntax.
This can export to reveal-js trough this: https://github.com/yjwen/org-reveal
Or in my case I use the spacemacs extension: https://github.com/syl20bnr/spacemacs/tree/master/layers/%2Bemacs/org#revealjs-support
So org mode is an intermediate format that compiles to whatever you want, ie reveal-js, html, or even latex.
This includes a reference management system: https://github.com/jkitchin/org-ref
I'm unhappy with this for reveal.js, if we use this with reveal.js we end up having all the citation being presented as the link (whatever we type after cite:) and the full format citations are grouped on whatever slide you place them (so if you have more than 3 you can't read it correctly, although I guess its in the HTML). What I want is either the numbers I get in latex or footnote based citations, because in case of slides footnotes work kind off good.
This will of course work for just HTML pages, however you probably want to have presentations like me. I was searching for a solution for this when I stumbled upon this unanswered question so I guess here is your answer.
I made a project, incidentally also called bibtex-js. Available on npm.
I made it because most BibTeX parsers out there take considerable shortcuts in parsing. This one aligns closely with the authoritative document on BibTeX, Tame the BeaST and so works pretty well in terms of references and parsing author names, which seems what you are after.
I would say, based on some bibliographic standard, roll your own inline citation function:
import {parseBibFile, normalizeFieldValue} from "bibtex";
// Parse bib file
const bibFile = parseBibFile(bibtexString); // insert the darwin1859origins example as a string
// Sanity check: print all ids of entries in the bibfile
console.log(Object.keys(bibFile.entries$));
// Get the entry we are after
const entry = bibFile.getEntry("darwin1859origins");
// Get the relevant fields
// normalizeFieldValue turns a BibTeX string into a Javascript string
const year = normalizeFieldValue(entry.getField("year"));
// get first author
// "author" is a special kind of BibTeX field
const author = entry.getField("author").authors$[0];
function inlineCite(author){
return "("
+ (author.firstNames
.concat(author.vons)
.concat(author.lastNames)
.concat(author.jrs)).join(" ")
+ "," + year
+ ")";
}
console.log(inlineCite(author)); // (Charles Darwin, 1859)
You can do something complicated with et al. if you have multiple authors.

Dashboard widget: getting version number from Info.plist

I'm writing a Dashboard widget in Dashcode, and on the back side, I've got a string for credits. I want to include the widget's version number in that string, but if possible, I want to programmatically grab it from the CFBundleVersion or CFBundleShortVersionString key in Info.plist to avoid having to change the number in multiple places if and when I update the widget.
Searches on Apple's developer documentation, Google and various forums have proven fruitless so far. What I'd like to know is whether there's a built-in way to do this that Apple included but forgot to mention (like var version = widget.version(); or something), or whether my script will have to pull in and parse the entire plist before plucking out the one value I actually want.
Thanks for any help you can provide!
I seem to have found the answer: use Dashcode's "data source" facility to read in Info.plist as an XML data source. From there, this blog post showed me how to traverse the plist's structure and get the correct string (in this case, the fifth <string> element in the file, corresponding to CFBundleShortVersionString.
The function I ended up with:
function getWidgetVersion() {
var dataSource = dashcode.getDataSource("infoPlist");
var version = dataSource.selection().valueForKey("dict").valueForKey("string")[4]; // This line and the previous could probably be combined for the sake of brevity
if (typeof(version) == 'string') {
document.getElementById("creditsLabel").innerHTML += version; //I'll change this to just pass the number on
}
}
Since the text of the creditsLabel div has already been started off with a localized string, I get a nice little label saying "Version 1.0".

How should I associate server-side data with client-side UI elements in HTML?

I run into this problem constantly while developing AJAX applications. Let's say I want users to be able to click on a "flag" icon associated with each comment on my site, which results in an AJAX request being sent to the server, requesting that the comment be flagged. I need to associate a comment id with the comment on the client side so that the AJAX request may communicate to the server which comment to flag.
This page explains a number of ways to annotate HTML in this manner, but none of them are very satisfactory. While I could just use an id or class attribute to associate the comment id with the flag button (e.g. id="comment_1998221"), this fails with more complex data that doesn't fit well into those attributes (e.g. arbitrary strings). Is there a best practice for this sort of thing? Every time I need to do this, I end up with some kludge like using the id attribute, a hidden form field, or worse yet a span set to display:none.
The HTML5 data-* attributes seem like a perfect solution, but I've seen a lot of animosity toward them, which makes me think that people must already have a solution they're happy with. I'd love to know what it is.
This page explains a number of ways to annotate HTML in this manner, but none of them are very satisfactory.
Still, they are pretty much all you've got. Although that page isn't a terribly good summary, there are errors and it misunderstands what ‘unobtrusive’ JavaScript means.
For example it is in fact perfectly valid to put a script element inside body — just not directly inside a table element. You could put all the script fragments at the bottom of the table, or put each row in its own table, or even, with some limitations if you are intending to mutate the DOM, inside the row in question.
Setting “id="comment-123"” then scanning for all rows with an id starting with ‘comment-’ is indeed good for your particular case. For setting non-identifying extra info attributes, you could use either HTML5 data-attributes or hack it into the classname using eg. “class="comment type-foo data-bar"”. Of course both IDs and classnames have their limits about what characters you can use, but it's possible to encode any string down to valid strings. For example, you could use a custom URL-style encoding to hide non-alphanumeric characters:
<tr class="greeting-Hello_21_20_E2_98_BA">
...
</tr>
function getClassAttr(el, name) {
var prefix= name+'-';
var classes= el.className.split(' ');
for (var i= classes.length; i-->0;) {
if (classes[i].substring(0, prefix.length)==prefix) {
var value= classes[i].substring(prefix.length);
return decodeURIComponent(value.split('_').join('%'));
}
}
return null;
}
var greeting= getClassAttr(tr, 'greeting'); // "Hello! ☺"
You can even store complex non-string values in this way, by encoding them JavaScript or JSON strings then retrieving them using exec (or JSON.parse where available).
However, if you are putting anything non-trivial in there it soon gets messy. That's where you may prefer comments. You can fit anything in here except the sequence '--', which is easily escaped if it happens to come up in a string.
<table>
<tr class="comment">
<td>...</td>
<!-- {"id": 123, "user": 456} -->
</tr>
</table>
function getLastComment(node) {
var results= [];
for (var i= node.childNodes.length; i-->0;)
if (node.childNodes[i]==8)
return node.childNodes[i];
return null;
}
var user= getLastComment(tr).user;
The summary warns that this may not be guaranteed to work because XML parsers may discard comments, but then DOM Level 3 LS parsers must keep them by default, and every browser and major XML library so far does.
jQuery data API is nice for this.
Suppose you have the following DOM...
<div class="comment">
Flag
Some text
</div>
Then, assuming you are also loading these elements by ajax, you can do
$(".comment").data('someKey', (any javascript value/object));
Then later, upon click handler to the flag, you can do...
$(".flagSelector").click(function(ev) {
var extraData = $(this).closest(".comment").data("someKey");
// use extraData along with your request
});
If you are generating the comments on the server side and shipping them with the initial page, you need to figure out how to initialize the data. One way would be to have unique ID-s for the comment and upon pageload, still load the custom data from the server by Ajax.
Here is how I would do this:
When rendering the page server-side, generate the flag link as a normal link, so that it would work fine if you didn't have javascript enabled.
<a class="flag_link" href="/comment/123/flag/"><img src="flag.gif" /></a>
Then, in the javascript, add a click event to do this by ajax instead. I'll use jQuery for my example, but the same thing is not hard to do without it.
<script>
$('a.flag_link').click(function() {
$.get($(this).attr('href'), function() {
alert('you flagged this comment');
});
});
</script>
Of course, you'll do something more user-friendly than an alert to signal success.

Categories

Resources