Custom ThingsBoard widget: set gauge value property - widget

I need to edit the "Speed gauge" widget to show zero value when certain condition is met. This action should be executed in the onDataUpdated() function.
This widget inherits methods from the "TbAnalogueRadialGauge" class, which contains an update() method. If I'm not wrong, this would be its implementation:
update() {
if (this.ctx.data.length > 0) {
const cellData = this.ctx.data[0];
if (cellData.data.length > 0) {
const tvPair = cellData.data[cellData.data.length -
1];
const value = tvPair[1];
if (value !== this.gauge.value) {
this.gauge.value = value;
}
}
}
}
TbAnalogueRadialGauge extends TbAnalogueGauge. TbAnalogueGauge exends TbBaseGauge, where update() is implemented.
It seems to access the this.gauge.value property to update the gauge value. However, when I try to access this property from widget development IDE, it turns out to be undefined.
self.onDataUpdated = function() {
self.ctx.gauge.update();
console.log(self.ctx.gauge.value) // output: undefined
}
Does anyone have any ideas about how to access this property?

You need the following
self.ctx.gauge.gauge.value

Related

Access checkbox value of an item in an array - Sitecore

I cannot seem to get the value of a checkbox field of "NoIndexNoFollow" I have set in my content.
I have tried two of the follow code samples below.
1) I get FALSE for every item even if I check the box in the content editor.
foreach (var item in Model.SiteSearchResults)
{
Sitecore.Data.Fields.CheckboxField checkboxField = Sitecore.Context.Item.Fields["NoIndexNoFollow"];
if (checkboxField.Checked)
{ *CODE*}
}
2) Nothing populates here.
foreach (var item in Model.SiteSearchResults)
{
var toindex = Sitecore.Context.Item.Fields["NoIndexNoFollow"].ToString();
if (toindex == "1")
{ *CODE* }
}
I am getting no value from these items.....Not sure the right way to call a checkbox field even though either of these seem to be working for other examples I am looking at.
You can use extensions methods to make the method re-useable, but the key thing to take away from these solutions is utilising the utility function from Sitecore MainUtil.GetBool(checkboxField.Value, false);
using System;
using Sitecore;
using Sitecore.Data.Fields;
using Sitecore.Resources.Media;
namespace MyProject.Extensions
{
public static class FieldExtensions
{
public static bool IsChecked(this Field checkboxField)
{
if (checkboxField == null)
{
throw new ArgumentNullException(nameof(checkboxField));
}
return MainUtil.GetBool(checkboxField.Value, false);
}
}
public static class ItemExtensions
{
public static bool IsChecked(this Item item, ID fieldId)
{
var checkboxField = item.Fields[fieldId];
if (checkboxField == null)
{
throw new ArgumentNullException(nameof(checkboxField));
}
return MainUtil.GetBool(checkboxField.Value, false);
}
}
}
MyRendering.cshtml - using FieldExtensions
#using MyProject.Extensions
#model Sitecore.Mvc.Presentation.RenderingModel
#{
var noIndexNoFollow = Model.Item.Fields["NoIndexNoFollow"].IsChecked();
}
MyRendering.cshtml - using ItemExtensions
#using MyProject.Extensions
#using Sitecore.Mvc
#model Sitecore.Mvc.Presentation.RenderingModel
#{
var noIndexNoFollow = Model.Item.IsChecked(Model.Item.Fields["NoIndexNoFollow"].ID);
}
In your comment you wrote that Model.SiteSearchResults is a list of Sitecore ID.
You need to get item with this ID first and then check the value of the field using for example MainUtil.GetBool() method like:
foreach (var id in Model.SiteSearchResults)
{
if (Sitecore.MainUtil.GetBool(Sitecore.Context.Database.GetItem(id)["NoIndexNoFollow"], false))
{
<text>checked</text>
}
else
{
<text>not checked</text>
}
}
foreach (var item in Model.SiteSearchResults)
{
Database database = Sitecore.Context.Database;
Item myItem = database.GetItem(item.ItemId);
var fieldValue = myItem.Fields["NoIndexNoFollow"];
string noIndexValue = Convert.ToString(fieldValue);
}
So after thinking more about what you guys said, I came up with a little easier solution that works perfect for what I need. I really appreciate all the insights!

