Polymer 1: How can I set up paper-checkbox label dynamically using a custom element - polymer-1.0

I want to set label/s of paper-checkbox elements through a custom element I have created.
This is how I am calling my custom element with the value set to a property called optionLabel which I want to display when checkbox renders on the screen.
<check-list optionLabel="My first checkbox"></check-list>
My custom element check-list looks like this:
<dom-module id="check-list">
<template>
<style>
:host {
display: block;
}
</style>
<paper-checkbox on-change="_checkChanged">{{optionLabel}}</paper-checkbox>
</template>
<script>
(function () {
'use strict';
Polymer({
is: 'check-list',
properties: {
optionLabel: {
type: String,
notify: true
}
},
_checkChanged: function (e) {
alert("State changed");
}
});
})();
</script>
</dom-module>
My goal is to reuse my custom element inside a dom-repeat layout and set values according to the requirement.
What is the correct way of doing this?

According to the documentation camelCase properties are "accessed" from outside the element like camel-case. The documentation states the following:
Attribute names with dashes are converted to camelCase property names
by capitalizing the character following each dash, then removing the
dashes. For example, the attribute first-name maps to firstName. The same mappings happen in reverse when converting property names to attribute names.
In other words, your code should have worked if you did the following instead:
<check-list option-label="My first checkbox"></check-list>

I got it to work! The variable (property) I was using previously was optionLabel, which did not work. Don't know what is the reason but when I changed it to optionlabel, i.e. all lowercase, it worked fine!
Not sure if above is the true solution to the problem I faced but it is working for me now :)
However, it will still be very helpful for many beginners like me if somebody please explain why optionLabel did not work.
So my code now changes to this
Custom element:
<dom-module id="check-list">
<template>
<style>
:host {
display: block;
}
</style>
<paper-checkbox on-change="_checkChanged">{{optionlabel}}</paper-checkbox>
</template>
<script>
(function () {
'use strict';
Polymer({
is: 'check-list',
properties: {
optionlabel: {
type: String,
notify: true
}
},
_checkChanged: function (e) {
if (e.target.checked) {
this.optionlabel = "Good to see you agree";
this.$.btnsubmit.disabled = false;
} else {
this.optionlabel = "Please agree to proceed";
this.$.btnsubmit.disabled = true;
}
}
});
})();
</script>
</dom-module>
And the call looks like:
<check-list optionlabel="My first checkbox"></check-list>

Related

Polymer app-route return empty data attributes

I'm learning Polymer and i got some issues with app-route and app-location. I tried a simple example like this:
<dom-module id="test-component">
<template>
<style scoped>
</style>
<app-location route="{{route}}" use-hash-as-path></app-location>
<app-route route="{{route}}" pattern="/test/:advisor_id/:user_id"
data="{{data}}" tail="{{subroute}}">
</app-route>
route : [[route.path]]<br>
data.advisor_id : [[data.advisor_id]]<br>
data.user_id : [[data.user_id]]
</template>
<script>
Polymer({
is: "test-component",
properties: {
route: String,
data: Object
},
ready: function() {
console.log(this.route);
}
});
</script>
</dom-module>
The main page only loads components and have test-component tags on the body
With the url localhost/test/advisor_id/14152, i see the component but data.advisor_id and data.user_id are empty. I tested route.path and it is empty too.
It seems like i forgot something but don't understand what it is.
Thanks in advance for your time
remove the 'use-hash-as-path' from your app-location, since it expects a 'http://..../#!/path' syntax for location

Polymer 1.0: Refresh/Re-Render Computed Value Without Property Binding

