I'm trying to learn polymer, and I'm trying make a basic messaging framework. So I created a little polymer-element called messages-framework that will display messages, and after 3 seconds remove the message. How can expose a method that will add a message to this element?
Here's my polymer element
<link rel="import" href="../bower_components/paper-input/paper-input-decorator.html">
<link rel="import" href="../bower_components/paper-button/paper-button.html">
<polymer-element name="messages-framework">
<template>
<ul>
<template repeat="{{ m in messages }}">
<li>{{ m }}</li>
</template>
</ul>
<paper-input-decorator label="message" floatinglabel>
<input is="core-input" value="{{ message }}" type="text">
</paper-input-decorator>
<paper-button raised on-tap="{{ addMessage }}">Add Message</paper-button>
</template>
<script>
Polymer({
messages: [],
addMessage: function () {
this.messages = this.messages.concat(this.message);
setTimeout(this.removeMsg.bind(this, this.message), 3000);
this.message = "";
},
removeMsg: function (msg) {
this.messages = this.messages.filter(function (m) {
return msg !== m;
});
}
});
</script>
</polymer-element>
Any help would be appreciated!
I don't think I phrased my question well. If have had two polymer elements, for instance messages-framework and a newuser-form, if newuser-form needed to add to the messages-framework, how would that polymer element use messages-framework API?
Exposing API in Polymer is just like one would do in every day elements.
Get a reference to the Element. eg var p = document.getElemnetTagName('p');
use it's methods. eg p.innerHTML = "Hello World" or p.setAttribute(attribute,value)
Example:
Your Custom Element
<polymer-element name="messages-framework">
<template>
//all your core markup
</template>
<script>
Polymer({
messages: [],
addMessage: function () {
this.messages = this.messages.concat(this.message);
setTimeout(this.removeMsg.bind(this, this.message), 3000);
this.message = "";
},
removeMsg: function (msg) {
this.messages = this.messages.filter(function (m) {
return msg !== m;
});
}
});
</script>
</polymer-element>
Using it's API
<body>
//Initialing the Element by loading it on to the page
<messages-framework></messages-framework>
<script>
var messagesFramework = document.getElementsByTagName('messages-framework');
//this will call the addMessage() method of the element
messagesFramework.addMessage();
</script>
</body>
Related
1) If I want to select element on dom-if condition and run observer how can I achieve that scenario i.e I have dropdown which is wrapped inside dom-if and on page load some observer is changing flag to true,which trigger dom-if condition to render that dropdown,but the problem is when page loads I bind the options for dropdown in observer which get the element this.$.elementID || this.$.querySelector('#elementID') and binds it so I am not getting that element but in ui it shows blank dropdown without options so I guess element is not getting selected.
Please help?
<template is="dom-if" if="[[flag]]" restamp="true">
<dropdown id = "elementID"></dropdown>
</template>
JS:
properties:{
list: {
type: Array,
notify: true,
value: [{label:"f1",value:"f1"},{label:"f2",value:"f2"}]
}
}
static get observers() {
return [
'_bindDrop(list)',
];
}
_bindDrop(list) {
const select = this.$.querySelector('#elementID');
if (select) {
select.options.length = 0;
list.forEach(function (item) {
const option = document.createElement('option');
option.textContent = item.label;
option.value = item.value;
select.appendChild(option);
});
}
}
or
2) How to add dynamic observer method on an element in dom-if condition,if element gets flag to true then it adds observer method ?
He an example add, remove options with Polymer-2.x :
Demo
window.addEventListener('WebComponentsReady', () => {
class XFoo extends Polymer.Element {
static get is() { return 'x-foo'; }
static get properties() {
return {
selected:{type:String}, style:{type:String},
list: {
type: Array,
value() {return [{label:"f1",value:"f1", flag:false},{label:"f2",value:"f2", flag:true},{label:"f3",value:"f3", flag:true}] }
}
};
}
static get observers(){ return ['_toogleSelected(selected)', '_listChanged(list.*)']}
_toogleSelected(s){
let objinx = this.list.findIndex(o => o.label === s);
let obj = this.list[objinx]
obj.flag = !obj.flag;
this.set('list.'+objinx, obj)
this.notifyPath('list.'+ objinx)
}
_styleIt(s) {
return s? "green;":"red;";
}
_listChanged(l){
console.log('List changed', l)
}
}
customElements.define(XFoo.is, XFoo);
});
<head>
<base href="https://cdn.rawgit.com/download/polymer-cdn/2.6.0.2/lib/">
<script src="webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="paper-item/paper-item.html">
<link rel="import" href="paper-listbox/paper-listbox.html">
<link rel="import" href="paper-input/paper-input.html">
<link rel="import" href="iron-selector/iron-selector.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<paper-dropdown-menu label="Listed Items">
<paper-listbox slot="dropdown-content" >
<template is="dom-repeat" items="[[list]]" id="lists" mutable-data>
<template is="dom-if" if="[[item.flag]]">
<paper-item >[[item.value]] - [[item.flag]]<paper-item>
</template>
</template>
</paper-listbox>
</paper-dropdown-menu>
<div>Options display Toogle</div>
<iron-selector attr-for-selected="name" selected="{{selected}}" >
<template is="dom-repeat" items="[[list]]" mutable-data>
<button name="[[item.label]]" id="[[item.label]]" style="background-color:{{_styleIt(item.flag)}}"> [[item.value]]</button>
</template>
</iron-selector>
</template>
</dom-module>
</body>
How should i display the date and time on web page by using template helper.Here is the code i wrote but it does not work.
I wrote the {{display_time}} part in html and javascript. I think display_time helper part is correct but i don't know how to render it in html.
Js file
if (Meteor.isClient) {
// counter starts at 0
Session.setDefault('counter', 0);
Template.hello.helpers({
counter: function () {
return Session.get('counter');
}
});
Template.hello.events({
'click button': function () {
// increment the counter when button is clicked
Session.set('counter', Session.get('counter') + 1);
}
});
Template.display_time.helpers({
datelist:function(){
var begin = new Date();
return Session.get('time');
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
html file
<head>
<title>my_first_app</title>
</head>
<body>
<h1>Hello from Me</h1>
{{> hello}}
{{> display_time}}
</body>
<template name="hello">
<button>Click Me</button>
<p>You've pressed the button {{counter}} times.</p>
</template>
<template name = "display_time">
<p>The time now is:{{time}}</p>
</template>
This is my first project using web components and I'm a rookie at best with js/jquery, so I am not sure why this happening.
I have a custom element built in "search-form.html", all of my components and scripts are then brought in via a master "components.html" in the head of my index as such...
index.html:
<head>
<meta charset='utf-8'>
<script src="/static/template/js/webcomponents.min.js"></script>
<link rel="import" href="/static/template/components/components.html">
<link rel="icon" type="image/png" href="/static/template/img/favicon.png">
</head>
components.html:
<!-- POLYMER MUST BE LOADED -->
<link rel="import" href="/static/template/polymer/polymer.html">
<!-- TEMPLATE SCRIPTS -->
<link rel="import" href="/static/template/components/scripts.html">
<!-- Loads jquery and other scripts -->
<!-- TEMPLATE COMPONENTS -->
<link rel="import" href="/static/template/components/search-form.html">
then the search-form.html looks like this:
<!-- Defines element markup -->
<dom-module id="search-form">
<template>
<div class="input-prepend input-append">
<form id="search" method="get" action="/property/search">
<div class="btn-group">
<button id="search-type" class="btn btn-inverse dropdown-toggle" data-toggle="dropdown">
Search by
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a data-name="city" data-label="city">City</a></li>
<li><a data-name="zip" data-label="zip">Zip Code</a></li>
<li><a data-name="mls" data-label="mls">MLS Number</a></li>
</ul>
</div>
<input id="input-tag" class="input-xlarge" type="text" placeholder="Select a search type..." data-provide="typeahead" value="" />
<button type="submit" class="btn"><i class="fa fa-search"></i></button>
</form>
</div>
</template>
<script>
/********************************
/ TYPEAHEAD ARRAY
/*******************************/
var obj = {
"city" : [],
"zip" : [],
};
$(document).ready(function() {
/*dynamic zipcode*/
for(i=43000; i<=45999;i++){
obj.zip.push({val: i.toString(), string: i.toString()});
}
for(i=48000; i<=49999;i++){
obj.zip.push({val: i.toString(), string: i.toString()});
}
});
/********************************
/ SEARCH TYPEAHEAD FUNCTION
/*******************************/
$(function searchTag($) {
var data = [];
$('.dropdown-menu > li > a').on("click", function() {
data = $(this).data('name');
});
var that = this;
$('#input-tag').typeahead({
source: function(query, process) {
var results = _.map(obj[data], function(value) {
return value.val;
});
process(results);
},
highlighter: function(val) {
var value = _.find(obj[data], function(p) {
return p.val == val;
});
return value.string;
},
updater: function(val) {
var value = _.find(obj[data], function(p) {
return p.val == val;
});
that.setSelected(value);
return value.string;
}
});
this.setSelected = function(value) {
$('#input-tag').val(value.string);
//$('#input-tag').attr('data-value', value.val);
$('#input-tag').data('value', value.val);
};
});
/********************************
/ QUICK SEARCH TAG FUNCTION
/*******************************/
$(function () {
var caret = ' <span class="caret"></span>';
function searchSelect() {
$('.dropdown-menu > li > a').on("click", function() {
$('#search-type').html($(this).text() + caret);
$('#input-tag').attr('placeholder', $(this).data('label'));
$('#input-tag').attr('name', $(this).data('label'));
});
}searchSelect();
});
Polymer({
is: 'search-form',
created: function() {},
ready: function() {},
attached: function() {},
detached: function() {},
attributeChanged: function(name, type) {}
});
</script>
</dom-module>
so the var obj is declared in search-form.html
finally, because of the way our back end is written, I have to run a loop on index.html to get the array to be used in var obj for "city" : [],
that looks like this:
/*TYPEAHEAD ARRAY*/
//MUST BE RUN FROM SHELL BECAUSE OF THE TMPL LOOP
<!-- TMPL_LOOP Cities -->
obj.city.push({val: "<!-- TMPL_VAR city_name -->", string: "<!-- TMPL_VAR city_name -->"})
<!-- /TMPL_LOOP -->
So now the problem. This all works without error in chrome, but I get the same error in FF, IE11, and Edge. That error is:
ReferenceError: obj is not defined
obj.city.push({val: "ALLEN PARK", string: "ALLEN PARK"})
Man I hope I documented this well enough for someone to help, because I have been pulling my hair out for hours before turning to stack :)
I think the imports are not loaded yet when you access the obj var in index.html, thus obj is undefined at this time. Since Chrome supports HTML imports natively, the imports are loaded earlier and it works there. Wrap the access to obj in HTMLImports.whenReady(callback). The callback will be called when all HTML imports have finished loading.
HTMLImports.whenReady(function(){
<!-- TMPL_LOOP Cities -->
obj.city.push({val: "<!-- TMPL_VAR city_name -->", string: "<!-- TMPL_VAR city_name -->"})
<!-- /TMPL_LOOP -->
});
I've wrote a helper for the 3 conditions of users being logged in. I've verified that the CurrentUsers collection is being populated on user login with console.log on client.js and browser console. I'm not sure if I'm going about this wrong or if its a little error. There are no error messages in server console or browser console but nothing shows up for the condition of 0 users being logged in.
JS:
CurrentUsers = new Meteor.Collection("currentUsers")
if (Meteor.isClient) {
Template.lobby.nousers = function() {
return CurrentUsers.find().count() === 0;
}
Template.lobby.oneuser = function(){
return CurrentUsers.find().count() === 1;
}
Template.lobby.twousers = function(){
return CurrentUsers.find().count() === 2;
}
}
if (Meteor.isServer) {
Meteor._onLogin = function (userId){
if(CurrentUsers.find({user: userId}).count()===0){
CurrentUsers.insert({user: userId})
}
}
Meteor._onLogout = function (userId){
CurrentUsers.remove({user: userId})
}
}
HTML:
<head>
<title>bubblepopper</title>
</head>
<body>
{{loginButtons align = "right"}}
</body>
<template name = "lobby">
{{#if nousers}}
<div class = "nouser">
Hello please sign in to enter the game lobby.
</div>
{{/if}}
</template>
You are missing {{> lobby}} in your body.
<body>
{{loginButtons align = "right"}}
{{> lobby}}
</body>
Also, as far as I'm aware Meteor doesn't offer login/logout hooks, so Meteor._onLogin and Meteor._onLogout won't work out of the box: https://github.com/meteor/meteor/issues/1074
This event-hooks package might be interesting for you.
I created a little example for myself to test some stuff with Meteor. But right now it looks like I can't subscribe to a collection, I published on the server side. I hope somebody can tell me where the bug is.
server/model.js
Test = new Meteor.Collection("test");
if (Test.find().count() < 1) {
Test.insert({id: 1,
name: "test1"});
Test.insert({id: 2,
name: "test2"});
}
Meteor.publish('test', function () {
return Test.find();
});
client/test.js
Meteor.subscribe("test");
Test = new Meteor.Collection("test");
Template.hello.test = function () {
console.log(Test.find().count());//returns 0
return Test.findOne();
}
Template.hello.events = {
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
};
client/test.html
<head>
<title>test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{#with test}}
ID: {{id}} Name: {{name}}
{{/with}}
<input type="button" value="Click" />
</template>
EDIT 1
I want to change the object test, findOne() returns. Let's say for adding an attribute avg which contains the average value of two numbers (test.number1 and test.number2). In my opinion this should look like the following code. But javascript is not synchronous, so this won't work.
Template.hello.test = function () {
var test = Test.findOne();
test.avg = (test.number1 + test.number2) / 2;
return test;
}
EDIT 2
This code worked for me. Now I have to rethink why this solution with 'if (test)' just works with findOne() without a selector in my original project.
Template.hello.test = function () {
var avg = 0, total = 0, cursor = Test.find(), count = cursor.count();
cursor.forEach(function(e)
{
total += e.number;
});
avg = total / count;
var test = Test.findOne({id: 1});
if (test) {
test.avg = avg;
}
return test;
}
The latency the client db uses to replicate data might cause the situation wherein the cursor reckons no results. This especially occurs when the template is immediately rendered as the app loads.
One workaround is to observe query documents as they enter the result set. Hence, something like the following for example happens to work pretty well:
Meteor.subscribe("Coll");
var cursor = Coll.find();
cursor.observe({
"added": function (doc) {
... something...
}
})
Try to surround {{#with test}}...{{/with}} with {{#if}}...{{/if}} statement (because in first data push test does not have id and name fields):
<head>
<title>test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{#if test}}
{{#with test}}
ID: {{id}} Name: {{name}}
{{/with}}
{{/if}}
<input type="button" value="Click" />
</template>
As a result:
UPDATE:
This code performs calculation of average of field number in all records:
model.js:
Test = new Meteor.Collection("test");
Test.remove({});
if (Test.find().count() < 1)
{
Test.insert({id: 1,
name: "test1",
number: 13});
Test.insert({id: 2,
name: "test2",
number: 75});
}
test.js
Test = new Meteor.Collection("test");
Template.hello.test = function () {
var avg = 0, total = 0, cursor = Test.find(), count = cursor.count();
cursor.forEach(function(e)
{
total += e.number;
});
avg = total / count;
return { "obj": Test.findOne(), "avg": avg };
}
UPDATE 2:
This code snippet works for me:
var test = Test.findOne();
if (test)
{
test.rnd = Math.random();
}
return test;
Maybe you should try to wrap assignment code into if statement too?