Error creating a Web component

I'm new to Web components and I am trying to create a very simple component to understand how it works. But I have a problem creating one. I followed the steps mentioned in both chrome and Mozilla docs but I still cant create one successfully and also couldn't find the problem.
class toolTip extends HTMLElement {
var msg = this.getAttribute('msg');
var value = this.getAttribute('value');
console.log(msg);
console.log(value);
this.innerHTML = msg + ' - ' + value;
}
customElements.define('mdm-tooltip', toolTip);
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Web Components</title>
</head>
<body>
<mdm-tooltip value='1st tooltip' msg='this the 1st tooltip created using WC'></mdm-tooltip>
<mdm-tooltip value='2nd tooltip' msg='I replaced the existing text'>Im the existing text</mdm-tooltip>
</body>
<script src="main.js" defer></script>
</html>
This is the error browser throws,
I'm running this code in Chrome V67.0.3396.99
Within a class, you need to define methods that actually contain executable code. In your case, your code looks a lot like initialization code, so a constructor seems appropriate.
class ToolTip extends HTMLElement {
constructor() {
let msg = this.getAttribute('msg');
let value = this.getAttribute('value');
console.log(msg);
console.log(value);
this.innerHTML = msg + ' - ' + value;
}
}
customElements.define('mdm-tooltip', ToolTip);
Also, one of the naming conventions in JavaScript is that classes should be pascal-cased (start with a capital letter).
J.P. ten Berge is mostly correct. But... According to the rules of a Web Component Constructor you can not and should not do several things:
https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance
4.13.2 Requirements for custom element constructors
When authoring custom element constructors, authors are bound by the following conformance requirements:
A parameter-less call to super() must be the first statement in the constructor body, to establish the correct prototype chain and this value before any further code is run.
A return statement must not appear anywhere inside the constructor body, unless it is a simple early-return (return or return this).
The constructor must not use the document.write() or document.open(type, replace) methods.
The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on upgrades makes the element less usable.
The element must not gain any attributes or children, as this violates the expectations of consumers who use the createElement or createElementNS methods.
In general, work should be deferred to connectedCallback as much as possible—especially work involving fetching resources or rendering. However, note that connectedCallback can be called more than once, so any initialization work that is truly one-time will need a guard to prevent it from running twice.
In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.
Moving the code into the connectedCallback is a better plan:
class ToolTip extends HTMLElement {
connectedCallback() {
var msg = this.getAttribute('msg');
var value = this.getAttribute('value');
console.log(msg);
console.log(value);
this.innerHTML = msg + ' - ' + value;
}
}
customElements.define('mdm-tooltip', ToolTip);
<mdm-tooltip msg="help me" value="10"></mdm-tooltip>
But you can also change it to something like this:
class ToolTip extends HTMLElement {
constructor() {
super();
this._msg = '';
this._value = '';
}
static get observedAttributes() {
return [ 'value', 'msg' ];
}
connectedCallback() {
this._render();
}
attributeChangedCallback(attr, oldVal, newVal) {
if (oldVal !== newVal) {
this['_'+attr] = newVal; // This will set either `this._msg` or `this._value`
this._render();
}
}
_render() {
this.innerHTML = `${this._msg} - ${this._value}`;
}
}
customElements.define('mdm-tooltip', ToolTip);
setTimeout(() => {
var el = document.querySelector('mdm-tooltip');
el.setAttribute('value', 'Free');
el.setAttribute('msg', 'I like getting stuff for');
}, 1000);
<mdm-tooltip msg="Help Me" value="10"></mdm-tooltip>
In this example we use observedAttributes and attributeChangedCallback to see when either the value or msg attributes change. When they do we re-render the component.
You can also use properties when setting values:
class ToolTip extends HTMLElement {
constructor() {
super();
this._msg = '';
this._value = '';
}
static get observedAttributes() {
return [ 'value', 'msg' ];
}
connectedCallback() {
this._render();
}
attributeChangedCallback(attr, oldVal, newVal) {
if (oldVal !== newVal) {
this['_'+attr] = newVal; // This will set either `this._msg` or `this._value`
this._render();
}
}
get msg() {
return this._msg;
}
set msg(val) {
if (this._msg !== val) {
this._msg = val;
this._render();
}
}
get value() {
return this._value;
}
set value(val) {
if (this._value !== val) {
this._value = val;
this._render();
}
}
_render() {
this.innerHTML = `${this._msg} - ${this._value}`;
}
}
customElements.define('mdm-tooltip', ToolTip);
var el = document.createElement('mdm-tooltip');
el.value = 10;
el.msg = 'Five times two equals';
document.querySelector('.here').append(el);
setTimeout(() => {
var el = document.querySelector('mdm-tooltip');
el.value = [1,2,3];
el.msg = 'I like getting stuff for';
}, 2000);
<div class="here"></div>
In this example I added properties for value and msg. Now, instead of having to use setAttribute you can now set the properties directly and the properties do not need to be strings like the attributes do.

