Angular - append html through different frames with one module per frame - html

My code have the main windonw and one iframe and each one with your module. A button in main window fires click event that should append html into iframe, the new html when appended into that should apply interceptors and directives properly, but it doesn't work!
Angular javascript:
angular.module('module1',[]).controller('Controller1', function ($scope) {
$scope.get = function(){
$http.jsonp("some_url_here").success(function(html){
$scope.content = html;
});
}
}).directive('click', function($compile) {
return {
link: function link(scope, element, attrs) {
element.bind('click',function(){
var unbind = scope.$watch(scope.content, function() {
var div=document.getElementById("frame").contentWindow.angular.element("divId");
div.append($compile(scope.content)(div.scope()));
unbind();
});
});
}
}
});
angular.module('module2',[]).directive('a', function() {
return {
restrict:'E',
link: function(scope, element, attrs) {
console.log('ping!');
console.log(attrs.href);
}
};
});
Html code:
<html ng-app="modile1">
<div ng-controller="Controller1">
<button type="button", ng-click="get('any_value')", click:""/> Load frame
</div>
<iframe id="frame" src="/please/ignore/this">
<!-- considere the html as appended from iframe-src and contains ng-app="module2" -->
<html ng-app="module2">
<div id="divId">
<!-- code should be inject here -->
</div>
</html>
</iframe>
</html>
Please, considere that angularjs, jquery if applicable, modules-declaration as well as headers are loaded properly.
I'd like to load the html content from main-frame/window into iframe and run interceptors and directives properly. Is it possible? If yes, how can I do it?
Thanks for advancing!

I've tried this code and it seems work fine! I found it here: http://www.snip2code.com/Snippet/50430/Angular-Bootstrap
var $rootElement = angular.element(document.getElementById("frame").contentWindow.document);
var modules = [
'ng',
'module2',
function($provide) {
$provide.value('$rootElement', $rootElement)
}
];
var $injector = angular.injector(modules);
var $compile = $injector.get('$compile');
$rootElement.find("div#divId").append(scope.content);
var compositeLinkFn = $compile($rootElement);
var $rootScope = $injector.get('$rootScope');
compositeLinkFn($rootScope);
$rootScope.$apply();

Related

$compile not triggering ng-click in angularjs

I am generating dynamic html element from angularjs controller.I have used $compile
to make ng-click work inside html element.But still it is not calling the function.
Here is my js
var accountApp = angular.module('accountApp', ['ngRoute']);
accountApp.config(['$compileProvider',function($compileProvider )
.controller('myController',function($scope,$compile)
{
var searchHTML = '<li><a href="javascript:void(0)" data-ng-
click="setMarkerToCenterA()">'+item.title+'</a></li>';
$compile(searchHTML)($scope);
$scope.setMarkerToCenterA = function() {
alert("this alert is not calling");
}
});
}]);
I have injected the dependencies also.Can anyone tell why ng-click is not calling function even though i am using $compile?
First you need an element where this li element will be located.
<div ng-controller="myController">
<ul id="list"></ul>
</div>
Then in your controller:
var element = angular.element("#list");
element.html($compile(html)($scope));
$scope.setMarkerToCenterA = function() {
alert("Here");
};
Probably you missed to parse the HTML string using angular.element(htmlString) which is then compiled.
var app = angular.module('stackoverflow', []);
app.controller('MainCtrl', function($scope, $compile, $sce) {
var searchHTML = 'hello';
var template = angular.element(searchHTML);
$compile(template)($scope);
angular.element(document.querySelector('#container')).append(template);
$scope.setMarkerToCenterA = function() {
alert("this alert is now calling");
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.9/angular.min.js"></script>
<div ng-app="stackoverflow" ng-controller="MainCtrl">
<div id="container"></div>
</div>

What is the angular way for cloning buttons?

I have a follow button for a particular user that should change its text to followed after it's clicked and vice versa. This follow button can show up in different modules on the page. When it's clicked, the follow button for this particular users should update in all of these modules. However, the buttons are in different scopes. What is the angular way of making sure the cloned buttons are in the same state?
My current solution is to use an universal jQuery selector to update all the buttons on click.
You should store the state in a service.
example:
app.factory('SharedService', function() {
this.buttonState = null;
this.setButtonState= function(value) {
this.buttonState = value;
}
this.getButtonState= function() {
return this.buttonState ;
}
return this;
});
Read: AngularJS Docs on services
or check this Egghead.io video
You can use $rootScope.$broadcast to do this. when any of button gets clicked you fire an event using $rootScope.$broadcast and then listen to it using $scope.$on and toggle the status of buttons. and you can also update state inside the service too, so you can fetch current value later if needed.
See the below example:
var app = angular.module('app', []);
app.controller('ctrl1', function($scope) {
$scope.label1 = "First Button";
});
app.controller('ctrl2', function($scope) {
$scope.label2 = "Second Button";
});
app.controller('ctrl3', function($scope) {
$scope.label3 = "Third Button";
});
// updating state in service too.
app.service('fButtons', function($rootScope) {
var buttonState = false;
this.getCurrentState = function() {
return buttonState;
};
this.updateCurrentState = function() {
buttonState = !buttonState;
};
});
app.directive('followButton', function($rootScope, $timeout, fButtons) {
return {
restrict: 'E',
scope: {
label: '='
},
template: '<button ng-click="buttonClick()" ng-class="{red: active}">{{label}}</button>',
controller: function($scope) {
$scope.$on('button.toggled', function() {
$scope.active = !$scope.active;
});
$scope.buttonClick = function() {
fButtons.updateCurrentState();
$rootScope.$broadcast('button.toggled');
console.log(fButtons.getCurrentState());
}
}
};
});
.red {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl1">
<follow-button label="label1"></follow-button>
</div>
<hr/>
<div ng-controller="ctrl2">
<follow-button label="label2"></follow-button>
</div>
<hr/>
<div ng-controller="ctrl3">
<follow-button label="label3"></follow-button>
</div>
</div>
see console for service state.
$broadcast docs

Web Component: How to listen to Shadow DOM load event?

I want to execute a JavaScript code on load of the Shadow DOM in my custom element.
I tried the following code but it did not work
x-component.html:
<template id="myTemplate">
<div>I am custom element</div>
</template>
<script>
var doc = this.document._currentScript.ownerDocument;
var XComponent = document.registerElement('x-component', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
var root = this.createShadowRoot();
var template = doc.querySelector('#myTemplate');
var clone = document.importNode(template.content, true);
clone.addEventListener('load', function(e) {
alert('Shadow DOM loaded!');
});
root.appendChild(clone);
}
}
})
});
</script>
Then I use it in another html as follows -
index.html:
<!doctype html>
<html >
<head>
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="import" href="x-component.html">
</head>
<body>
<x-component></x-component>
</body>
</html>
The doc variable is used as I am using Polymer webcomponents.js polyfill and the polyfill needs it.
What is the right syntax to listen to load event of Shadow DOM?
AFAIK, the only way to achieve this is to use MutationObserver:
attachedCallback: {
value: function() {
var root = this.createShadowRoot();
var template = document.querySelector('#myTemplate');
var clone = document.importNode(template.content, true);
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes) { // this is definitely a subject to change
alert('Shadow is loaded');
};
});
})
observer.observe(root, { childList: true });
root.appendChild(clone);
}
}
I would be glad to know if there is more elegant way, but for now I use this one.
Live preview: http://plnkr.co/edit/YBh5i2iCOwqpgsUU6En8?p=preview

