Polymer 1.0 paper-tabs addEventListener stops binding tabs dynamically - polymer-1.0

I am trying very basic tab switching and tab content. Here is my code:
A Custom element I built to generate Tab Strips dynamically
<dom-module id="tab-generator">
<template>
<paper-tabs selected="{{defaultindex}}" id="tabstrip">
<template is="dom-repeat" items="{{tabitems}}">
<paper-tab>{{item.name}}</paper-tab>
</template>
</paper-tabs>
</template>
<script>
(function () {
'use strict';
var pages = document.querySelector("#plantContent");
var tabs = this.$.tabstrip;
tabs.addEventListener('iron-select', function () {
pages.selected = tabs.selected;
});
Polymer({
is: 'tab-generator',
properties: {
tabitems: {
type: Array,
notify: true
},
defaultindex: {
type: String,
notify: true
}
}
});
})();
</script>
</dom-module>
From index.html I am referencing this as follows:
<paper-card heading="Dynamic Tabs">
<div class="card-content">
<tab-generator selected="0" tabitems='[{"name": "Plant Genus"}, {"name": "Plant Series"}, {"name": "Plant Species"}]'></tab-generator>
<!-- iron pages to associate tabs as contents -->
<iron-pages selected="0" id="plantContent">
<div>Content - Tab 1</div>
<div>Content - Tab 2</div>
<div>Content - Tab 3</div>
</iron-pages>
</div>
</paper-card>
Tabs bind fine before I added the following snippet in my custom element:
<script>
var pages = document.querySelector("#plantContent");
var tabs = document.querySelector("paper-tabs");
tabs.addEventListener('iron-select', function () {
pages.selected = tabs.selected;
});
</script>
But when I remove the event listener snippet i.e.
tabs.addEventListener('iron-select', function () {
pages.selected = tabs.selected;
});
tabs are back again.
I am referring to this post. What is wrong in my code?
Thanks in advance.

Hmm, I would re-write your code to the following:
tab-generator.html:
<dom-module id="tab-generator">
<template>
<paper-tabs selected="{{selectedTab}}">
<template is="dom-repeat" items="{{tabItems}}">
<paper-tab>{{item.name}}</paper-tab>
</template>
</paper-tabs>
</template>
<script>
Polymer({
is: 'tab-generator',
properties: {
tabItems: {
type: Array,
notify: true
},
selectedTab: {
type: String,
notify: true
}
}
});
</script>
</dom-module>
index.html:
<paper-card heading="Dynamic Tabs">
<div class="card-content">
<tab-generator selected-tab="{{selectedTab}}" tab-items='[{"name": "Plant Genus"}, {"name": "Plant Series"}, {"name": "Plant Species"}]'></tab-generator>
<!-- iron pages to associate tabs as contents -->
<iron-pages selected="{{selectedTab}}" id="plantContent">
<div>Content - Tab 1</div>
<div>Content - Tab 2</div>
<div>Content - Tab 3</div>
</iron-pages>
</div>
</paper-card>
You should in your index.html also have property named selectedTab. What happens now is that, when you change selectedTab inside the tab-generator.html it is propogated to the property selectedTab inside index.html and will update the UI accordingly. No javascript is needed. :)

Related

Angular JS "<a href='tel:{num} '> call me</a> hyperlink does not work

While trying to render the hyper link using Angular JS it does not generate the hyper link. All other tags like <p> or <h2> work fine, but href fails.
var tempString = "<a href='tel:{mob_number}'>call me support</a>"
actual output - string is displayed but hyperlinked is not rendered. It's not clickable. Inspect page display tag generated as
<a> call me support </a>
Expected output - should display string with hyperlink.
I try to do this by 3 mood "Html, directive, bind-html"
Directive not work stackoverflow, try it on your local
var app = angular.module("app", []);
app.controller("ctrl", [
"$scope", "$sce",
function($scope, sce) {
$scope.mob_number = "123";
var tempString = "call me support";
$scope.asHtmlTemplate = sce.trustAsHtml(tempString);
}
]);
app.directive("mobile", function() {
return {
templateUrl: "mobile.html",
scope: {
content: "#",
mobNumber: "="
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<h4>simple html</h4>
<a ng-href="tel:{{mob_number}}">call me support</a>
<h4>as directive [directive not display in stackoverflow]</h4>
<mobile mob_number="mob_number" content="call me support"></mobile>
<!-- directive not display in stackoverflow ? -->
<h4>as Html template from controller</h4>
<div ng-bind-html="asHtmlTemplate"></div>
<script type="text/ng-template" id="mobile.html">
<a ng-href='tel:{{mobNumber}}'>{{content}}</a>
</script>
</div>

Polymer 1.0 : split index.html

I want to split my index.html file as it's too long(more than 500 lines) due to many submenus like the one below.
<paper-submenu>
<paper-item class="menu-trigger">
<iron-icon icon="add-circle-outline"></iron-icon>
<span>Medical</span>
</paper-item>
<paper-menu class="menu-content">
<a data-route="medical" href="/medical">
<paper-item>
<span>Introduction</span>
</paper-item>
</a>
Is it possible to store all the submenu lines in the index.html into another file?
Thank you in advance.
<link rel="import" href="../../bower_components/polymer/polymer.html">
<!-- Make sure to add other dependencies here -->
<dom-module id="test-comp">
<template>
<paper-submenu>
<paper-item class="menu-trigger">
<iron-icon icon="add-circle-outline"></iron-icon>
<span>{{title}}</span>
</paper-item>
<paper-menu class="menu-content">
<a data-route="{{dataRoute}}" href="{{href}}">
<paper-item>
<span>{{item}}</span>
</paper-item>
</a></paper-menu>
</paper-submenu>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'test-comp',
properties: {
title: {},
dataRoute: {},
href: {},
item: {},
},
});
})();
</script>
</dom-module>
Now insert the element like so
<test-comp title="Medical" data-route="medical" href="/medical" item="Introduction"></test-comp>
Edit: The styles that affect your index.html will not affect your custom component. Add styles INSIDE your custom component to have them included

