Popovers without jQuery - angularjs

I've been implementing popovers with AngularStrap and AngularUI Bootstrap. I can get both of these frameworks to get popovers working alongside the full jQuery library, but not when I exclude jQuery. I know that Angular includes a version of jQuery called jQlite, and supposedly that should be all you need to implement these other frameworks. Here's my question, is it even possible to implement popovers in Angular without the full jQuery library?

The first line of the ui-bootstrap homepage states:
This repository contains a set of native AngularJS directives based on
Bootstrap's markup and CSS. As a result no dependency on jQuery or
Bootstrap's JavaScript is required
AngularStrap's page doesn't mention any dependancy on jQuery either.
So, to answer your question, yes, you can implment the popovers without jQuery being included in your project.

I wasn't able to get the AngularUI popups working either, but was successful with Angular-Strap. I put together an example plunker that may help you out. I used something similar on my site.
Angular Strap popup directive (on Plunker http://embed.plnkr.co/lxL65Q/preview)
I'm using the popup for images. The 'image-popup' directive is placed in an img tag, where it expects a src attribute. Libraries I needed were the Bootstrap styles, Angular JS, and the Angular Strap script and template files.
<!DOCTYPE html>
<html ng-app="popupApp" ng-controller="AppCtrl">
<head>
<link data-require="bootstrap-css#3.3.1" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<link href="style.css" rel="stylesheet" />
<script data-require="angular.js#1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js"></script>
<script data-require="angular-animate#1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular-animate.js"></script>
<script src="//cdn.rawgit.com/mgcrea/angular-strap/v2.1.3/dist/angular-strap.js" data-semver="2.1.3" data-require="angular-strap#2.1.3"></script>
<script src="//cdn.rawgit.com/mgcrea/angular-strap/master/dist/angular-strap.tpl.js" data-semver="2.1.3" data-require="angular-strap#2.1.3"></script>
<script src="app.js"></script>
</head>
<body>
<h1>Using Angular JS and Angular-Strap to create a popup image viewer</h1>
<figure class="col-sm-4">
<img image-popup alt="lolcat" class="img-popup-source" src="http://obeythekitty.com/wp-content/uploads/2015/01/lolcat_flying_cat.jpg" />
<figcaption>Leaping Lolcats!</figcaption>
</figure>
</body>
</html>
The example app code looks like this:
// Include a reference to ngStrap like this
var popupApp = angular.module('popupApp', ['mgcrea.ngStrap']);
// This demo doesn't really need a controller but hey.
popupApp.controller('AppCtrl', ['$scope', function($scope) {
}]);
// Directive that displays an image within an Angular Strap popup.
// This directive should only be applied to an img, as it expects a 'src' attribute.
popupApp.directive('imagePopup', ['$popover', '$compile', '$window', function($popover, $compile, $window) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var myPopover = $popover(element, {
title: attrs.popupTitle,
template: 'angular-strap-popover-tpl.html',
html: true,
trigger: 'manual',
autoClose: true,
transclude: true,
scope: scope,
animation: 'am-fade'
});
// Toggle the popover closed when you click it
scope.closeMe = function() {
myPopover.toggle();
};
// Toggle the popover closed when you click the original smaller image.
element.on('click', function(element) {
myPopover.toggle();
});
},
// Isolate scope, load the popup template image's src from the src attribute of the original image
scope: {
popupImageSrc: '#src'
}
}
}]);
You may want to fiddle with the popup options and styles. Here's the popup template I used:
<div class="popover">
<div class="arrow"></div>
<h3 class="popover-title" ng-bind="title" ng-show="title"></h3>
<div class="popover-content" ng-click="closeMe()"><img ng-src="{{popupImageSrc}}"></div>
</div>
Angular Strap's components worked well for me, and they have a builder that lets you put together just the components that you need. I did have one challenge in testing this directive on my site though. I also use AngularUI (for the carousel) and there was a namespace collision on a 'tooltip' module shared by both libraries. I never did figure out how to work around that one.
I hope this is helpful, all the best.

Related

Applying conditions in ng-show and ng-hide

