How to avoid Polymer element's AttributeChanged event calling Multiple times - javascript

Actually I am using Polymer elements A ,B , C and D in my Application. Consider the below scenario.
1) Element A has boolean attribute named 'devEnable'
2) Element A has attributeChanged event named 'devEnableChanged'
3) And my business logic is implemented in 'devEnableChanged' event
4) Other elements --> B extends A , C extends A , D extends A
Now I run my application, when the attribute devEnable is changed to true or false then devEnableChanged event is getting fired 3 times because of this my business logic also is getting fired 3 times. So I want to avoid firing devEnableChanged event multiple times.
Is there any way to avoid calling it multiple times ?

Using a base polymer element(A) as kind of composition within a template tag of a former derived polymer element(B, C or D) is an option instead of heritage:
index.html:
<!DOCTYPE html>
<html>
<head>
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="import" href="derived-element.html">
</head>
<body>
<derived-element id="derivedElement" value="3546"></derived-element>
<input type="button" id="assignBaseElementButton" value="assign base"/>
<input type="button" id="assignDerivedElementButton" value="assign derived"/>
<input type="text" id="valueField"/>
</body>
<script>
document.getElementById('assignDerivedElementButton').addEventListener('click',function(){
var derivedElement = document.querySelector('#derivedElement');
var textFieldValue = document.getElementById('valueField').value;
// get derived element's attribute value to assign it to a value of valueField textfield
derivedElement.value = textFieldValue;
});
document.getElementById('assignBaseElementButton').addEventListener('click',function(){
var derivedElement = document.querySelector('#derivedElement');
var textFieldValue = document.getElementById('valueField').value;
// get base element's attribute value to assign it to a value of valueField textfield
derivedElement.getBaseElement().value = textFieldValue;
});
</script>
</html>
derived-element.html:
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="/base-element.html">
<polymer-element name="derived-element" attributes="value">
<template>
<!-- embed base element here -->
<base-element id="baseElement"></base-element>
<div>
derived element'value: {{value}}
</div>
</template>
<script>
Polymer('derived-element',{
value: null,
ready:function(){
this.getBaseElement().baseValue = this.value;
},
// watcher for value property
valueChanged: function(oldvalue, newvalue) {
// this method is called once
console.log('my-element', 'oldvalue: ',oldvalue, ' newvalue: ',newvalue);
},
getBaseElement:function(){
return this.$.baseElement;
}
});
</script>
</polymer-element>
base-element:
<link rel="import" href="/bower_components/polymer/polymer.html">
<polymer-element name="base-element">
<template>
<span>Hello from <b>base-element</b>. This is my Shadow DOM. And my value is {{value}}</span>
</template>
<script>
Polymer('base-element',{
value: null,
// watcher for value property
valueChanged: function(oldvalue, newvalue) {
// this method is called once
console.log('base-element', 'oldvalue: ',oldvalue, ' newvalue: ',newvalue);
}
});
</script>
</polymer-element>

Related

How to check input box is empty by v-model in Vue JS?

In my learning app about Vue, I bind message with the input box using v-model of vue. In that, I set another method to check if the input box is empty then I set default message value to something else by default.
This below is my snippet:
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
methods:{
check:function(){
if (this.message==''){
this.message='Please enter text in text box below';
}
}
}
})
<html>
<head>
<link rel="stylesheet" href="index.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!--
v-model:
Used with form input for user synchronize data between front-end and back-end
as seen here message is bind with form input, so whenver we update the form, the var message
will be updated as well
-->
<div id="app">
<p>{{ message }}</p>
<input v-model="message" v-on="check">
</div>
<script src="index.js"></script>
</body>
</html>
However, it seemed like v-on="check" does not work as well as the input box is empty the message does not change. Is there anything I was missing?
PS: I am new to VueJS :)
There are multiple ways to solve this issue like:
Add conditional logic to your template:
<p>{{ message || 'Your default text here'}}</p>
Use computed property
Use filter
Test it Like this:
if (this.message === '' || this.message === null || this.message.value === 0){
this.message='Please enter text in text box below';
}

SAPUI5 Select first item in list after rendering

