$http.get on HoverOver to populate PopOver text - javascript

I'm trying to implement a feature using Angular and Boostrap where the user can get a popOver on an item in the list, and have it perform an angular factory $http.get function to retrieve data and populate the popover text.
I'm not sure this is the best approach, but I have a ng-repeat like so:
<ul>
<li ng-repeat="product in products">
<model-popover ng-attr-id="{{product.Id}}"></model-popover>
</li>
</ul>
And my best guess is to use an angular directive, taking in the id number as a scope attribute,and performing a factory call from the directive. I've read up on the controller/link functions within the directive, but not sure the proper implementation
app.directive('modelPopover', ['Factory', function (Factory) {
return {
restrict: 'E',
replace: true,
scope: { id: "=" },
controller: function($scope){
var prod = Factory.getProductDetail(id);
},
template: '<a popover-placement="bottom" popover="{{prod}}">{{prod}}</a> '
};
}]);
I know the directive is incorrect, but i'm hoping there's enough information to help me out. Thanks in advance!

You do not need special directive for this, due to value-binding u can just change scope variable and popover will change also.
So u simply:
<button popover="{{var}}" popover-trigger="mouseenter" class="btn btn-default" ng-mouseover="changeVar()">Mouseenter</button>
And in changeVar you can change $scope.var any way you want.
Here is example plunk ($http call emulated using $timeout):
http://plnkr.co/edit/gnm1BtnHzNLnvO62Ar2i?p=preview

This var prod = Factory.getProductDetail(id);
has to be changed to $scope.prod = Factory.getProductDetail(id) if you want to use the mustaches

Related

angularjs click event, after appending content [duplicate]

