How to use HTML inside conditional bind in markup - angularjs

I have markup like this
<p>{{ !job.AdditionalNotes ? "No additional notes." : job.AdditionalNotes }}</p>
Would like to emphasis No Additional notes using something like.
<p>{{ !job.AdditionalNotes ? <em>"No additional notes."</em> : job.AdditionalNotes }}</p>
Is there a way to do this without using ng-if and ng-show to do this retaining the ternary operator?

1st Option
I get this working in the following way (without ng-show or ng-if). I'm using ng-bind-html and $sce service to render the HTML. Since your "no additional notes" message is generic and common, we can easily define in the controller and get it from a method after sanitization.
var app = angular.module("sa", []);
app.controller("FooController", function($scope, $sce) {
$scope.jobs = [{
name: "Sr. Software Developer"
}, {
name: "Software Associates",
AdditionalNotes: "Remote location"
}, {
name: "Front-end developer"
}];
$scope.trust = function(text) {
return $sce.trustAsHtml(text);
};
$scope.noNotesMessage = function() {
return $scope.trust("<em>No additional notes.</em>")
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sa" ng-controller="FooController">
<ol>
<li ng-repeat="job in jobs">
<strong>{{job.name}}</strong>
<p ng-bind-html="!job.AdditionalNotes ? noNotesMessage() : trust(job.AdditionalNotes)"></p>
</li>
</ol>
</div>
2nd Option
Alternatively, you can write a directive:
var app = angular.module("sa", []);
app.controller("FooController", function($scope, $sce) {
$scope.jobs = [{
name: "Sr. Software Developer"
}, {
name: "Software Associates",
AdditionalNotes: "Remote location"
}, {
name: "Front-end developer"
}];
});
app.directive('notes', function() {
return {
restrict: 'E',
scope: {
additional: '='
},
link: function($scope, element, attr) {
var html = "<p>" + ($scope.additional || "<em>No additional notes.</em>") + "</p>";
element.html(html);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sa" ng-controller="FooController">
<ol>
<li ng-repeat="job in jobs">
<strong>{{job.name}}</strong>
<notes additional="job.AdditionalNotes"></notes>
</li>
</ol>
</div>

Related

AngularJS, ng-repeat with condition

I'm new in AngularJS (2 days). I have this code:
<tr class="factoring-grid__row fixed_row" ng-if="vm.filteredXml.length > 0" ng-class="{disabled: vm.disableMyData(item)}"
ng-repeat="(index, item) in vm.filteredXml">
<td>{{item.receptor.razonSocial}}</td>
<td>{{item.folio}}</td>
<td style="text-align: center">
<div class="form-group datetime" style="display: flex">
<div class="factoring-grid__date-picker">
<input type="text" autocomplete="off" class="form-control input-sm" uib-datepicker-popup="{{vm.format}}"
ng-model="vm.filteredXml[index].fechaVencimiento" is-open="vm.datePickerList[index].isOpen"
datepicker-options="vm.dateOptions" current-text="Hoy" close-text="Cerrar" clear-text="Reset"
alt-input-formats="vm.altInputFormats" popup-placement="top-right" style="width: 100%;" />
</div>
<span ng-click="vm.openDatePicker(index)" class="calendar-icon"></span>
</div>
</td>
<td>
<span>{{item.montoTotal | filterPesoChile}}</span>
</td>
</tr>
I need to filter the data, I mean, those item have a propertie called item.status so I need to avoid to render as a the items where item.status === 'deleted'. Should I filter right here that condition? or should I filter the data that use this ng-repeat (vm.filteredXml`).
I'm not used to angularjs directives. In reactjs I just simply map or filter the array of data and return each , but don't know how to to that with ng-repeat
Here is a sample snippet using a directive and a dedicated filter with an optional status (default is disabled)
The directive template is embedded because I cannot add an external file in a StackOverflow snippet.
(function () {
'use strict';
angular.module('app', []);
angular
.module('app')
.directive('items', function () {
return {
restrict: 'EAC',
scope: {},
template: '<ul><li ng-repeat="item in vm.items|excludedItemsFilter:\'disabled\'">{{item.label}}</li></ul>',
link: link,
controller: controller,
controllerAs: 'vm'
};
function link(scope, element, attrs) {
scope.$on('$destroy', function () {
// destroy events binded to $rootScope,
// cancel $interval and $timeout timers
});
}
function controller($scope) {
var vm = this;
vm.items = [
{label: "Item 1", status: "enabled"},
{label: "Item 2", status: "enabled"},
{label: "Item 3", status: "disabled"},
{label: "Item 4", status: "enabled"},
{label: "Item 5", status: "disabled"}
];
}
});
angular
.module('app')
.filter('excludedItemsFilter', function () {
return function (items, excludedStatus) {
excludedStatus = excludedStatus || 'disabled'; // default value
return items.filter(function (item) {
return item
&& item.status !== excludedStatus;
});
}
});
})();
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<script src="module.js"></script>
</head>
<body>
<div items></div>
</body>
</html>
You can also remove the dedicated filter and filter items in directive like this :
angular.module("app").directive("items", function () {
return {
restrict: "EAC",
scope: {},
template: '<ul><li ng-repeat="item in vm.items">{{item.label}}</li></ul>',
link: link,
controller: controller,
controllerAs: "vm",
};
function link(scope, element, attrs) {
scope.$on("$destroy", function () {
// destroy events binded to $rootScope,
// cancel $interval and $timeout timers
});
}
function controller($scope) {
var vm = this;
vm.items = [
{ label: "Item 1", status: "enabled" },
{ label: "Item 2", status: "enabled" },
{ label: "Item 3", status: "disabled" },
{ label: "Item 4", status: "enabled" },
{ label: "Item 5", status: "disabled" },
].filter(function (item) {
return item && item.status !== "disabled";
});
}
});

Add html to json content in an NG-Repeat

Im repeating objects of JSON using NG-Repeat. How do I include html, such as a link, in here:
{id: "53", description: "here is a <a href = '#/detail/'>Link</a> to something", additional: ["2.jpg", "3.jpg" ]}
You need to use $sce to trust the HTML. I like to create a filter and use ng-bind-html on a <span> tag. Using a filter makes it super simple to show HTML wherever you need. Here is a simple example.
angular.module('app', [])
.filter('unsafe', ($sce) => {
return function(value) {
return $sce.trustAsHtml(value);
}
})
.controller('ctrl', ($scope) => {
$scope.items = [{
id: "53",
description: "here is a <a href = '#/detail/'>Link</a> to something",
additional: ["2.jpg", "3.jpg"]
},
{
id: "54",
description: "here is another <a href = '#/detail/'>Link</a> to something",
additional: ["4.jpg", "5.jpg"]
}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="item in items"><span ng-bind-html="item.description | unsafe"></span></li>
</ul>
</div>

how to access controller dataset in custom directive

I was playing with angular js scope of custom directive and tried to use controller dataset in custom directive with different attribute name but did not succeed. Kindly see my code and suggest me what is the proper way to access the data and set in ng-repeat in custom directive.
Do I need to set ng-repeat on custom directive or inside the directive template ? I mean
like this
<movie-list ng-repeat="entry in movieData" > </movie-list>
or inside directive template
<movie-list movieArray = "movieData" ></movie-list>
and then
<div>
<ul >
<li ng-repeat="entry in movieArray"> {{...}} </li>
</ul>
</div>
here is my data
app.js
angular
.module('app')
.controller('homeController', function($scope) {
$scope.movieData = [{
name : 'PK',
star : 'Aamir Khan',
releaseYear : '2015'
},
{
name : 'PiKu',
star : 'Irrfan Khan',
releaseYear : '2015'
}
];
});
// custom directive
angular
.module('app')
.directive('movieList', function(){
// Runs during compile
return {
scope: { movieArray : '=movieArray' },
controller: function($scope, $element, $attrs, $transclude) {
},
require: '?ngModel',
restrict: 'E',
templateUrl: 'movie.html',
replace: true,
link: function($scope, element, attr, controller) {
// console.log($scope.$parent);
}
};
});
index.html
<div ng-controller="homeController" >
<div class="col-lg-6">
<movie-list movieArray="movieData"></movie-list>
</div>
movie.html
<div>
<ul class="list-group" >
<li ng-repeat="entry in movieArray" class="list-group-item" >
<h3 class="list-group-item-heading">{{ entry.name }}</h3>
<p class="list-group-item-text">
{{ entry.star }} - Release in {{ entry.releaseYear }}
</p>
</li>
</ul>
</div>
scope: { movieArray : '=movieArray' } >> not working
scope: { movieArray : '=movieData' } >> not working
even I changed the attribute
<movie-list movieArray="movieArray"></movie-list>
but not working
Update attribute name:
<movie-list movieArray="movieData"></movie-list>
To:
<movie-list movie-array="movieData"></movie-list>
Note: Directive or attribute name should be lower case.
If you want to skip scope (delete scope: { movieArray : '=movieArray' }, from your directive ) in your directive you can do that by below code in Index.html:
<div ng-controller="homeController" >
<div class="col-lg-6">
< div movie-list></div>
</div>
And in your controller you need to replace movieData by movieArray:
.controller('homeController', function($scope) {
$scope.movieArray= [{
name : 'PK',
star : 'Aamir Khan',
releaseYear : '2015'
},
{
name : 'PiKu',
star : 'Irrfan Khan',
releaseYear : '2015'
}
];

How to evaluate stored expression in AngularJS $scope

Please help me in this issue. Following is the the code.
HTML:
<div ng-controller="ctrl">click here</div>
JS:
app.controller('ctrl', function($scope) {
$scope.true_link = "http://google.com";
$scope.link = "{{ true_link }}";
});
Result:
<div ng-controller="ctrl">click here</div>
Expectation:
<div ng-controller="ctrl">click here</div>
Replace {{ link }} by {{ true_link }} in HTML will solve this problem. But I have to use this way. How can I evaluate expression in $scope.link content again? Please help me. Thanks.
Update
Look like facebook, I have two wall pages: User page and Actor page. They have same template structure and process (append, remove element etc...) after bussiness function such as changeAvatar(), changeCover(), post() etc... So I create 'homepage' based directive:
JS
app.directive('homepage', function() {
return {
restrict: 'A',
templateUrl: 'homepage.html',
controller: 'homepageCtrl'
};
});
app.controller('homepageCtrl', function($scope) {
$scope.changeAvatar() = ...;
$scope.post() = ...;
});
and two extend controllers:
app.controller('userCtrl', function($scope, $http) {
$http.({...}).success((data){ $scope.username = data.username })
$scope.menu = [
{
title: "foo-user"
link: "/u/{{ username }}/foo-user"
}
{
title: "bar-user"
link: "/u/{{ username }}/bar-user"
}
]
});
app.controller('actorCtrl', function($scope) {
$http.({...}).success((data){ $scope.actorname = data.actorname })
$scope.menu = [
{
title: "foo-actor"
link: "/u/{{ actorname }}/foo-actor"
}
{
title: "bar-actor"
link: "/u/{{ actorname }}/bar-actor"
}
]
});
HTML
homepage.html
<section>
<header>
<ul class="menu">
<li ng-repeat="_menu in menu">
<a href="{{ _menu.link }}">
{{ _menu.title }}
</a>
</li>
</ul>
</header>
<main>
content...
</main>
</section>
User page:
<div homepage ng-controller="userCtrl"></div>
Actor page:
<div homepage ng-controller="actorCtrl"></div>
Two pages menu has same HTML structure & effect, but differ in items. I wanna define menu item in extended controller (userCtrl, actorCtrl) and print them by ng-repeat. The problem is evaluate $scope.menu.link content.
Solution
I found solution: using $scope.$eval (https://docs.angularjs.org/guide/expression).
In userCtrl, the $scope.menu[i].link is dynamic content because included username - received from ajax call. I can update $scope.menu[i].link in $http.success() by using foreach. But I think using $scope.$eval help me auto update wherever I want easier.
So, the code is:
JS
app.controller('userCtrl', function($scope, $http) {
$http.({...}).success((data){ $scope.username = data.username })
$scope.menu = [
{
title: "foo-user"
link: "'/u/' + username + '/foo-user'"
show: 'true'
}
{
title: "bar-user"
link: "'/u/' + {{ username }} + '/bar-user'"
show: 'username == "lorem"'
}
]
});
HTML
homepage.html
<section>
<header>
<ul class="menu">
<li
ng-repeat="_menu in menu"
>
<a
ng-href="{{$parent.$eval(_menu.link)}}"
ng-show="$parent.$eval(_menu.show)"
>
{{_menu.title}}
</a>
</li>
</ul>
</header>
<main>
content...
</main>
</section>
Create a directive, and use $eval to parse the expression:
app.directive('a', function (){
return {
restrict : 'E',
link: function(scope, element, attr){
element.attr('href',scope.$eval(attr.href));
}
}
});
app.controller('ctrl', function($scope) {
$scope.true_link = "http://google.com";
$scope.link = $scope.true_link; // you need a copy of `$scope.true_link` here
});
<div ng-controller="ctrl"><a ng-href="link">click here</a></div>
$scope.link = "{{ true_link }}"; this is just a string, nothing else, so it will render as such.
Don't be confused by the curly brackets.
You can complicate things by using $compile, eval, etc... or you can simply assign the true_link value to the link variable.
UPDATE
You have a problem in your ng-repeat directive. This:
<li ng-repeat="_menu in menu">
{{ menu.title }}
</li>
Should be this (note _menu):
<li ng-repeat="_menu in menu">
{{ _menu.title }}
</li>
Also, there's no need to use templating in your controller, so this:
$scope.menu = [
{
title: "foo-user"
link: "/u/{{ username }}/foo-user"
}
{
title: "bar-user"
link: "/u/{{ username }}/bar-user"
}
]
can be this:
$scope.menu = [
{
title: "foo-user"
link: "/u/" + $scope.username + "/foo-user"
}
{
title: "bar-user"
link: "/u/" + $scope.username + "/bar-user"
}
]
Why do you want to use {{link}} instead of {{true_link}}? What exactly the issue you've run into that "you have to use this way"? If you can explain this in details, may we can figure out a better solution.

AngularJS: Custom directive using dynamic attribute value doesn't work inside "ng-repeat"

Could you explain please why the following directive doesn't work?
attrs.ngMydirective seems to be undefined inside the linking function.
Live example here
HTML:
<body ng-controller="MyCtrl">
<ul>
<li ng-repeat="person in people">
{{ person.name }}
<span ng-mydirective="{{ person.age }}"></span>
</li>
</ul>
</body>
JS:
var app = angular.module('myApp', []);
app.directive('ngMydirective', function() {
return {
replace: true,
link: function(scope, element, attrs) {
if (parseInt(attrs.ngMydirective, 10) < 18) {
element.html('child');
}
}
};
});
app.controller('MyCtrl', function($scope) {
$scope.people = [
{name: 'John', age: 33},
{name: 'Michelle', age: 5}
];
});
You should use attrs.$observe to have actual value.
Another approach is to pass this value to directive's scope and $watch it.
Both approaches are shown here (live example):
var app = angular.module('myApp', []);
app.directive('ngMydirective', function() {
return {
replace: true,
link: function(scope, element, attrs) {
attrs.$observe('ngMydirective', function(value) {
if (parseInt(value, 10) < 18) {
element.html('child');
}
});
}
};
});
app.directive('ngMydirective2', function() {
return {
replace: true,
scope: { ngMydirective2: '#' },
link: function(scope, element, attrs) {
scope.$watch('ngMydirective2', function(value) {
console.log(value);
if (parseInt(value, 10) < 18) {
element.html('child');
}
});
}
};
});
app.controller('MyCtrl', function($scope) {
$scope.people = [
{name: 'John', age: 33},
{name: 'Michelle', age: 5}
];
});
<body ng-controller="MyCtrl">
<ul>
<li ng-repeat="person in people">
{{ person.name }}
<span ng-mydirective="{{ person.age }}"></span>
</li>
<li ng-repeat="person in people">
{{ person.name }}
<span ng-mydirective2="{{ person.age }}"></span>
</li>
</ul>
</body>

Resources