call a function after completely loaded into html with ng-repeate

how can I call a function after completely loaded my json data from the server into a ng-repeate?
Ex: I have json URL it have some products data which is in my car, once I clicked on view cart the cart items should be showed in the popup box. and if add another product it should me added in the popup box.
Please help me guys
HTML
<div data-ng-controller="MainController">
<ul>
<li ng-repeat="asset in assets" my-directive>{{asset}}</li>
</ul>
</div>
JS
var app = angular.module('myApp', []);
app.controller('MainController',function($scope) {
$scope.assets = [1,2,3,4,5,6,7,8,9,10];
});
app.value('myFunc',function(){
alert('Hello');
});
app.directive('myDirective', function( myFunc) {
return {
restrict: 'A',
link: function(scope, elem) {
if (scope.$last){
myFunc();
}
}
};
});
It should work also with a asyn service (to get data)

Adding an angularJS directive based on a parameter value

TL;DR: Is there a way to dynamically set a directive based on a parameter value? Something similar to ng-class for setting css elements, but a way to set the directive based on the value in the scope. I would have the value in the scope so I could call:
<div class="data.directiveType"></div>
When
data.directiveType = "my-directive"
the div would become
<div class="my-directive"></div>
and myDirective would be invoked?
Detailed Question:
What I am trying to do is allow the user to add elements to the web application and I wanted the directive for each element to be added based on what the user clicks.
I have the following Directives:
app.directive("mySuperman", function(){
//directive logic
});
app.directive("myBatman", function(){
//directive logic
});
app.directive("myWonderWoman", function(){
//directive logic
});
I have the following controller
app.controller("ParentCtrl", function($scope){
$scope.superHeros = [];
var superman = {directiveType: "my-superman"};
var batman = {directiveType: "my-batman"};
var wonderWoman = {directiveType: "my-wonder-woman"}
$scope.addBatman = function()
{
var batmanInstance = {}
angular.copy(batman, batmanInstance);
$scope.superHeros.push(batmanInstance);
}
$scope.addSuperman = function()
{
var supermanInstance = {}
angular.copy(superman, supermanInstance);
$scope.superHeros.push(supermanInstance);
}
$scope.addWonderWoman = function()
{
var wonderwomanInstance = {}
angular.copy(wonderWoman, wonderwomanInstance);
$scope.superHeros.push(wonderwomanInstance);
}
});
In the index.html I have
<body ng-controller="ParentCtrl>
<a ng-click="addBatman()">Add Batman</a>
<a ng-click="addSuperman()">Add Superman</a>
<a ng-click="addWonderWoman()">Add WonderWoman</a>
<div ng-repeat="hero in superHeros">
<!-- The following doesn't work, but it is the functionality I am trying to achieve -->
<div class={{hero.directiveType}}></div>
<div>
</body>
The other way I thought of doing this was just using ng-include in the ng-repeat and adding the template url to the hero object instead of the directive type, but I was hoping there was a cleaner way that I could make better use of the data binding and not have to call ng-include just to call another directive.
You can create a directive that takes the directive to add as a parameter, adds it to the element and compiles it. Then use it like this:
<div ng-repeat="hero in superHeros">
<div add-directive="hero.directiveType"></div>
</div>
Here is a basic example:
app.directive('addDirective', function($parse, $compile) {
return {
compile: function compile(tElement, tAttrs) {
var directiveGetter = $parse(tAttrs.addDirective);
return function postLink(scope, element) {
element.removeAttr('add-directive');
var directive = directiveGetter(scope);
element.attr(directive, '');
$compile(element)(scope);
};
}
};
});
Demo: http://plnkr.co/edit/N4WMe8IEg3LVxYkdjgAu?p=preview