I'm trying to re-compute and render a value within a Polymer 1.0 template. However, I'm trying to do this without binding to any properties.
In case it matters, the use case is for a translation mechanism that uses a string key to find the translated value. When the 'translations' value changes, the translate() call needs to be re-computed.
The component definition is as follows :
<dom-module id="my-component">
<template>
<style></style>
<p><span>[[translate("SOME_STRING")]]</span></p>
</template>
<script>
var MyComponent = Polymer({
is: "my-component",
properties: {
translations: {
type: Object,
notify: true,
value: {
"SOME_STRING": "Some String"
}
}
},
translate: function (key) {
if (this.translations.hasOwnProperty(key)) {
return this.translations[key];
}
}
});
</script>
</dom-module>
I can get the refresh to work by adding the translations property to the translate() call as follows :
<p><span>[[translate("SOME_STRING", translations)]]</span></p>
However, what I would like to do is re-compute/refresh without having to put the translations property as a second parameter in every call (there's other reasons too).
Basically, when the translations object updates with different locale translations, I'd like the translate("SOME_STRING") to be re-computed.
Is this possible? Is there any way to re-render the template or even just re-render the entire component manually? How? If not, what is the simplest way to get the computed value or template re-rendered without a property in the binding?
How about that????
<dom-module id="my-component">
<template>
<style></style>
<p><span>[[str]]</span></p>
</template>
<script>
var MyComponent = Polymer({
is: "my-component",
properties: {
translations: {
type: Object,
notify: true,
value: {
"SOME_STRING": "Some String"
},
observer: '_Changed'
},
str: {
type: String,
value: "hello"
}
},
_Changed: function(){
this.set("str",this.translate(this.str));
},
translate: function (key) {
if (this.translations.hasOwnProperty(key)) {
return this.translations[key];
}
}
});
</script>

Passing down Polymer's HTML attributes to nested components

I have a custom element <x-marker> with a <paper-checkbox> inside of it.
I want an checked attribute on my element to reflect down on the <paper-checkbox> but my solution doesn't seem to be the way to do it.
HTML:
<x-marker is-checked="<%= someBoolean %>"></x-marker>
Element:
<dom-module id="x-marker">
<template>
<div>
<paper-checkbox id="checkbox"></paper-checkbox>
</div>
</template>
<script>
Polymer({
is: 'x-marker',
properties: {
isChecked: String
},
listeners: {
change: 'changeHandler'
},
attached: function() {
if (this.isChecked === 'true') {
this.$.checkbox.setAttribute('checked', this.isChecked);
}
},
changeHandler: function (event, detail, sender) {
//...
}
});
</script>
</dom-module>
In Polymer 0.5 you could use checked?="{{isChecked}}", but that doesn't seem to work in 1.0 anymore. Also hard coding <paper-checkbox checked="false"> still checks the checkbox as long as the attribute is present. The value doesn't seem to matter, that's why the attribute itself has to be bound, and not its value.
I can't seem to figure this out, including treating the property as a string === 'true' instead of a boolean, and bind it straight to the <paper-checkbox>
In 1.0 you just need to use checked="{{isChecked}}". You can implement it as follows:
HTML:
<x-marker checked="{{someBoolean}}"></x-marker>
ELEMENT:
<dom-module id="x-marker">
<template>
<div>
<paper-checkbox id="checkbox" checked="{{checked}}">Check me</paper-checkbox>
</div>
</template>
<script>
Polymer({
is: 'x-marker',
properties: {
checked: {
type: Boolean,
observer: '_checkChanged'
}
},
_checkChanged: function(newValue, oldValue) {
// Do something
}
});
</script>
</dom-module>

AngularJS change attribute model from inside directive

I have setup a directive as below, which is to control the opening and closing of a Flyout panel, I want the state(whether its open/closed) of the flyout to be publicly accessible by the parent scope, I see some use a service for this but it seems verbose use a service, my questions is I'm wondering is there an elegant way to set the variable attached to the attribute on the Close Event? or do I have to access the parent scope? Fiddle Here http://jsfiddle.net/sjmcpherso/EbDRR/
<div class="page">
<button ng-click="openFlyout()">Bhttp://jsfiddle.net/sjmcpherso/EbDRR/#baseutton {{fly.flyoutOpen}}</button>
<flyout foopen={{fly.flyoutOpen}}>
<button ng-click="close()">X</button>
</flyout>
</div>
angular.module('myApp', [])
.controller('MyController', function($scope) {
$scope.fly = {flyoutOpen:false};
$scope.openFlyout = function(){
$scope.fly.flyoutOpen = !$scope.fly.flyoutOpen;
}
}).directive('flyout', function() {
return {
restrict: 'AE',
link: function(scope, el, attr) {
close.bind('click', function() {
//Set fly.flyoutOpen via attr.foopen to false
});
attr.$observe('foopen', function(value) {
console.log(typeof value);
if (value == "true") {
el.css('right', '0');
console.log("left:"+value);
} else {
el.css('right', '-150px');
console.log("right:"+value);
}
});
}
};
});
In my opinion, it is not angular-way to change parent scope in directives.
Although I also have faced the situations that I could not find other solutions, I think we should avoid this way as much as possible.
In your situation, I believe you could avoid to change the parent scope in your directive.
Therefore what I suggest you is ...
Remove close.bind('click'... in your directive.
Add close function of the controller in which you could change the scope value.(as you know this way would not break the angular-principles at all.)
jsfiddle is here.
I hope this would help you.
It is simple.
You can can use directive scope that can use for two way binding.
<http://jsfiddle.net/EbDRR/14/>
I have added "openclose" attribute in your code.
Have a look at these two examples; sorry they're both one-pagers...
I'm trying to keep the code self-contained, so that you can see the all of the pieces in one place. Normally, there'd be build-processes keeping code and templates and the page separate; not so much, here.
The good news is that these pages should work without so much as a server (as the only external file is angular), so you can save each one as an .html page, and open it from the drive.
Shared Config Between Parent and Child
<!doctype html>
<html ng-app="myApp" ng-controller="MyController">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<style>
page-flyout { display: block; }
page-flyout.expanded { background-color : grey; }
</style>
</head>
<body>
<main ng-controller="page-controller as page">
<button ng-click="page.toggle()"
>{{ page.flyout.expanded ? "Hide":"Show" }} Flyout</button>
<page-flyout
view="page.flyout"
class="ng-class: {'expanded':page.flyout.expanded,'contracted':!page.flyout.expanded }"
></page-flyout>
</main>
<script type="text/ng-template" id="components/page-flyout/page-flyout.html">
<h4>I am {{ view.expanded ? "OPEN" : "CLOSED" }}</h4>
<button ng-click="flyout.close()" ng-hide="!view.expanded">×</button>
</script>
<script >
var app = initApp();
initAngular(app);
function initAngular (MyApp) {
angular.module("myApp", ["pageFlyout"]);
angular.module("pageFlyout", []);
angular.module("myApp")
.controller("MyController", [MyApp.MyController])
.controller("page-controller", [MyApp.PageController]);
angular.module("pageFlyout")
.controller("page-flyout-controller", ["$scope", MyApp.PageFlyoutController])
.directive("pageFlyout", [function () {
return {
scope: { view: "=" },
restrict: "AE",
replace: false,
controller: "page-flyout-controller",
controllerAs: "flyout",
templateUrl: "components/page-flyout/page-flyout.html"
};
}]);
};
function initApp () {
var MyApp = {
MyController: function MyController () {
},
PageController: function PageController () {
var page = extend(this, { toggle: toggle, flyout: { expanded: false } });
function toggle () {
var currentState = page.flyout.expanded;
page.flyout.expanded = !currentState;
}
},
PageFlyoutController: function PageFlyoutController ($scope) {
var flyout = extend(this, { close: close });
function close () { $scope.view.expanded = false; }
}
};
function extend () {
var args = [].slice.call(arguments);
return angular.extend.apply(angular, args);
}
return MyApp;
}
</script>
</body>
</html>
I'm using a "PageController" as an outer controller; this outer element has a toggle method, and a flyout object.
The flyout object has an expanded property, and nothing more.
This is a config / state object that will be shared between the parent and child. The parent won't know anything about the child, the child won't know anything about the parent...
But both need to know about the structure of this one object; basically, the parent has to agree to give the child a config object which meets the child's needs, if it's going to use the child's services.
In my child scope, I'm referring to this object as the view, which is provided in my directive declaration <page-flyout view="page.flyout">.
The flyout has a controller, with a close method, which sets view.expanded = false;
Because the parent and the child both share this config object, and because a UI event triggers a check for digest, this works and everybody's happy...
Try it out, and see what happens: the flyout html is in a completely different universe, and yet the button still does what it should, and all of the text inside and outside of the flyout's scope keeps itself up to date...
...except that this isn't quite as clean as could be.
Sharing some sort of readable state would be one thing, but two people directly writing to it, and reading from it, might be a bit much.
Parent Controls State, Child Defines Event, Parent Implements Event-Handler
<!doctype html>
<html ng-app="myApp" ng-controller="MyController">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<style>
page-flyout { display: block; }
page-flyout.expanded { background-color : grey; }
</style>
</head>
<body>
<main ng-controller="page-controller as page">
<button ng-click="page.toggle()"
>{{ page.flyout.expanded ? "Hide":"Show" }} Flyout</button>
<page-flyout
onclose="page.close()"
class="ng-class: {'expanded':page.flyout.expanded,'contracted':!page.flyout.expanded }"
></page-flyout>
</main>
<script type="text/ng-template" id="components/page-flyout/page-flyout.html">
<h4>I am {{ view.expanded ? "OPEN" : "CLOSED" }}</h4>
<button ng-click="flyout.close()">×</button>
</script>
<script >
var app = initApp();
initAngular(app);
function initAngular (MyApp) {
angular.module("myApp", ["pageFlyout"]);
angular.module("pageFlyout", []);
angular.module("myApp")
.controller("MyController", [MyApp.MyController])
.controller("page-controller", [MyApp.PageController]);
angular.module("pageFlyout")
.controller("page-flyout-controller", ["$scope", MyApp.PageFlyoutController])
.directive("pageFlyout", [function () {
return {
scope: { onclose: "&" },
restrict: "AE",
replace: false,
controller: "page-flyout-controller",
controllerAs: "flyout",
templateUrl: "components/page-flyout/page-flyout.html"
};
}]);
};
function initApp () {
var MyApp = {
MyController: function MyController () {
},
PageController: function PageController () {
var page = extend(this, {
toggle: toggle,
close: close,
flyout: { expanded: false }
});
function toggle () {
var currentState = page.flyout.expanded;
page.flyout.expanded = !currentState;
}
function close () {
page.flyout.expanded = false;
}
},
PageFlyoutController: function PageFlyoutController ($scope) {
var flyout = extend(this, { close: close });
function close () { $scope.onclose(); }
}
};
function extend () {
var args = [].slice.call(arguments);
return angular.extend.apply(angular, args);
}
return MyApp;
}
</script>
</body>
</html>
I'll save you some hunting; almost every single line here is 100% the same.
The important ones are in the directive:
return {
scope: { onclose: "&" }
}
...the ampersand means that the property $scope.onclose is a method, which is defined on declaration of the directive.
Sure enough, when you look at the declaration of page-flyout, you'll notice <page-flyout onclose="page.close()">
Now $scope.onclose doesn't equal page.close(); so much as it equals function () { page.close(); }.
It could, instead be a simple expression, inside of the scope of the parent:
<page-flyout onclose="page.flyout.expanded = false">
$scope.onclose = function () { page.flyout.expanded = false; };
You can even pass named params back into your parent expression/method (unnecessary here; read up on it in tutorials, but basically you call $scope.method( paramsObj ) where the keys of the object are the same as the argument names in the implementation. Really handy stuff.
So in my case, my flyout button calls flyout.close(), a method on my controller. My controller method calls $scope.onclose(); (a separation between my viewmodel stuff, and the angular-specific glue that ties everything together), $scope.onclose then calls page.close() in the parent, which sets page.flyout.expanded = false;.
Nice. Neat. Clean.
The child knows absolutely nothing about the parent, and yet, I created an event that the parent could subscribe to; in HTML, no less.
One big problem, though.
If you load this page up, you'll notice that the flyout's text never changes, and the close button for the flyout never hides itself, based on whether the flyout is "open" or "closed".
The reason for that is dirt-simple.
The child knows absolutely nothing about the parent or the parent's data.
That's fine, but in this case, that means despite being able to close itself just fine, it can never know if it's open in the first place (if I used ng-hide in the parent scope, that would solve the problem, if you didn't want to leave it half-open).
The solution, of course, is to give the child a shared object, representing the state of a flyout...
We've been there before...
...except that this time, it's a read-only object (no guarantees, just by practice), and you're using events to talk up, and using properties to talk down.
The real solution involves doing both: having both the read-only properties for the child to use in its view, and to create events that the parent can hook into, to update its model (and what it gives back to the child on the next $digest).
Then aside from the contract the child defines and the parent keeps, neither one knows anything about the other.
I assume what you want to achieve is as follows.
encapsulate the features inside directive.
don't share the scope values with parent controller.
don't use services.
My idea is something like this.
Prepare the function which is the wrapper for updating the element of your flyout panel.(This function is completely encapsulated only in the directive.)
Instanciate the wrapper and use it in your directive code independently.
After instanciating the wrapper, the directive send it via oninit function to a parent controller.
After recieveing the wrapper, the controller could use it without sharing the scope.
jsfiddle is here.

How to manually fire validation of a textbox with angularJS

I am using angularJs (and I am new with it) to validate some fields on a form. This is the particular input that I am having problem with.
<input type="text" ng-required="!isWPPOnly" name="grades"
ng-class="{error:frmTestingDates.grades.$invalid}"
ng-model="date.grades" style="display:;"/>
If I press some keys like 1,2,3 the validation fires. However if I set the value of that input control using javascript in the controller like
$("[name=grades]").val('1');
angularJs does not know that the value of the control has changed. I guess that it listens to keydown or keyup event.
Anyway, how can I fire that validation manually from the controller after I set the value?
This is the code that gets fired in the controller when I click on a button
$scope.togglegrade = function (obj) {
var checkedGrades = [];
$("[name=grades]").val('');
$("input:checkbox[name=chkGrades]:checked").each(function()
{
checkedGrades.push($(this).val());
$("[name=grades]").val(checkedGrades);
});
};
I tried this but it does not work
$scope.togglegrade = function (obj) {
$scope.apply(function () {
alert(1);
var checkedGrades = [];
$("[name=grades]").val('');
$("input:checkbox[name=chkGrades]:checked").each(function()
{
checkedGrades.push($(this).val());
$("[name=grades]").val(checkedGrades);
});
});
};
You can see the
complete html payment.html here http://pastebin.com/9wesxaVd
complete controler payment.js here http://pastebin.com/1FWJKYyy
Notice I have commented out the ng-required="!isWPPOnly" line 454 in payment.html for a while until I find out how to fix the issue. One other thing I need to mention is that this application has no definition of date.grades in it's controller nor in any other place in the entire application. It has to be generated automatically somehow
first of all, you should NEVER use jQuery and any kind of DOM manipulation in your view. The only place you could do that are directives (and some specific kinds of services, very, very rare). Think declarative instead of imperative.
Also, forget binding via events, just change the model value, for instance:
$scope.$apply(function(){
$scope.date.grades = 'foo bar';
})
I don't have access to any text editor or IDE atm, so please let me know if there are any typos.
EDIT
I've created a simple example with good practices regarding to ng-model and data binding in AngularJS in general. Code is based the source, you've provided.
View Plunkr
http://embed.plnkr.co/bOYsO4/preview
Source:
View
<!DOCTYPE html>
<html ng-app="app">
<!-- ... -->
<body ng-controller="test">
<!--Note that the form creates its own controller instance-->
<form name="testForm" class="form form-horizontal">
<label><input type="checkbox" ng-model="model.isWPPOnly"> isWPPOnly</label>
<!-- element name CANNOT contain a dot, since it's used as a form controller property -->
<input type="text" ng-required="!model.isWPPOnly" name="date_grades" ng-model="date.grades" />
<h3>Result</h3>
<pre>{{date | json}}</pre>
<pre>{{model | json}}</pre>
</form>
</body>
</html>
Controller
angular.module('app', []).controller('test', function($scope){
// Create object to store bindings via references
// In javascript simple types (Number, String, Boolean) are passed via value
// and complex ones ([], {}) via reference (1)
$scope.model = {}
$scope.model.isWPPOnly = true;
$scope.date = {};
$scope.date.grades = 'foo';
// (1) I know, I've oversimplified it a little bit, since everything's and object in JS.
})
CSS
/* Note that there's no need to create an .error class, since angular provides you with default ones */
.error, .ng-invalid{
border: 1px solid red;
box-shadow: 0 0 10px red;
}
form{
padding: 10px;
}
$scope.$apply(function () {
$('[name=grades]').val('1');
});

Resources