I have a Master-Detail application and I would like the first list item to be selected automatically once the application is loaded. I have tried the following solution but it does not work:
onAfterRendering: function() {
var oList = this.getView().byId("bankList");
var aItems = oList.getItems("listItems");
console.log(aItems);
if (aItems.length) {
aItems[0].setSelected(true);
}
}
The strange thing is that aItems seems to be empty even though it contains the correct details. Below is what is printed in the console when I console.log(aItems):
The result of console.log(aItems)
Guessing that you are using a sap.m.List, You should use the setSelectedItem() function, that receives the Item object as parameter.
Furthermore I recommend you to avoid using the onAfterRendering lifecycle method, to keep the lifecycle clean. There are usually many items that you can use, for example updateFinished for sap.m.List
Here the snippet
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta charset="utf-8">
<title>MVC with XmlView</title>
<!-- Load UI5, select "blue crystal" theme and the "sap.m" control library -->
<script id='sap-ui-bootstrap' src='https://sapui5.hana.ondemand.com/resources/sap-ui-core.js' data-sap-ui-theme='sap_belize_plus' data-sap-ui-libs='sap.m' data-sap-ui-xx-bindingSyntax='complex'></script>
<!-- DEFINE RE-USE COMPONENTS - NORMALLY DONE IN SEPARATE FILES -->
<!-- define a new (simple) View type as an XmlView
- using data binding for the Button text
- binding a controller method to the Button's "press" event
- also mixing in some plain HTML
note: typically this would be a standalone file -->
<script id="view1" type="sapui5/xmlview">
<mvc:View xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" controllerName="my.own.controller">
<List items="{/options}" mode="SingleSelect" updateFinished="onUpdateFinished">
<StandardListItem title="{value}"></StandardListItem>
</List>
</mvc:View>
</script>
<script>
// define a new (simple) Controller type
sap.ui.controller("my.own.controller", {
onUpdateFinished: function(oEvent) {
var oList = oEvent.getSource();
var aItems = oList.getItems();
oList.setSelectedItem(aItems[0])
}
});
/*** THIS IS THE "APPLICATION" CODE ***/
// create some dummy JSON data
var data = {
options: [{
key: '1',
value: 'option 1'
}, {
key: '2',
value: 'option 2'
}, {
key: '3',
value: 'option 3'
}]
};
var oJSONModel = new sap.ui.model.json.JSONModel();
oJSONModel.setData(data);
// instantiate the View
var myView = sap.ui.xmlview({
viewContent: jQuery('#view1').html()
}); // accessing the HTML inside the script tag above
myView.setModel(oJSONModel);
// put the View onto the screen
myView.placeAt('content');
</script>
</head>
<body id='content' class='sapUiBody'>
</body>
</html>

Data context on JsViews' include tag