I am new to Angular JS, I got stuck up with this code, While I am reading the tutorials, I got stuck up with this code on Angular JS. Please help me on that.
More particularly how 'AfterClicked' is working in controlling the visibility of the DIV elements.
HTML Code:
<!DOCTYPE html>
<html ng-app="myModel">
<head>
<title>Begin with Angular</title>
<meta charset="utf-8" />
<!-- Adding the Bootstrap style sheet-->
<link href="Styles/bootstrap.min.css" rel="stylesheet" />
<!-- Adding the Angular File-->
<script src="Scripts/angular.js"></script>
<script src="Scripts/MainM.js"></script>
</head>
<body>
<div ng-controller="myController as control">
<div ng-hide="AfterClicked">
{{control.title}}
click To Edit
</div>
<div ng-show="AfterClicked">
<input ng-model="control.title">
Done Editing??
</div>
</div>
</body>
</html>
JS Code:
/// <reference path="angular.js" />
var myApp = angular.module("myModel", [])
.controller("myController", function () {
this.title = "Welcome to Angular",
this.AfterClick=0
});
It's working fine, The thing that i got stuck up is "AfterClicked", How it's working in ng-show, ng-hide and ng-click in anchor tags.
Please help me on that..!!
Updated answer:
When you don't define an object in AngularJS controller code but use it in the template, Angular creates that object and it's assigned empty string value. So, in this case, even though you did not assign any value to AfterClicked in JS code, it was created by Angular and set to ``. This evaluates to false and the div is hidden.
You can check the value in this fiddle: http://jsfiddle.net/5DMjt/12053/
Firstly, couple of mistakes in the code.
1.There is a typo with AfterClicked in JS code.
2.Since you are using the myController as control syntax, ng-click on the anchor tag should use control.AfterClicked and not AfterClicked.
Now in JS code, AfterClicked is being set to 0, which evaluates to false in JS.
In the ng-hide="AfterClicked" code, AfterClicked evaluates to false and hence angular hides the div.
In the anchor tag code ng-click="AfterClicked=!AfterClicked",means that on click of anchor tag, the expression "AfterClicked=!AfterClicked" is evaluated ,which negates AfterClicked.

Dynamically loaded javascript of angular not working

I am building an application where my DOM is add dynamically outside of angular world ie. jQuery and also the javascript for that is loaded dynamically. I get $compile from angular.injector method and also the scope and then did used the $compile service. But with all this my directives and controller don't run. I see them added to my app but still controller won't initiated.
index.html is
<html>
<head>
<script data-require="jquery#2.2.0" data-semver="2.2.0" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="https://code.angularjs.org/1.5.8/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-app="test">
<div id="main">
</div>
</div>
<!--<script src="https://googledrive.com/host/0ByYZbKdhSDoSeFNQVGdEd2k3amM/direc.js"></script>-->
</body>
</html>
script.js
//Create angular module
angular.module("test",[]);
$(document).ready(function(){
//Load dynamically the JS
$.getScript('https://googledrive.com/host/0ByYZbKdhSDoSeFNQVGdEd2k3amM/direc.js', function(){
angular.element($("#main")[0]).scope().$root.$apply();
});
var scrip = "<script src=\"https://googledrive.com/host/0ByYZbKdhSDoSeFNQVGdEd2k3amM/direc.js\"></script>"
var htmlStr = "<span test-drective=\"test-drective\">Test</span>";
$("#main").append(angular.element('*[ng-app]').injector().get("$compile")(htmlStr)(angular.element($("#main")[0]).scope().$new(true)));
})
and direc.js is
angular.module("test")
.directive("testDrective", function(){
return {
restrict: 'A',
controller: function($scope) {
console.log("Controller")
},
link: function(scope, elem, attrs) {
console.log("link")
}
};
})
If I add the direc.js in index.html, all works fine but not with the dynamic load of script using jquery or added as part of dom.
Sample app:
https://plnkr.co/edit/gekifqGzJDAsWs5OF4ro
Try to bootstrap your angular app in code after you loading is completed and your DOM is ready angular.bootstrap(document, ['myApp']);
See bootstraping angular section "Manual Initialization"
In general all your javascript code of your angular app must be present at the client. And you should not manipulate the DOM outside directives. Perhaps this ng-bind-html-compile directive helps you to solve your problem in a more angular way