Access child scope corresponding to a model variable

I have a list of items and I've rendered them in a template via ng-repeat. Each of them is controlled by an itemController which exposes some behavior for item (e.g. grabing focus). Here is the HTML:
<body ng-controller="mainController">
<button ng-click="addItem()">add</button>
<ul>
<li ng-repeat="item in items" ng-controller="itemController">
<div ng-if="item.isEditing">
<input ng-model="item.name"/>
<button ng-click="item.isEditing=false">done</button>
</div>
<span ng-if="!item.isEditing">{{item.name}}</span>
</li>
</ul>
</body>
In the mainController, I have a function for adding a new item to items. Here is the code for mainController:
app.controller('mainController', function($scope){
$scope.items = [
{
name: "alireza"
},
{
name: "ali"
}
];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
}
});
Whenever I add an item to items array, a corresponding li element is added into the view which is controlled by an instance of itemController, and the corresponding model is the new item I've just added (or maybe the scope of the itemController, which contains item).
Problem:
When I add some item to items, I only have access to item and not the scope of the recently created item. So I can't run some function (like grabFocus) on the scope of new item.
Is it something semantically wrong in my design? What is the canonical approach for this problem?
Plunker link
Here is the plunker link with related comments
You can use $broadcast from the parent scope, along with $on from the child scope, to notify the child scopes of the newly added item. And by passing (as an argument) the $id of the child scope that corresponds to the newly added item, each child catching the event can know whether or not it's the one that needs to have grabFocus() called.
Here's a fork of your Plunker that uses that approach. I wasn't sure what you were trying to accomplish with $element.find(":text").focus(); in your original Plunker, so I tweaked it to toggle a $scope property that in turn controlled a style in the view. The newly added item will be red (by calling its own grabFocus function to toggle the flag to true), and the others will be black (by calling their own loseFocus function to toggle the flag to false).
Modified HTML (just the repeated li):
<li ng-repeat="item in items" ng-controller="itemController">
<div ng-if="item.isEditing">
<input ng-model="item.name"/>
<button ng-click="item.isEditing=false;handleItemAdded($index);">done</button>
</div>
<span ng-if="!item.isEditing" ng-style="{ color: isFocused ? 'red' : 'black' }">{{item.name}}</span>
</li>
Full JavaScript:
var app = angular.module("app",[]);
app.controller('mainController', function($rootScope, $scope){
$scope.items = [ { name: "alireza" }, { name: "ali" } ];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
};
$scope.handleItemAdded = function (index) {
// $rootScope.$broadcast('item-added', { index: index });
for(var cs = $scope.$$childHead; cs; cs = cs.$$nextSibling) {
if (cs.$index === index) {
$rootScope.$broadcast('item-added', { id: cs.$id });
break;
}
}
};
});
app.controller('itemController', function($scope, $element){
$scope.$on('item-added', function (event, args) {
if ($scope.$id === args.id + 1) {
$scope.grabFocus();
} else {
$scope.loseFocus();
}
});
$scope.grabFocus = function() {
$scope.isFocused = true;
};
$scope.loseFocus = function() {
$scope.isFocused = false;
};
});
I changed your approach a little bit by creating an unique id for every input, based on its index number. See code below, hope it helps.
// Code goes here
var app = angular.module("app",[]);
app.controller('mainController', function($scope,$timeout){
$scope.items = [
{
name: "alireza"
},
{
name: "ali"
}
];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
$timeout(function(){
document.getElementById("newItem"+($scope.items.length-1)).focus();
},0)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.3.0" data-semver="1.3.0" src="//code.angularjs.org/1.3.0/angular.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body ng-controller="mainController">
<button ng-click="addItem()">add</button>
<ul>
<li ng-repeat="item in items">
<div ng-if="item.isEditing">
<input ng-model="item.name" id="newItem{{$index}}"/>
<button ng-click="item.isEditing=false">done</button>
</div>
<span ng-if="!item.isEditing">{{item.name}}</span>
</li>
</ul>
</body>
</html>

Parameter not passed to Handler in nested Angular Directive

I have "Pages" property which contains an array of "Page". Each "Page" have an array of "Control" exposed against "Controls" properties.
I have written Directive to render markup for Page and Control. Control template have a button which is supposed to pass control instance (or Id property of control.) The event is triggering but the scope/id is not coming through.
PROBLEM: I need to have either control instance or control.id value in "$scope.deleteControl" method.
Here is the code (just save it as HTML and it will run.)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AngularJS Tree demo</title>
</head>
<body ng-app="wizardApp">
<script type="text/ng-template" id="page.html">
<div class="tree-node">
<div class="tree-node-content">
<h4 class="clearfix pull-left">{{page.title}}</h4>
<div class="btn-group pull-right" >
<button class="glow left" title="Add new page" data-nodrag ng-click="addNewPage()">+ Add Page</button>
<button class="glow right" title="Delete page" data-nodrag ng-click="deletePage()">X</button>
</div>
</div>
</div>
<br />
<ol ui-tree-nodes="" ng-model="page.controls" ng-class="{hidden: collapsed}">
<li ng-repeat="control in page.controls" ui-tree-node>
<control-Ui control="control" on-delete-control="deleteControl('{{ control.id }}')"></control-Ui>
</li>
</ol>
</script>
<script type="text/ng-template" id="control.html">
Control markup over here... {{ control.id }} ~ {{ control.ctrltype }}
<button class="glow right" title="Delete control" ng-click="deleteControl()">Delete Ctrl</button>
</script>
<div class="container" ng-controller="wizardController">
<ol ui-tree-nodes="" ng-model="pages">
<li ng-repeat="page in pages" ui-tree-node>
<page-Control page="page" on-add-new-page="addNewPage(this)" on-delete-page="deletePage(this)" on-delete-control="deleteControl()"></page-Control>
</li>
</ol>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<script>
angular
.module('wizardApp', [])
.directive('pageControl', function () {
return {
restrict: 'E',
scope: { page: '=', 'deletePage': '&onDeletePage', 'addNewPage': '&onAddNewPage', 'deleteControl': '&onDeleteControl' },
//template: 'something ~ {{ page.title }} ~ {{ page.id }}'
templateUrl: 'page.html'
};
})
.directive('controlUi', function () {
return {
restrict: 'E',
scope: { control: '=', 'deleteControl': '&onDeleteControl' },
templateUrl: 'control.html'
};
})
.controller('wizardController', ['$scope', function ($scope) { // function referenced by the drop target
$scope.pages = [{
"id": 1,
"title": "Page 1",
"ctrltype": "page",
"controls": [
{ "id": 10, "ctrltype": 'textbox' },
{ "id": 20, "ctrltype": 'dropdown' }
]
}];
$scope.deletePage = function (pg) {
alert('Page deleted - WORKS FINE');
};
$scope.addNewPage = function (scope) {
alert('Page added - WORKS FINE');
};
$scope.deleteControl = function (ctrl) {
alert('Delete invoked - NOT WORKING FINE'); // Expecting to pass the Id of control i.e. 10
alert(ctrl.id + ' ~ ' + ctrl.ctrltype);
};
}]);
</script>
</body>
</html>
How you have defined your isolated scope html attribute expression and how the invocation is done with ng-click needs to be fixed.
The directive html should be
<control-Ui control="control" on-delete-control="deleteControl(controlId)"></control-Ui>
The invocation should be
ng-click="deleteControl({controlId:control.id})"
to make it work.
You can also pass the control directly if you do
ng-click="deleteControl({controlId:control})"
Update: For multi-level directives passing parameter was not as expected. I created a fiddle to show the approach http://jsfiddle.net/8XkHn/2/
Basically you need to keep passing the call level up.

How to create a closeable tab in angularjs UI-Bootstrap

I want to create closeable tabs (like chrome tab or firefox tab, which has a small "x" on every tab). How to configure the ready-made tab component in UI-Bootstrap to add this functionality?
Thanks.
you can use html & ng-click in your tab-heading, e.g.
<div ng-controller="mainCtrl">
<tabset>
<tab ng-repeat="t in tabs">
<tab-heading>{{t.title}} <a ng-click="removeTab($index)" href=''><i class="icon-remove"></i></a></tab-heading>
<div ng-bind-html-unsafe='t.content'></div>
</tab>
</tabset>
</div>
angular.module('myApp', ['ui.bootstrap']).controller("mainCtrl", function ($scope) {
$scope.tabs = [{
title: "one",
content: '<h1>tab one</h1>'
}, {
title: "two",
content: '<h1>tab two</h1>'
}, {
title: "three",
content: '<h1>tab three</h1>'
}];
$scope.removeTab = function (index) {
$scope.tabs.splice(index, 1);
};
});
JSFiddle: http://jsfiddle.net/alfrescian/ZE9cE/

Resources