I am working with angularjs 1.2.0-rc.3. I'd like to include html code into a template dynamically. For that I use in the controller :
html = "<div>hello</div>";
$scope.unicTabContent = $sce.trustAsHtml(html);
In the template I got :
<div id="unicTab" ng-bind-html="unicTabContent"></div>
It works fine for regular html code. But when I try to put angular template it is not interpreted, it is just included in the page. For example I'd like to include :
<div ng-controller="formCtrl">
<div ng-repeat="item in content" ng-init="init()">
</div>
</div>
Thanks a lot
This solution doesn't use hardcoded templates, and you can compile Angular expressions embedded within an API response.
Step 1.
Install this directive: https://github.com/incuna/angular-bind-html-compile
Step 2. Include the directive in the module.
angular.module("app", ["angular-bind-html-compile"])
Step 3. Use the directive in the template:
<div bind-html-compile="letterTemplate.content"></div>
Result:
Controller Object
$scope.letter = { user: { name: "John"}}
JSON Response
{ "letterTemplate":[
{ content: "<span>Dear {{letter.user.name}},</span>" }
]}
HTML Output =
<div bind-html-compile="letterTemplate.content">
<span>Dear John,</span>
</div>
For reference sake, here's the relevant directive:
(function () {
'use strict';
var module = angular.module('angular-bind-html-compile', []);
module.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
}());
This is what I've made, no idea if it's the angular wayTM, but it works and is super simple;
.directive('dynamic', function($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, element, attrs) {
scope.$watch(attrs.dynamic, function(html) {
$compile(element.html(html).contents())(scope);
});
}
};
});
So;
<div id="unicTab" dynamic="unicTabContent"></div>
Edit: I found the angular way, and it's super simple.
$templateCache.put('unicTabContent', $sce.trustAsHtml(html));
<div id="unicTab" ng-include="'unicTabContent'"></div>
Don't need to make your own directives or anything.
But it's a bind-once sort of deal, it wont see changes made to your html like the custom directive does.
As Vinod Louis says in his comment, the best way to do that was to use templates. I had to define a template outside of the regular code, for example I added that code inside of my index.html :
<script type="text/ng-template" id="unic_tab_template.html">
<div ng-switch on="page">
<div ng-switch-when="home"><p>{{home}}</p></div>
<div ng-switch-when="form">
<div ng-controller="formCtrl">
<div ng-repeat="item in content">{{item.name}}:{{item.value}}</div>
</div>
</div>
<div ng-switch-default>an error accured</div>
</div>
</script>
This template is conditional, so depending on the value of $scope.page, it switches between the 3 templates (the third being an error handler). To use it I had :
<div id="unicTab" ng-controller="unicTabCtrl">
<div ng-include="'unic_tab_template.html'"></div>
</div>
That way my page changes depending on the $scope inside of my unicTabCtrl controller.
To conclude the idea of inserting angularsjs template seams to be difficult to realize ($compile seams to be the solution, but I wasn't able to make it work). But instead you may use conditional templating.
I was trying to do the same thing and came across this module.
http://ngmodules.org/modules/ng-html-compile
I just included it and then I was able to use "ng-html-compile" instead of "ng-bind-html"
My solution to similar problem in my current app without using template (not elegant, but working):
directive('ngBindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
compile: function compile(tElement, tAttributes, transcludeFn) {
return function postLink(scope, element, attributes) {
scope.$watch(function() {
return scope.$eval(attributes.ngBindHtml);
}, function(newValue, oldValue) {
$compile(element.children())(scope);
});
};
}
};
}]);
It requires ngBindHtml on the same element and compiles the element content after it changes with ngBindHtml.
<div id="unicTab" ng-bind-html="unicTabContent" ng-bind-html-compile></div>
ng-html-compile looks similar but at first glance it won't be recalculated when the template content is changing. But I haven't tried it.
One way is use a directive for purpose of inserting custom templates that include angular expresssions
<div id="unicTab" unic-tab-content></div>
app.directive("unicTabContent",function(){
return {
restrict:"A",
template:'{{unicTabContent}}'
}
})
The code below is much simpler using Angular's built-in $interpolate and $sce objects. First inject the $interpolate and $sce Angular objects into your directive as you do anything custom you need in your directive.
amqApp.directive('myDir', ['$interpolate', '$sce', function ($interpolate,$sce ) {...}
Then create all your scoped variables found in your imported html expressions...
$scope.custom = 'Hello World';
Next use $interpolate to process your custom HTML and its expressions...then make sure you use the $sce object to trust it as HTML before binding...
var html = $interpolate('<b>{{custom}}</b>')($scope);
$scope.data = $sce.trustAsHtml(html);
Finally, in your view, just make sure use an element with the "ng-bind" or "ng-bind-html" on it in your view display. I found the $sce piece wont display the HTML as HTML (sees it as text) if you don't bind it in your html template like this...
<span ng-bind-html="data"></span>
You should see in bold...
Hello World
I used this trick to import in text/HTML with custom angular {{expressions}} from a web.config.
A lot simplified solution of #clement-roblot based on built-in templates.
Controller:
app.controller('TestCtrl', [
'$scope',
'$templateCache',
function ($scope, $templateCache) {
$templateCache.put('test.html', '2 + 2 = {{ 2 + 2 }}');
}
]);
View:
<div ng-include="'test.html'"></div>

Recursion in angular directives / templates

I was trying to build a custom directive for displaying a tree. For some reason it seems that once you include the directive in its own template, something runs wild in the angular compiler and the browser process gets stuck in a loop.
Here's a plunker:
<li class="list-group-item">
<a ng-click="clicked(item)"><span ng-if="item.items" class="glyphicon glyphicon-plus text-primary"></span></a>
<span ng-if="!item.items" class="glyphicon glyphicon-record text-primary"></span>
<a>{{item.name}}</a>
<div ng-if="item.items && item.items.length>0">
<ul class="list-group">
<taxonomy-item ng-if="item.items && item.items.length>0" ng-repeat="child in item.items"></taxonomy-item>
</ul>
</div>
If you pay attention you'll see that it doesn't even bind data, so it shouldn't be a recursive loop caused by model/data, but rather a compiler issue...
http://plnkr.co/edit/1aollcuCr2gA96W6Sk6q
Careful, running might freeze your browser tab!
Any suggestions on how to work around this problem?
The solution is quite simple. Since angular doesn't permit the usage of directives within themselves, you need to make use of the link function, to add the child directive afterwards. A minimal example:
myApp.directive("myDirective", ["$compile",function($compile){
var template = '<my-directive ng-repeat="child in item.items" ng-model="child"></my-directive>';
return{
templateUrl: "myDirective.html",
replace: true,
transclude: true,
scope: {
item: "=ngModel"
},
link: function(scope, element){
var compiled = $compile(template)(scope);
element.append(compiled);
}
}
}]);
This won't cause any trouble, since the compiled child directive is self-contained and compiled afterwards.

Template inside of a directive

I have a strange situation where I need to put a template inside of a template in my directive. The reason for this is that AngularJS will not read ng-repeat code inside of attributes.
In an ideal world, this is what my code would look like:
<div ng-repeat="phone in phones">
<button popover="<div ng-repeat='friend in phone.friends'>{{friend.name}}</div>"></button>
</div>
Unfortunately this does not work because of the quotes around the popover attribute. This has led me down a pretty deep rabbit hole where I'm trying to put a template inside of a template like in this plunker:
http://plnkr.co/edit/ZA1uA1UOlU3cCH2mbE0X?p=preview
HTML:
<div my-popover></div>
Javascript:
angularApp.directive('myPopover', function( $compile) {
var getTemplate = function()
{
var scope = {title: "other title"};
var template = "<div> test template. title: {{title}}</div> ";
return $compile(template)(scope);
}
return {
restrict: 'A',
template: '<button class="btn btn-default" popover="{{content}}" popover-title="title">template</button>',
link: function(scope) {
scope.content = getTemplate();
}
};
})
Unfortunately this does not work because AngularJs complains about a circular reference. Please help! (this has been taking me all day)
I'm not sure I understand exactly what you are trying to achieve, but from the look of it you might want check out the transclude option for directives.
From the docs:
use transclude: true when you want to create a directive that wraps
arbitrary content.
If you use transclude, you can store the popover content inside the button, and "forward" that content to where you want it using the ng-transclude directive.
Your code would then look something like this:
<button>
<div ng-repeat='friend in phone.friends'>{{friend.name}}</div>
</button>
You can see some examples in action in the guide to directives.

Using AngularJS directives to create a single corrent answer survey

I've created a single answer survey, and I want to translate it into a directive. I'ts a single answer because only one answer can be chosen at the same time, and it triggers x or y function (also ng-class) depending on the chosen answer.
I have the working functionality without directive in this jsfiddle. As you'll see, every link triggers an different ng-click.
<div class="col-md-4">
<a class="btn col-md-12" ng-click="continent='1'" ng-class="{'active' : continent == '1'}">North America</a>
</div>
<div class="col-md-4">
<a class="btn col-md-12" ng-click="continent='2'" ng-class="{'active' : continent == '2'}">South America</a>
</div>
My problem is that this method doesn't work (or better, I don't know how to do it) when translated into a directive. You can see the jsfiddle with the directive here.
And the js:
.directive('myContinent', function() {
return{
restrict: 'E',
template: '<div class="col-md-4"> \
<a class="btn col-md-12" ng-click="continent=\'1\'" ng-class="{active : continent == \'1\'}">{{text}} \
</a> \
</div>',
replace: true,
scope: {
text: '#'
}
};
});
I think one of the easiest and clean ways is to pass the controller's scope variable to your directive scope (using =). You can save the selected continent in this variable and use it in multiple directive instances.
Fiddle: http://jsfiddle.net/pascalockert/xRy7H/1/
The problem is that all your my-continent directives has different isolated scopes, so each of them has scope.continent == 1 after you click it.
You can modify your directive in any of the available solutions:
Build in on top of radio input group and standard angular form.
Use $parent.continent and assign different values to it (bad practice actually).
Use a single directive with transclusion (where options are transcluded into the 'parent' directive).
I'll leave the choice for you, as it depends on the overall architecture of your decision.

How to define a function inside Angular-js Directive

I created a directive for selecting users using bootstrap's drop-down element. as follows.
Javascript
app.directive('usersDropdown', function(ConfigService,$http) {
return {
templateUrl: 'app/views/users_dropdown.html',
link: function($scope, element, attrs) {
});
};
});
users_dropdown.html
<div class="btn-group pull-right" >
<a class="btn dropdown-toggle" data-toggle="dropdown" href="">
Select User
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-right align-left" role="menu" aria-labelledby="dropdownMenu">
<li ng-repeat = "userList in userLists"><a ng-click="getUserDetails('{{userList.SessionId}}')" style="cursor:pointer">{{userList.UserPartyName}}</a></li>
<li ng-hide="userLists" style="cursor:pointer"><a>No Users</a></li>
</ul>
</div>
I need to create a function getUserDetails, which should be called while clicking on a user from the list. My question is how to define this function inside the directive itself? I wrote it in the controller. But the problem is I am having several controllers. It's not a good practice to write the same function in all controllers. If I can wrote the function common for all controller, that is inside the directive, it will be good. If you have a good solution, let me know that.
Modification
I modified my directive's js as follows
app.directive('usersDropdown', function(ConfigService,$http) {
return {
templateUrl: 'app/views/users_dropdown.html',
link: function($scope, element, attrs) {
$scope.getUserDetails = function(user_id){
console.log(user_id);
}
}
};
});
For this I am getting the result in the console as {{userList.SessionId}}. Not the value for that. But when I Inspected the function is passing expected value.
Then why the expected value is not getting inside of that function?
Solved
I solved the issue by changing my directives html tamplate. I removed ' and {{ from it as follows.
ng-click="getUserDetails(userList.SessionId)"
The link Directive function is a js function, inside which you may do anything
app.directive('usersDropdown', function(ConfigService,$http) {
return {
scope : {},
templateUrl: 'app/views/users_dropdown.html',
link: function($scope, element, attrs) {
$scope.somefunction = function(a) {
// Do some stuff
}
}
};
});
Also you should use isolated scope to accomplish, if you want this function not be accesible from outside the directive.
If you find yourself in the need of a function or data variable in various places along your application, it could possibly be a good solution to move some of your logic into a service.
You could, for example, create a user service, which should provide application-wide access to user-related information and methods.

Categories

Resources