How to determine if a getter is defined before accessing it?

I have a set of get functions in JS such as:
get UserName() {
return this.userModel.Name;
}
I want the ability to check if the function exist before I call it.
I tried:
if (this.UserName == 'function')...
but it's always false, since userModel.name is a string, typeof UserName returns 'string' type and not a 'function'.
any idea how I can accomplish this ?
One simple way to check that UserName exists (without calling the getter) is to use in:
if ('UserName' in this) {
// this.UserName is defined
}
If you need a stronger check where you directly access the getter function, use Object.getOwnPropertyDescriptor:
var userNameDesc = Object.getOwnPropertyDescriptor(this, 'UserName');
if (userNameDesc && userNameDesc.get) {
// this.UserName is definitely a getter and is defined
}
You can use Object.getOwnPropertyDescriptor() which returns basically the same data structure that is fed to Object.defineProperty() like this:
let descriptor = Object.getOwnPropertyDescriptor(this, "UserName");
if (descriptor && typeof descriptor.get === "function") {
// this.UserName is a getter function
}
Or, if you want more granular info, you can do this:
let descriptor = Object.getOwnPropertyDescriptor(this, "UserName");
if (!descriptor) {
// property doesn't exist
} else if (typeof descriptor.get === "function") {
// this.UserName is a getter function
} else if (typeof descriptor.value === "function") {
// property directly contains a function (that is just a regular function)
} else {
// property exists, but it does not have a getter function and
// is not a regular function
}
You can also test many other properties of the descriptor such as value, set, writable, configurable, enumerable as described here on MDN.

dynamic objects in if condition

how to make if condition which contains dynamic object? i tried this way, but error
function pass(xxx:String,yyy:String,zzz:String)
{
//trace(xxx,yyy,zzz);
if (this[xxx].hitTestObject(this[yyy])) //an original if (obj1.hitTestObject(obj2))
{
trace("right");
}
else
{
trace("fail");
}
}
"this[]" is not work, TypeError: Error #1010: A term is undefined and has no properties.
"this[]" can work if it is outside "if".
Is there any other way for this problem? Thanks before
You should use getChildByName(), if you are transferring names of the MCs, but check if that name is a direct child of this.
function pass(xxx:String,yyy:String,zzz:String):void {
var x=this.getChildByName(xxx);
if (!x) return;
var y=this.getChildByName(yyy);
if (!y) return; // insert similar for zzz here
if (x.hitTestObject(y)) {
trace("right");
}
else
{
trace("fail");
}
}
Otherwise specify what inputs does thje function have.
Unless you have a specific reason to be supplying the object names as Strings, I suggest changing the argument types to DisplayObject:
function pass(a:DisplayObject, b:DisplayObject):void
{
if(a.hitTestObject(b))
{
trace("right");
}
else
{
trace("fail");
}
}
If you need to use the Strings, just do this:
var obj1:DisplayObject = getChildByName("obj1");
var obj2:DisplayObject= getChildByName("obj2");
pass(obj1, obj2);