angular js dynamic directive passing model attribute

I knwo that are many relevant posts about how creating dynamic directive mapping in angular but I cannot find anything similar in my case. As the description suggests I am trying to make a dynamic directive where depending the value of an object attribute the html is changed. I simplified my problem to this case:
I have a list of object "Widget" which has an attribute name 'type'. I want depending on the type attribute of this list to render html depending on the attribute. To be more specific the Widget.type can have 3 values for example: widget1,widget2 . So in my case I want the directive to return the file widget*.html. (widget1.html for widget type "widget1" etc). Though I pass the argument to the directive using attributes, the value is not evaluated and the string widget.type is return.
Can you help me?
Thx in advance.
app.js file:
var app = angular.module("MainCtrl", []);
app.controller("myCtrl", function($scope) {
function Widget(type){
this.type=type;
}
$scope.widgets=[];
$scope.widgets.push(new Widget('widget1'),new Widget('widget2'),new Widget('widget3'));
});
app.directive('widget', function() {
return {
restrict: 'E',
scope: {
obj: '='
},
templateUrl: function(element,attr){
//console.log(attr);
console.log(attr.obj);
return attr.obj+'.html';
}
}
});
index.html file:
<!DOCTYPE html>
<html ng-app="MainCtrl">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<!-- css -->
<link rel="stylesheet" href="style.css" />
<!-- utilities -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<!-- angular module file -->
<script src="script.js"></script>
</head>
<body ng-controller="myCtrl">
<div ng-repeat="widget in widgets">
{{widget.type}}
<widget obj='widget.type' ></widget>
</div>
</body>
widget1.html file:
<div class="widget">
widget1:{{widget.type}}
</div>
widget2.html,widget3.html are the same
Notice the console output in the directive.
I created a plunker so it's easier to notice the problem:http://plnkr.co/edit/ulBSQQrqpSV9g3BNGRhO?p=preview
First example with your directive: http://plnkr.co/edit/XAXy5RGLnvUZIoRj5xAs?p=preview
Secone example without your directive:
<div ng-repeat="widget in widgets">
<!--{{widget.type}}-->
<div ng-include="widget.template"></div>
</div>
Note: additional property in your Widget-object. Easier to handle.
First one is not as beautiful as the example without the directive, becaue your scope-variable of the directive has to be the same as the variable in your templates ('widget'). I like the second example much more.

How do I include HTML from an attribute as a token in a directive template

I want to inject HTML as an attribute into a template and have it display within the directive that passed the attribute.
My HTML
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.4.0-rc.0" src="https://code.angularjs.org/1.4.0-rc.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app">
<div>
<my-test mymessage='<b>Booya!</b>'></my-test>
</div>
</body>
</html>
My Javascript
app = angular.module('app', [])
.directive("myTest", function(){
return {
scope: {
mymessage: '='
},
restrict: 'E',
transclude: true,
template: '<p>Guess what...{{mymessage}}...you know it!</p>'
};
});
Here is the code in Plunker: http://plnkr.co/edit/ZJSkf1Ye4ccKURTJU8KD?p=preview
Note how it shows the literal for the binding.
I'm sure there are several problems here:
I am probably not properly binding the attribute within the directive scope
Once I have problem #1 solved I am pretty sure the HTML will be escaped.
What changes do I need to make in order to get the directive to render properly?
If you are binding HTML without Angular code (expressions and directives), then you need to use ng-bind-html:
scope: {
mymessage: "#" // no need for two-way binding
},
template: '<p>Guess what...<span ng-bind-html="mymessage"></span>...you know it!</p>'
This, on its own, would not work since it is unsafe. You have two options then:
1) include ngSanitize dependecy to your app:
angular.module("app", ["ngSanitize"])
This will automatically apply HTML sanitation - Demo
or, 2) use $sce service and call $sce.trustAsHtml on the variable holding the HTML content. This would not work, however with one-way string binding "#".

