AngularJS and Shadow DOM - angularjs

I'm developing a web component form.
I'm having troubles with angularjs bootsrap.
I'm creating dinamically a custom element with form.createdCallback <form> and <input, textarea etc> and adding ng-app="", ng-controller="" attributes.
Later I encapsule it with template.createShadowRoot()
I use document.addEventListener('DOMContentLoaded', function(){angular.bootstrap(document, ['mform'])}), but doing this not execute anything from my angular app...
So, I tried removing the line template.createShadowRoot() and angular execute everything pretty well... so I arrived to the conclusion that the problem is angular not compiling inside shadow dom.
There's any hack or way to do this?

I'm not sure if this really answers your question, but I am able to get Shadow DOM to work in Angular 1.x. Be aware that if you are after CSS encapsulation, then this will only work on browsers that natively support Shadow DOM since the Polyfill can not do the same thing.
I use $compile to create the contents of the Shadow DOM and bind that back into the directive.
Here is my code example:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>Angular 1.x ShadowDOM example</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var app = angular.module('app', []);
app.controller('myElController', ($scope) => {
});
var template = `
<style>
:host {
left: 50%;
position: fixed;
top: 50%;
transform: translate(-50%,-50%);
border: 1px solid black;
box-shadow: 4px 4px 4px rgba(0,0,0,.4);
display: inline-block;
padding: 6px 12px;
}
h3 {
border-bottom: 1px solid #999;
margin: 0 -12px 8px;
padding: 0 12px 8px;
}
p {
margin: 0;
}
</style>
<h3>{{title}}</h3>
<p>{{info}}</p>
`;
app.directive('myEl', ($compile) => {
return {
"restrict": "E",
"controller": "myElController",
"template": '',
"scope": {
"title": "#",
"info": "#"
},
"link": function($scope, $element) {
console.log('here');
$scope.shadowRoot = $element[0].attachShadow({mode:'open'});
$scope.shadowRoot.innerHTML = template;
$compile($scope.shadowRoot)($scope);
}
};
});
</script>
</head>
<body>
<div>
<my-el title="This is a title" info="This is the info of the element"></my-el>
</div>
<div class="shell">
<h3>Outside title (H3)</h3>
<p>Outside content (P)</p>
</div>
</body>
</html>

Related

Set selected item class to active in angularjs

I am working on an application where I have to set item class to active but Its not working. It's not in the ng-repeat list. Its 4 normal button that has to be active when clicked on it.
Try achieving this by ng-class as shown below.
Controller
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', function ($scope) {
$scope.isActive = false;
$scope.activeButton = function() {
$scope.isActive = !$scope.isActive;
}
});
CSS
$base: #F8E1C2;
$button: #3D3240;
$button-active: #FF735C;
$margin: 40px;
body {
background: $base;
-webkit-font-smoothing: antialiased;
padding: $margin;
text-align: center;
}
.button {
border: none;
padding: 14px;
color: #fff;
margin: 0 10px;
background: $button;
font-weight: 700;
border-radius: 4px;
}
.active {
background: $button-active;
}
.disabled {
background: #eee;
color: #aaa;
}
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
</head>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<button class="button" ng-class="{'active': isActive}" ng-click="activeButton()" type="button">Click Me</button>
</div>
</body>
</html>
remove single quotes on class name active, like from 'active' to active

How to create a ripple effect in a button in an angular way

I want to create a button with a ripple effect on clicked.Is it necessary to achieve it through a directive. How do i handle the javascript required to handle the click and propagate the ripple away from the point it was clicked.
I have done it using javascript and css but it is not the angular way.
<!doctype html>
<head>
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import"/>
<link href="paper-ripple/paper-ripple.html" rel="import"/>
</head>
<body>
<test-elem></test-elem>
<dom-module id="test-elem">
<template>
<style>
div {
width: 150px;
position: relative;
padding: 15px;
border: 1px solid black;
}
button { color: green; border: 1px solid green; }
</style>
<div>
<paper-ripple></paper-ripple>
<button id="btn">Button</button>
</div>
</template>
<script>
Polymer({ is: 'test-elem',
listeners: {'btn.down': 'onDown'},
onDown: function(e) {
e.stopPropagation();
}
});
</script>
</dom-module>
</body>

change value of <meter> dynamically from $scope variable