I made a snippet to show the issue I'm facing:
<html>
<head>
<title>Demo 1 JsViews</title>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.90/jsviews.min.js"></script>
</head>
<body>
<script id="template" type="text/x-jsrender">
<p>{{:property}}</p>
{^{on ~root.testFunction}}PUSH ME!{{/on}}
{{for nestedObjects}}
{{include #data tmpl="#template-2"/}}
{{/for}}
</script>
<div id="container"></div>
<script id="template-2" type="text/x-jsrender">
<p>{{:~root.property}}</p>
{^{on ~root.testFunction}}PUSH ME!{{/on}}
</script>
<div id="container-2"></div>
<script>
data = {
property : "PARENT",
testFunction : function(){ alert(this.property); },
nestedObjects : [
{
id: 0,
property: "CHILD1",
testFunction : function(){ alert(this.property);}
},
{
id: 1,
property: "CHILD2",
testFunction : function(){ alert(this.property);}
}]
};
/**
* Funciones
*/
$(document).ready(function(){
var tmpl = $.templates("#template");
tmpl.link("#container", data);
});
</script>
</body>
</html>
As you can see, in '#template-2' it's taking the properties from main data object, not from each nestedObjects' item. I know this would be the normal behaviour.
Is there a way to make include tag to take each nestedObjects' item and not the whole data object as context?
I know that if I remove the '~root' modifiers in '#template-2' it will work as expected, but I need it to work with '~root' modififer if possible.
Thanks in advance :)
After trying many things, I managed to get it working in another way using a helper object, if someone has a similar issue feel free to contact me :).
~root is a built-in helper that points to the top-level data that you passed to the link() or render() method. See
http://www.jsviews.com/#contextualparams#root.
So you can't change it to have it point somewhere else. But you can create your own helpers (not using the reserved root name), such as:
{{include #data ~myroot=#data tmpl="#template-2"/}}
and in template-2 write
{{:~myroot.property}}

How to mutate Polymer v1.x property inside ajax request

I+m trying to populate an array based in thr results that I fetch from an Ajax request via iron-ajax
When I try to Access to my property, my watch window displays "not available"
Below is an little example I recreated for explain my issue:
my-component.html
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../bower_components/app-storage/app-localstorage/app-localstorage-document.html">
<link rel="import" href="../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<dom-module id="my-component">
<template>
<div>
<paper-dropdown-menu
id="location" label="Ubicación"
value="{{vehicle.location}}">
</paper-dropdown-menu>
<paper-dropdown-menu id="city" label="Ciudad">
<paper-menu
class="dropdown-content"
selected="{{city.city_code}}"
attr-for-selected="value"
on-iron-select="estadoSeleccionado">
<template id="" is="dom-repeat" items="[[opcionesDisponibilidad]]" as="i">
<paper-item value="[[i.valor]]">[[i.texto]]</paper-item>
</template>
</paper-menu>
</paper-dropdown-menu>
</div>
<iron-ajax
id="ajax"
method="GET"
url="http://localhost:8080/ws.asmx"
params=""
content-type="application/x-www-form-urlencoded; charset=UTF-8"
handle-as="text"
on-response="handlecities"
debounce-duration="300">
</iron-ajax>
<app-localstorage-document id="localStorageElement" key="myapp.login_data" data="{{loginInfo}}"></app-localstorage-document>
</template>
<script>
Polymer({
is: "my-component",
properties:{
vehicle: {
type: Object,
value:{
id: "",
plate: "",
location: ""
}
},
cities:{
notify: true,
type: Array,
value: [{city_code:'0', city_name:'-'}]
}
},
ready:function () {
this.querycities();
},
querycities: function () {
if (this.$.localStorageElement.storage['myapp.login_data']){
this.$.loginInfo = JSON.parse(this.$.localStorageElement.storage['myapp.login_data']);
} else return false;
this.$.ajax.url = this.$.ajax.url + "/listadoCiudades";
this.$.ajax.verbose = true;
this.$.ajax._boundHandleResponse = this.handlecities;
this.$.ajax.params = {dispositivo:'12345', credencial: this.$.loginInfo.cred};
this.$.ajax.generateRequest();
},
handlecities: function (request) {
console.log(request.response);
var xPath = new SoftXpath();
xPath.registerNamespace("","");
xPath.loadXML(request.response);
var cities = xPath.selectNodes("//Ciudad//*[self::Codigo|self::Nombre]");
var xPathElement = new SoftXpath();
if (cities){
for(var i = 1; i < cities.length;i++){
var c = new Object();
c.Codigo = cities[i-1].text;
c.Nombre = cities[i].text;
this.$.cities.push(c);// <---- Here I have not access to my polymer property ' cities '.
//How to update my polymer var cities ??
//Tried also with:
// this.cities --> undefined
// this.$$('#myassignedId').cities --> undefined
}
}
}
})
</script>
</dom-module>
How can I populate my 'cities' property if appears out of scope ?
Well,
After too many try-error variations, finally I found the cause of error.
Line 65:
this.$.ajax._boundHandleResponse = this.handlecities;
Removed that line and all my polymer attributes returned to normality.
I was using that sentence because I was trying to reuse the component for several ajax requests. 'handlecities' is specified as attribute as well, so is redundant (only when there are no intentions of reuse). I still don´t know how to reuse the component defining a custom handler, but for some reason if i use the above sentence, all polymer properties are lost.
So, in the meantime I´m defining several ajax components one for each ajax request

No communication between custom Polymer components

I've written a single custom Polymer component that has multiple smaller custom components designed to one specific step of an entire process.
The elements are supposed to request specific input parameters and each step builds upon the previous steps. The final component contains the results retrieved from an API that accepts the input gathered from the first steps.
My problem is that I can't get info from one component to the next. I have this issue in practically every component I use, so I suspect the way I want to do it is the problem.
The first problem is that I need to retrieve a list of possibilities from the API, and display this list in a dropdown in multiple child components.
I put this list in a Polymer property, but the auto binding does not fire and the data change is not properly propagated down to the child components (if at all). I define a few iron-ajax elements that fetch the data OK, and have a template with child components that need the output of these iron-ajax elements.
<template>
<iron-ajax auto url="{{typesURL}}" handle-as="json"
on-response="handleTypesResponse"></iron-ajax>
<!-- more iron-ajax elements -->
<section id="activities">
<template is="dom-repeat" items="{{activities}}" as="activity">
<my-activity activity="{{activity}}"
types="{{types}}" />
</template>
</section>
<!-- more classic html elements -->
</template>
My url is straightforward and it casts into an Array just fine, but the template in activities is not reloaded and edited if I add an item to the activity array. How could I accomplish this?
A second issue that I'm facing is getting the 'result' of a component.
I have a form where I want the user to specify times. For this, I'm using paper-time-picker. When choosing a time however, and returning to the underlying web component, the time is not changed.
This is the code that defines the dialog:
<paper-dialog id="timeDialog" modal class="paper-time-picker-dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation">
<h2>Start time <span>{{activity.name}}</span></h2>
<paper-time-picker id="startTimePicker"></paper-time-picker>
<div class="buttons">
<paper-button dialog-dismiss>Cancel</paper-button>
<paper-button dialog-confirm autofocus on-tap="confirmTime">OK</paper-button>
</div>
</paper-dialog>
And these functions defined below the Polymer properties both show and retrieve the dialog and result:
showAttachedDialog: function (id) {
var button = this.$$(id);
if (!button.hasAttribute('data-dialog')) {
return;
}
var dialogId = '#' + button.getAttribute('data-dialog');
var dialog = this.$$(dialogId);
if (dialog) {
dialog.open();
}
},
confirmTime: function () {
this.activity['time'] = this.$$('#timePicker').time;
this.notifyPath('activity.time', this.activity.time);
console.log(this.activity);
console.log("time activity: " + this.activity.time);
},
The console output turns up empty (as in 'time activity: '). Does anyone see what I'm doing wrong? Do ask for more information if you require any to see what I may be missing here.
Thank you.
regarding activities thingy - you should use Polymer's push, pop etc, see https://www.polymer-project.org/1.0/docs/devguide/templates.html#dom-repeat. Or manualy invoke notifyPath. This works:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>xxz</title>
<script src="./bower_components/webcomponentsjs/webcomponents.js"></script>
<link rel="import" href="./bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/paper-time-picker/paper-time-picker.html">
<link rel="import" href="bower_components/paper-input/paper-input.html">
<link rel="import" href="bower_components/paper-button/paper-button.html">
<link rel="import" href="bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="bower_components/neon-animation/animations/scale-up-animation.html">
<link rel="import" href="bower_components/neon-animation/animations/fade-out-animation.html">
<dom-module id="my-activity">
<style>
:host{
display: block;
}
</style>
<template>
Activity <span>{{activity.name}}</span>
<paper-button on-click=showAttachedDialog>Time dialog <span>{{activity.time}}</span></paper-button>
<paper-dialog id="timeDialog" modal class="paper-time-picker-dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation">
<h2>Activity name <span>{{activity.name}}</span></h2>
<!-- from https://github.com/bendavis78/paper-time-picker -->
<paper-time-picker id="startTimePicker"></paper-time-picker>
<!--unfortunately paper-time-picker is not a iron-input (is="iron-input") so you can't jut bind to its value-->
<!--start time: <paper-input id="time" value="{{activity.time}}"></paper-input>-->
<div class="buttons">
<paper-button dialog-dismiss>Cancel</paper-button>
<paper-button dialog-confirm autofocus on-tap="confirmTime">OK</paper-button>
</div>
</paper-dialog>
</template>
<script>
HTMLImports.whenReady(function () {
Polymer({
is: "my-activity",
activity: Object,
listeners: {
'startTimePicker.time-changed': '_onTimeChanged'
},
//this is not needed if time is copied in confirmTime
_onTimeChanged: function(){
if (this.activity) {//first event is fired before activity is set
console.log("time activity: " + this.activity.time+' startTimePicker.time='+ this.$.startTimePicker.time);
this.activity.time = this.$.startTimePicker.time;
}
},
showAttachedDialog: function (event /*id*/) {
// that's I can't comprehend
// var button = this.$$(id);
// if (!button.hasAttribute('data-dialog')) {
// return;
// }
//
// var dialogId = '#' + button.getAttribute('data-dialog');
// var dialog = this.$$(dialogId);
// if (dialog) {
// dialog.open();
// }
this.$.timeDialog.open();
},
confirmTime: function () {
// this.activity['time'] = this.$$('#timePicker').time;
// this.notifyPath('activity.time', this.activity.time);
this.activity.time = this.$.startTimePicker.time;
console.log("time activity: " + this.activity.time+' startTimePicker.time='+ this.$.startTimePicker.time);
}
})
});
</script>
</dom-module>
<dom-module id="my-element">
<template>
<paper-button on-click=handleTypesResponse>Kinda call ajax</paper-button>
<section>
<template is="dom-repeat" items="{{activities}}" as="activity">
<my-activity activity="{{activity}}" />
</template>
</section>
</template>
<script>
HTMLImports.whenReady(function () {
Polymer({
is: "my-element",
handleTypesResponse: function () {
this.splice('activities', 0, 3);
for (var i = 0; i < 10; i++) {
console.log('i=' + i);
this.push('activities', {name: 'name'+i, time: new Date()});
}
},
ready: function() {
this.activities = [];
this.push('activities', {name: 'name XXX', time: new Date()});
this.push('activities', {name: 'name YY', time: new Date()});
this.push('activities', {name: 'name ZZ', time: new Date()});
}
});
});
</script>
</dom-module>
</head>
<body>
<my-element></my-element>
</body>
</html>

Categories