Angular JS ng-click inside a modal window / Refresh angular scope in modal callback - angularjs

I'm loading a modal window into my html using magnific popup, which is essentially doing this:
<html ng-app='basket'>
<body ng-controller='BasketController as basket'>
<a id='open' href='/product/1'>Open modal</a>
<a ng-click='addToCart(1)'>Add to cart</a>
</body>
</html>
... After Ajax load
<html ng-app='basket'>
<body ng-controller='BasketController as basket'>
<div class='modal>
<h1>Product title</h1>
<a ng-click='addToCart(1)'>Add to cart</a>
</div>
<a id='open' href='/product/1'>Open modal</a>
<a ng-click='addToCart(1)'>Add to cart</a>
</body>
</html>
But the ng-click inside the modal doesn't fire. Is there a way I can refresh the scope with the modal content included?
To begin with, I thought I'd give it a go by adding an ng-click to the open modal button that can pass the scope to another function that opens the modal. I have a function that can console log the $scope after it has loaded, but I just need to refresh or something to make angular recognise the new html in the modal window.

The reason this doesn't work is because angularJS is not aware of the ng-click inside the modal. It was added by jQuery by the looks of it. AngularJS has not processed that HTMl and the ng-click does nothing.
You can, however, potentially use the $compile function in AngularJS to $compile the modal HTML with a particular scope. Then, the ng-click will work.
Have a read about $compile here:
https://docs.angularjs.org/api/ng/service/$compile
So an example in your case might be:
$compile(modalElement)($scope);
Where this happens inside BasketController, so that would be BasketController passed in to manage modalElement - the raw DOM element, not the jQuery element.
So at some point if you have access to the modal html (just after load) you could do that. Not recommended at all though! Use an Angular library.

Related

Ionic - How to use two controllers conditionally in one modal

I am working on ionic project, where I need to use single modal view & conditionally call controller for it.
I am trying to do like following -
<div ng-if="!isEditMode" ng-controller="Controller1">
<div ng-if="isEditMode" ng-controller="Controller2">
<ion-content>
<!-- My view code -->
</ion-content>
</div>
</div>
here, both controller are get called(can see console in controller). but html page not get rendered just white screen appear.
If I remove controller2 then it works fine with just one controller.

ng-click is not working in kendo editor

I'm trying to insert an html tag with ng-click event in the kendo editor. When I get the inserted data and show in a div, ng-click is not working. The normal onclick in javascript is working fine, but ng-click is not.
The below given is the <a> tag inserted on the editor text area.
<a ng-click="testMsg()"><span>' + nodeId + '</span></a>
Any idea on how to resolve this ?
onclick is DOM native event handler but ng-click isn't. Only those functions that are registered in the scope will be available. Any built-in functions will NOT be available to ng-click without an explicit assignment to the scope as my example shows.
var app = angular.module('app1', []);
app.controller('ctrl1', ['$scope', function($scope) {
function testMsg() {
alert('Hello world');
}
$scope.goodTestMsg = testMsg;
}]);
<div ng-app="app1" ng-controller="ctrl1">
<p ng-click="goodTestMsg()">Click me will show a message</p>
<p ng-click="testMsg()">Click me should happen nothing</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
TL;DR: The method testMsg is not exposed to the controller scope. Try adding something like $scope.testMsg = testMsg; will solve your problem.
Instead of textarea, try using div. I am facing similar problem which was partially solved with div. However it will be inline editing as the toolbar will get hidden and it will be displayed only when you focus cursor on the div.

AngularJS - looking to add and remove an iframe based on dom events

I would like to add an iframe to a page when certain links are clicked and remove it when other mouse events happen on the page. From what I can see, it seems that using an AngularJS directive would be the best way to do this. What I'm not sure about is what is the best way to format the directive. I'm thinking of making the directive at the attribute level...something like this:
<div myIframeDirective></div>
What I'm not sure of is the best way of removing/adding the directive contents (an iframe and a header above it) when various click events happen on the main page. Not looking for anyone to write the code for me...just looking for examples of how this can be best accomplished.
You should be using ng-if.
<iframe src="http://www.example.com/" ng-if="showIframe"></iframe>
<button ng-click="showIframe = !showIframe">Click me to show/hide the iframe</button>
Here's a working example:
var app = angular.module('app', []);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<iframe src="http://www.example.com/" ng-if="showIframe"></iframe>
<button ng-click="showIframe = !showIframe">Click me to show/hide the iframe</button>
</div>
In Angular, ng-if will remove the iframe from the DOM if it is false.
This means the URL will NOT be requested until ng-if is true.
<iframe ng-if="frameDisplayed" ng-src="{{src}}"></iframe>
And use the link
Toggle
Then in your controller, you can control what your iframe display:
$scope.src = 'https://angularjs.org';

$scope.$emit not working while $rootScope.$broadcast does