I am trying to use meter tag to show battery power indicator. It works with hard coded value but does not work with $scope variable.Here is the jsfiddle
HTML
<div ng-app="app" ng-controller="main">
<div id="page-wrapper">
<p>
<meter min="0" max="4" low="1" high="3" optimum="4" value="2"></meter>
</p>
<p>
<meter min="0" max="4" low="1" high="3" optimum="4" value="value"></meter>
</p>
</div>
</div>
**JS*
angular.module("app", [])
.controller("main", ['$scope', function($scope) {
$scope.value = 2
}])
You have to use {{value}} to reflect the scope value.
So use,
value="{{value}}"
instead of
in value="value"
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<style>
.styled meter {
/* Reset the default appearance */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 100%;
height: 30px;
/* For Firefox */
background: #EEE;
box-shadow: 0 2px 3px rgba(0,0,0,0.2) inset;
border-radius: 3px;
}
/* WebKit */
.styled meter::-webkit-meter-bar {
background: #EEE;
box-shadow: 0 2px 3px rgba(0,0,0,0.2) inset;
border-radius: 3px;
}
.styled meter::-webkit-meter-optimum-value,
.styled meter::-webkit-meter-suboptimum-value,
.styled meter::-webkit-meter-even-less-good-value {
border-radius: 3px;
}
.styled meter::-webkit-meter-optimum-value {
background: #86CC00;
}
.styled meter::-webkit-meter-suboptimum-value {
background: #FFDB1A;
}
.styled meter::-webkit-meter-even-less-good-value {
background: #CC4600;
}
/* Firefox */
.styled meter::-moz-meter-bar {
border-radius: 3px;
}
.styled meter:-moz-meter-optimum::-moz-meter-bar {
background: #86CC00;
}
.styled meter:-moz-meter-sub-optimum::-moz-meter-bar {
background: #FFDB1A;
}
.styled meter:-moz-meter-sub-sub-optimum::-moz-meter-bar {
background: #CC4600;
}
button {
display: inline-block;
border-radius: 3px;
border: none;
font-size: 0.9rem;
padding: 0.4rem 0.8em;
background: #69c773;
border-bottom: 1px solid #498b50;
color: white;
-webkit-font-smoothing: antialiased;
font-weight: bold;
margin: 0 0.25rem;
text-align: center;
}
button:hover, button:focus {
opacity: 0.75;
cursor: pointer;
}
button:active {
opacity: 1;
box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.1) inset;
}
</style>
<div ng-app="myApp" ng-controller="myCtrl">
<div id="page-wrapper">
<p>
<meter min="0" max="4" low="1" high="3" optimum="4" value="4"></meter>
</p>
<p>
<meter min="0" max="4" low="1" high="3" optimum="4" value="{{value}}"></meter>
</p>
</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.value = 2
});
</script>
</body>
</html>
PLEASE RUN THE ABOVE SNIPPET
Here is a working DEMO
Some observations as per your fiddle :
You forgot to include AngularJS library in your fiddle.
To get the $scope value into the HTML . Use angular expression {{value}}
Change LOAD TYPE to No wrap - in <head> from onLoad in JAVASCRIPT section of the fiddle.
DEMO

Angular App not showing the list from json local file