How to hide and show templates rather than destroy and reinject them everytime

I am using ui-router and Named views. it works flawlessly but it doesn't suit my purpose. I don't want the templates to be injected everytime i click on the menu item but only the first time it is clicked after application is loaded. The remaining times, it should simply hide the template and show the template that is currently active for a menu click. when coming back to the original menu click, it should just do ngShow="true" for the hidden template and hide the current one. Is that possible to do without editing the cleanupLastView() in the angular-ui-router?
If i comment out the cleanupLastView(), it simply keeps on injecting templates and does nothing to older templates. So both end up showing. Tried to debug the cleanupLastView() but i couldn't figure out what variables are available to me there so i could re-show what ever is clicked a second time rather than inject again.
index.html
<!DOCTYPE html>
<html lang="en">
<head ng-app="myApp">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="style.css" rel="stylesheet">
<script type="text/javascript" src=".lib/angular-1.3.0/angular.js"></script>
<script type="text/javascript" src="./angular-ui-router.js"></script>
<script type="text/javascript" src="./modular/ct-ui-router-extras.core.js"></script>
<script type="text/javascript" src="./modular/ct-ui-router-extras.dsr.js"></script>
<script type="text/javascript" src="../timerService.js"></script>
<script type="text/javascript" src="../dataService.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body>
<div class="navbar navbar-default navbar-static-top" role="navigation">
<ul class="nav navbar-nav">
<li ng-class="{active: $state.includes('main')}"><a ui-sref="home">Main</a></li>
<li ng-class="{active: $state.includes('files')}"><a ui-sref="files">Files</a></li>
</ul>
</div>
<div>
<div ui-view="pane"></div>
</div>
</div>
</body>
</html>
app.js
var myApp = angular.module("myApp", [ 'ui.router', 'ct.ui.router.extras.dsr']);
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('main',
url:"/main",
deepStateRedirect: true,
views : {
pane: {
templateUrl : 'tpl.pane-0.html'
},
}
)
.state('files',
url: '/files',
deepStateRedirect: true,
views : {
pane : {
templateUrl : 'tpl.pane-1.html'
},
'first#files' : {
templateUrl : 'tpl1.first.html',
deepStateRedirect: true
},
'second#files' : {
templateUrl : 'tpl1.second.html',
deepStateRedirect: true
},
},
)
});
tpl.pane-0.html
<div>
<input type='text'></input>
</div>
tpl.pane-1.html
<div >
<div data-ui-href='first'></div>
<div data-ui-href='second'></div>
</div>
tpl1.first.html
<div>
<input type='text'></input>
</div>
tpl1.second.html
<div>
Second
</div>
This is the simplified code of my app. it works to do the multiple named views but fails at deepStateRedirect which seems to be the one associated with nav bar type of templates rather than(?) Sticky. Is that correct? I added the param deepStateRedirect: true to allthe views being injected but to no avail. if the tpl1.first.html's input text box is written and the view switched to main, and come back, there is no text to be found in the input box. What am i doing wrong?
Yes, you can do this but it requires some custom work.
Change the state so that it uses an empty template.
In the state's controller check if the document already contains the HTML for the view (use a unique ID).
If required, use the $template cache and $compile to create the original template view and append it to the document body.
In the template, you can use ng-show="isState_expression | isState" expression to toggle the visibility of the template.
Basically the ui-routes work the same as before, but you're handling the compiling of the template yourself. Once it's in the DOM (not inside the ui-view). You can just leave it there to be handled by ng-show.
I don't recommend using resolve on the state since it's going to become a static state.
Here's the manual on $compile:
https://docs.angularjs.org/api/ng/service/$compile
Here's a tutorial on $compile:
http://www.benlesh.com/2013/08/angular-compile-how-it-works-how-to-use.html
Here's a stack answer on $compile:
Compiling dynamic HTML strings from database
Why doesn't it suit your needs? Do you need to preserve the states when they are not shown?
If so, perhaps you should look into UI Router Extras Sticky States. It allows you to preserve the $scope of a state and not create/destroy it every time. This would be better than trying to reinvent the wheel.
Take a look at the sample.

Resources