Here's my parent controller where I listen for the event
app.controller("SectionLayoutController", function($scope) {
$scope.$on("sectionLayout.doAction", function(e, options) {
// do some stuff
});
});
And in my child controller I do this
$scope.$emit("sectionLayout.doAction", { someOption: "something" });
The event should be triggered in the parent controller but it isn't. I did check what the contents of my child controller's scope was (with console.log) right before emitting the event, and the listener for the event does exist in one of its parents.
Maybe it's because the child controller's scope is not a direct child of the scope where the event is being listened on? This is not really a critical issue because I can just replace the line with
$rootScope.$broadcast("sectionLayout.doAction", { someOption: "something" });
and it works fine, but I'm still curious as to why $scope.$emit isn't working (it did work at some point, it just stopped randomly).
(Angular version 1.2.16)
EDIT
The problem seems to be a bug in angular.js, not ui-router (it has already been reported and is scheduled for a fix in version 1.3 https://github.com/angular/angular.js/issues/5489). Using ng-transclude inside a directive seems to create a sibling scope for the directive, which makes this kind of hierarchy (with my first plunker example) : http://i.imgur.com/4OUxJSP.png.
So yeah, I guess for now i'll be using $rootScope.$broadcast
Ok, so from what I've found, this is a bug with ui-router (forgot to mention I was using that, oops).
If the ui-view of a child route is inside a transcluded directive, events will get stuck there. Here's a plunkr of that bug in action http://plnkr.co/edit/zgqAPiDbB2LUtwJaeHhN?p=preview. The Dummy controller uses the sectionLayout directive and the ui-view is transcluded (as you can see in dummy.html).
<!-- dummy.html -->
<div section-layout="layout">
<!-- transcluded stuff -->
<div ui-view></div>
</div>
<!-- sectionlayout.html -->
<div>
<p>Section Layout for {{layout.title}}</p>
<p ng-repeat="r in recieves">{{r.message}}</p>
<div ng-transclude style="background-color: #EEE;"></div>
</div>
Here's another plunkr where $scope.$emit does work, and the only difference is that the ui-view is directly inside sectionlayout.html instead of transcluded in the directive http://plnkr.co/edit/mVftwkZrynkF6KanE4zV?p=preview.
<!-- dummy.html -->
<div section-layout="layout"></div>
<!-- sectionlayout.html -->
<div>
<p>Section Layout for {{layout.title}}</p>
<p ng-repeat="r in recieves">{{r.message}}</p>
<div ui-view style="background-color: #EEE;"></div>
</div>
And here's a completely different plunkr where I don't use ui-router, but it's still the same thing. A directive is used in a parent where events are listened, and the child controller is transcluded in the directive. In this one, both $scope.$emit and $rootScope.$broadcast work fine, so it seems to be a bug with transcluded ui-views in ui-router. http://plnkr.co/edit/Iz5YcbMiTzrXQ6bJMskK?p=preview
<div ng-controller="ParentController">
<p>Parent Controller</p>
<p ng-repeat="r in recieves">{{r.message}}</p>
<div my-directive>
<div ng-controller="ChildController" style="background-color: #EEE;">
<p>Child Controller</p>
<button ng-click="tryEmit()">$scope.$emit</button>
<button ng-click="tryBroadcast()">$rootScope.$broadcast</button>
</div>
</div>
</div>
I'll report this bug on their github page

Angular.js: How to reload scope?

I have some markup and loaded controllers.
Then I load some modal window contents by ajax, which is using one of controllers I have defined before. But looks like this controller isn't being used, because he is not required until modal loaded.
Question: How to make controller work when modal loaded? I tryied $scope.$digest(), got error "digest in progress".
index.html
<html data-ng-app="foo">
<head>
<script src="/js/app.js"></script>
</head>
<body>
<div id="modal"></div>
</body>
</html>
js/app.js
!(function(){
function FormCtrl($scope) {
console.log($scope); // never fired
$scope.Submit = function() {
console.log('submit'); // never fired too :C
}
}
angular.module('foo', []).controller('FormCtrl', FormCtrl);
})();
html content loaded by ajax and inserted to #modal
<div data-ng-controller="FormCtrl">
<form name="signup" data-ng-submit="Submit()">
<!-- form data -->
</form>
</div>
SOLUTION:
$.modal().open({
onOpen: function($e) {
$http.get('/views/' + url).success(function(data) {
$compile(data)($scope, function(clonedElem) {
$e.html(clonedElem);
});
// $e.html(data); was used instead of statement above
});
}
});
If you want to inject new DOM elements into existing Anuglar app. You options are to use
ng-include: This has a src property that takes the url from which partial content has to be loaded. AngularJS would internally compile it. One important thing here is that angular will download the template as soon it encounter ng-include in html.
Download and compile DOM manually using the $compile service which is a more involved process.
If your AJAX content contains a controller defined in ng-controller, AngularJS would create it for you.
But in any case, keep in mind the controller script should have been already wired at the initialization\setup phase.

Resources