I have some dummy data in a mainMenu.json which will server to populate a list which will be the first template loaded by angular framework when app starts.
chrome console reporting no errors.
But I get a white "blank" page "right image" and not sure where I went wrong. Thanks
//mainMenu.json
[{
"name": "item1",
"image": "item1_"
}, {
"name": "item2",
"image": "item2_"
}, {
"name": "item3",
"image": "item3_"
}]
//--------------------------------------------------------------------
//app.js
var myApp = angular.module('myApp', [
'ngRoute', //turn on deep linking
'appControllers' //js to handle the route
]);
//Configure how the partials are going to run
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/list', {
templateUrl: 'partials/mainMenu.html',
controller: 'MainMenuCtrl'
}).otherwise({ //home page
redirectTo: '/list'
});
}]);
//--------------------------------------------------------------------
// controlers.js
var appControllers = angular.module ('appControllers', []);
appControllers.controller ('MainMenuCtrl', ['$scope', '$http', function ($scope, $http){
$http.get('js/mainMenu.json').success (function (data){
$scope.menuItems = data;
});
}]);
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
header > button {
height: 1.5em;
width: 1.5em;
font-size: 1.5em;
top: 0;
}
label.pageTitle {
display: inline-block;
width: calc(100% - 5em);
text-align: center;
}
header {
border-bottom: 1px solid black;
width: 100%;
position: fixed;
top: 0;
}
main {
margin-top: 40px;
margin-bottom: 40px;
padding: 0.5em;
}
footer {
position: fixed;
bottom: 0;
width: 100%;
}
footer > button {
font-size: 1em;
padding: 0.5em 1em;
}
input {
font-size: 1em;
width: 100%;
}
header, footer {
background-color: white;
}
footer > button {
padding: 0.5em 1em;
width: 31.33%;
margin: 0 1%;
float: left;
box-sizing: border-box;
}
<!--index.html-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="css/index.css">
<script src="angular.js"></script>
<script src="angular-route.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<header>
<button class="menuLeft" type="button" >☰</button>
<label class="pageTitle">Title of Page</label>
<button class="menuRight" type="button">⋮</button>
</header>
<main ng-view></main>
<footer>
<button class="menuLeft" type="button" >NO</button>
<button class="menuLeft" type="button" >EXTRA</button>
<button class="menuRight" type="button">YES</button>
</footer>
</body>
</html>
//--------------------------------------------------------------------
<!--mainMenu.html-->
<section>
<ul>
<li ng-repeat="item in menuItems">
<br/>
<p>dummy</p>
<h2>{{item.name}}</h2>
</li>
</ul>
</section>
You have an error in your code
var appControllers = angularModule ('appControllers', []);
It should be
var appControllers = angular.module ('appControllers', []);
To catch an error on $http.get promise do something like this :
$http.get(your_options).then(function(data){
}, function(err){
});
First function is your success callback and the second one is your error callback. More about $http can be found HERE

Angular 1.4 View Encapsulation and Shadow DOM - is it possible?

I am in the process of changing my app from using Polymer to Angular 1.4 for stability reasons. Because of my familiarity with Polymer and web components (and future use of Angular 2) I am choosing to architect my app using the Component Pattern.
I have searched and thus far only come across this question and the solution was a no longer maintained github repo. I want to know if it is possible to have view encapsulation with Angular 1.4. In case I am misstating, specifically, can my directives with templates have their own encapsulated styles like is done in Polymer and Angular 2 without relying on Grunt?
Seems like it is. I am using Angular 1.6 in my test, but I was able to get shadow DOM working inside an angular directive including bindings.
Here is my sample code:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>Angular 1.x ShadowDOM example</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var app = angular.module('app', []);
app.controller('myElController', ($scope) => {
});
var template = `
<style>
:host {
left: 50%;
position: fixed;
top: 50%;
transform: translate(-50%,-50%);
border: 1px solid black;
box-shadow: 4px 4px 4px rgba(0,0,0,.4);
display: inline-block;
padding: 6px 12px;
}
h3 {
border-bottom: 1px solid #999;
margin: 0 -12px 8px;
padding: 0 12px 8px;
}
p {
margin: 0;
}
</style>
<h3>{{title}}</h3>
<p>{{info}}</p>
`;
app.directive('myEl', ($compile) => {
return {
"restrict": "E",
"controller": "myElController",
"template": '',
"scope": {
"title": "#",
"info": "#"
},
"link": function($scope, $element) {
console.log('here');
$scope.shadowRoot = $element[0].attachShadow({mode:'open'});
$scope.shadowRoot.innerHTML = template;
$compile($scope.shadowRoot)($scope);
}
};
});
</script>
</head>
<body>
<div>
<my-el title="This is a title" info="This is the info of the element"></my-el>
</div>
<div class="shell">
<h3>Outside title (H3)</h3>
<p>Outside content (P)</p>
</div>
</body>
</html>
The trick is to not have a default template for the directive. Just us an empty string. Then, in the link function you would create the shadow root and set it's innerHTML to your real template. Then you need to run $compile on the shadow root to bind it back to the directive's $scope.
Be aware that this will only work on a browser that natively supports shadow DOM. Any Polyfill will not support real CSS encapsulation. For that it is better to use a form of BEM CSS: http://getbem.com/introduction/

Resources