Why does TRY / CATCH overwrite default property values?

I'm using a Value Object which can receive an object when it is instantiated, so its default values can be updated directly when a new VO is created, like so:
public class SeatSettingsVO
{
public var globalPosition:Point = new Point(0, 0);
public var dealerChipOffset:Point = new Point(0, 0);
public var chipStackOffset:Point = new Point(0, 0);
public function SeatSettingsVO(obj:Object = null)
{
if (obj)
parseSettings(obj);
}
}
The parseSettings method uses a try/catch block in order to get only the existing properties in the object passed to the constructor (or at least, that would be the intention):
private function parseSettings(obj:Object):void
{
try
{
this.globalPosition = obj.globalPosition;
this.chipStackOffset = obj.chipStackOffset;
this.dealerChipOffset = obj.dealerChipOffset;
}
catch (error:Error)
{
}
}
Now consider this scenario: a new Value Object needs to be created, but with only one of the three properties defined:
new SeatSettingsVO({globalPosition:new Point(300, 277)})
The problem is that if obj does not contain a particular property (e.g. chipStackOffset), instead of maintaining the initial property value (Point(0,0)), the method overwrites it to null.
My guess is that accessing non-existent properties on an Object class instance, does not trigger an error, but rather, null is returned, which in turn causes the default value to be overwritten. Can anyone explain this behavior, and possibly suggest a solution ?
Thank you very much.
A slightly more succinct solution than the others:
this.globalPosition = obj.globalPosition || DEFAULT_GLOBAL_POSITION;
Like in Python, the || operator returns the first operand if that operand evaluates to something besides 0, null, false, NaN, "", or undefined. Otherwise, it returns the second operand as-is:
trace(new Point(3, 3) || "hi"); //(x=3, y=3)
trace(false || "hi"); //hi
trace("hi" || "bye"); //hi
trace(0 || null); //null
trace(NaN || 0); //0
trace("" || undefined); //undefined
trace(undefined || new Point(0.4, 0)); //(x=0.4, y=0)
trace(null || false); //false
As a result, you can use it to check whether a value is defined, use that value if so, and use a default value if not. I'm honestly not sure if it makes your code more or less readable, but it's an option.
Flex Objects have a hasOwnProperty() method that you might find useful. You can use this to check if a dynamic object has a parameter defined, and only pull it if it exists, instead of getting nulls.
if (obj.hasOwnProperty("globalPosition"))
this.globalPosition = obj.globalPosition;
//etc...
In this case, your object is dynamic so you don't get an exception if the property doesn't exist. You do, however, get undefined. undefined evaluates to null, so you can always say:
this.globalPosition = obj.globalPosition ? obj.globalPosition : default;
where default is whatever you want to put there... even this.globalPosition would work if you want to set it back to what it was.
You can also ask if the property exists:
if( "globalPosition" in obj)
private function parseSettings(obj:Object):void
{
try
{
this.globalPosition = obj.globalPosition;
this.chipStackOffset = obj.chipStackOffset;// when error occured here,
// this.chipStackOffset still waiting for a value to set and it sets to null.
// probably dealerChipOffset doesnt change by default value.
this.dealerChipOffset = obj.dealerChipOffset; // this is {0,0} point prob,didnt try it.
}
catch (error:Error)
{
}
}
I would use somthing like below. Hope it helps.
private function parseSettings(obj:Object):void
{
for(var name in obj){
this[name] = obj[name];
}
}