Angularjs: evaluate expression inside HTML tag - angularjs

Note this question is a bit different from the similar titled here or here. What I want to do is to "evaluate inside the HTML tag" not inside a directive (or in the controller). This description can be wrong or hard to understand but I cannot find a better way; so I made bellow self-contained code to illustrate. You may copy and past and save as "xyz.html" and see what I mean.
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.6/angular.min.js"></script>
<script>
'use strict';
let app = angular.module('myApp', []);
app.controller('myCtrl', ['$scope', function ($scope) {
let d = DATA_TYPE;
let e = EDIT_TYPE;
$scope.tbContents = {
fields: [
{name: 'name', dataType: d.text, editType: e.text, refs:[]},
{name: 'catalog', dataType: d.text, editType: e.dropdown, refs: ['catalog1', 'catalog2', 'catalog3']},
{name: 'status', dataType: d.int, editType: e.dropdown, refs: [1, 2, 3, 4]}],
rows: [
{name: 'name1', catalog: 'catalog1', status: 1},
{name: 'name2', catalog: 'catalog1', status: 1},
{name: 'name3', catalog: 'catalog2', status: 2}
]
};
$scope.refNameKey = '';
$scope.setRefNameKey = function(key) {
$scope.refNameKey = key;
};
$scope.getRefByNameKey = function(key) {
let res = [];
for(let i = 0; i < $scope.tbContents.fields.length; i++) {
if($scope.tbContents.fields[i].name == key) {
return $scope.tbContents.fields[i].refs;
}
}
};
}]);
app.filter('filterDropdown', function () {
return function (fields) {
let output = [];
for (let i = 0; i < fields.length; i++) {
if (fields[i].editType == EDIT_TYPE.dropdown) {
output.push(fields[i]);
}
}
return output;
};
});
const DATA_TYPE = {
int: 1,
text: 2,
//...
};
const EDIT_TYPE = {
dropdown: 1,
test: 2,
//...
};
</script>
</head>
<body>
<div ng-controller="myCtrl">
<div>
<!--<p ng-repeat="field in tbContents.fields | filterDropdown">{{field.name}}</p>-->
<p ng-repeat="field in tbContents.fields | filterDropdown">{{field.name}}</p>
</div>
<hr />
<div ng-show = "refNameKey">
<p ng-repeat = "ref in getRefByNameKey(refNameKey)">{{ref}}</p>
</div>
</div>
</body>
</html>
What I want is the line I commented in the HTML code, refNameKey=field.name instead of setRefNameKey(field.name). I don't know why refNameKey=field.name does not work, but I don't like creating the setRefNameKey function for this simple task.

ng-repeat creates child scope for each iteration. So your refNameKey variable is created in each child scope and its not referring to the refNameKey in parent scope. You can fix this by modifying it like this:
<p ng-repeat="field in tbContents.fields | filterDropdown">{{field.name}}</p>
Plunker : http://plnkr.co/edit/mcDvGqd6SFCfmqyfUNRc?p=preview

Related

angularjs $compile html template in forEach not update variables

I want to dynamically generate html. I have generateHtml function contains loop for items, currently it is not displaying proper variables added in template. It is always display the last items data on all the iteration on compiled html.
Following is the controller & template code
This is my controller code
angular.module('app').controller('PageController', ['$scope', '$sce', '$compile','$templateRequest',
function ($scope, $sce, $compile,$templateRequest) {
$scope.itemsHtml = '';
// Array contains dynamic data
vm.items = [{
id: 1,
name: 'abc',
}, {
id: 2,
name: 'pqr',
}, {
id: 3,
name: 'stu',
}, {
id: 4,
name: 'xyz',
}];
vm.currentItem = [];
let templateUrl = $sce.getTrustedResourceUrl('/views/item.html');
$templateRequest(templateUrl).then(function(template) {
vm.itemTemplate = template;
}, function() {
});
vm.generateHtml = function() {
items.forEach(function (item, key) {
vm.currentItem = item;
let compiledTemplate = $compile(vm.itemTemplate)($scope).html();
/* Append compiled dynamic html */
$scope.itemsHtml += compiledTemplate;
});
}
function init() {
vm.generateHtml();
}
init();
}
]);
This is template view
<script type="text/ng-template" id="item.html">
<div className="item-wrapper">
<div className="item-inner">
{{ pageCtrl.currentItem.name }}
</div>
<div className="action-inner">
<div className="btn-action"
role="button"
ng-click="pageCtrl.edit(
pageCtrl.currentItem.id
)">
<i className="fa fa-plus"></i>
</div>
</div>
</div>
</script>
I got the solution for this issue.
Actually when we use compile after that we have to interpolate the compiled template
compiledTemplate = $interpolate(compiledTemplate)($scope);
let compiledTemplate = $compile(vm.itemTemplate)($scope).html();
/* Here interpolated compiled template */
compiledTemplate = $interpolate(compiledTemplate)($scope);
/* Append compiled dynamic html */
$scope.itemsHtml += compiledTemplate;

AngularJS - Watch filtered list for changes

Within angular I have a filtered list of people that takes the filter criteria from a predicate function. I want to watch a variable of the filtered list (called filteredPeople) every time the filtered list changes. But I am unable to see when that variable changes.
My code is below:
HTML:
<ul>
<li ng-repeat="person in ($ctrl.filteredPeople = ($ctrl.people | filter: $ctrl.filter))">
...
</li>
</ul>
JS:
controller: ['$scope',
function ($scope) {
var $ctrl = this;
$ctrl.people = {...}
$ctrl.filteredPeople = [];
$scope.$watch($ctrl.filteredPeople, function () {
console.log("called"); //not being called
});
$ctrl.filter = function (p) {
//custom filter function for each item in the array of people
}
}]
I can answer any questions of provide more code if needed
angular.module('app', []).controller('ctrl', function($scope) {
var vm = this;
vm.items = [
{ name: 'Sam' },
{ name: 'Max' },
{ name: 'Tom' },
{ name: 'Henry' },
{ name: 'Jack' },
{ name: 'Kate' }
]
var counter = 1;
$scope.$watchCollection('vm.filtered', function(){
console.log('Changed' + counter++);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl as vm'>
<input type='text' ng-model='vm.filter' />
<ul>
<li ng-repeat='item in vm.filtered = (vm.items | filter : vm.filter)'>{{item}}</li>
</ul>
</div>

Computed values in angular-schema-form

I have a form which is used to enter a bunch of values. I want to show various calculation on the values, but dynamically, so that when a number is changed the results immediately update. I thought that this should work, but it doesn't - i.e. the calculation is never run:
angular.module('calcs', ['schemaForm'])
.controller('CalcCtrl', function ($scope) {
$scope.schema = {
type: 'object',
properties: {
width: {
type: 'number',
title: 'Width'
},
depth: {
type: 'number',
title: 'Depth'
}
}
};
$scope.form = ['*'];
$scope.model = {};
$scope.$watch('[model.width, model.depth]', function() {
// This function is never called
$scope.area = $scope.model.width * $scope.model.depth;
});
});
I have seen this question, but I am doing quite a number of calculations and I really don't want to have to create a directive for each, so I am hoping there is another way. For reference, here is my template:
<div ng-controller="CalcCtrl">
<form sf-schema="schema" sf-form="form" sf-model="model"></form>
<p>Area: {{area}}</p>
</div>
I believe what you want is $watchCollection:
$scope.$watchCollection('[model.width, model.depth]', function() {
// This function is never called
$scope.area = $scope.model.width * $scope.model.depth;
});
example:
var app = angular.module('app', []);
app.controller('myController', function($scope) {
$scope.model = {}
$scope.$watchCollection('[model.width,model.height]', function() {
$scope.area = $scope.model.width * $scope.model.height;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="myController">
<input ng-model="model.width">
<input ng-model="model.height">
{{area || ''}}
</div>
<div>

How to chain AngularJS filters in controller

I have few filters in view
<tr ng-repeat="x in list | filter:search| offset:currentPage*pageSize| limitTo:pageSize ">
In my project to achieve good result, i have to make this filtering in controller not in view
i know the basic syntax $filter('filter')('x','x') but i don't know how to make chain of filters in controller, so everything will work as in my example from template.
I found some solution, now just with one filter, but should work with many ;)
$scope.data = data; //my geojson from factory//
$scope.geojson = {}; //i have to make empty object to extend it scope later with data, it is solution i found for leaflet //
$scope.geojson.data = [];
$scope.FilteredGeojson = function() {
var result = $scope.data;
if ($scope.data) {
result = $filter('limitTo')(result,10);
$scope.geojson.data = result;
console.log('success');
}
return result;
};
and i use this function in ng-repeat works fine, but i have to check it with few filters.
You can just re-filter what you get returned from your first filter. So on and so forth.
var filtered;
filtered = $filter('filter')($scope.list, {name: $scope.filterParams.nameSearch});
filtered = $filter('orderBy')(filtered, $scope.filterParams.order);
Below plunkr demonstrates the above.
http://plnkr.co/edit/Ej1O36aOrHoNdTMxH2vH?p=preview
In addition to explicitly applying filters to the result of the previous one you could also build an object that will chain multiple filters together.
Controller
angular.module('Demo', []);
angular.module('Demo')
.controller('DemoCtrl', function($scope, $filter) {
$scope.order = 'calories';
$scope.filteredFruits = $scope.fruits = [{ name: 'Apple', calories: 80 }, { name: 'Grapes', calories: 100 }, { name: 'Lemon', calories: 25 }, { name: 'Lime', calories: 20 }, { name: 'Peach', calories: 85 }, { name: 'Orange', calories: 75 }, { name: 'Strawberry', calories: 65 }];
$scope.filterFruits = function(){
var chain = new filterChain($scope.fruits);
$scope.filteredFruits = chain
.applyFilter('filter', [{ name: $scope.filter }])
.applyFilter('orderBy', [ $scope.order ])
.value;
};
function filterChain(value) {
this.value = value;
}
filterChain.prototype.applyFilter = function(filterName, args) {
args.unshift(this.value);
this.value = $filter(filterName).apply(undefined, args)
return this;
};
});
View
<!doctype html>
<html ng-app="Demo">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script src="script.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div ng-controller="DemoCtrl">
<input type="text" ng-model="filter" ng-change="filterFruits()" placeholder="Filter Fruits" />
<select ng-model="order">
<option value="name">name</option>
<option value="calories">calories</option>
</select>
<div ng-repeat="fruit in filteredFruits">
<strong>Name:</strong> {{fruit.name}}
<strong>Calories:</strong> {{fruit.calories}}
</div>
</div>
</body>
</html>
This is a typical case for FP libraries like lodash or Ramda. Make sure your common data is applied as last arg to each filter. (in this case columns)
$scope.columnDefs = _.compose(
$filter('filter3'),
$filter('filter2'),
$filter('filter1')
)($scope.columns)
or with extra args
$scope.columnDefs = _.compose(
$filter('filter3').bind(null, optionalArg1, optionalArg2),
$filter('filter2').bind(null, optionalArg1),
$filter('filter1')
)($scope.columns)

Editing model property inside ngRepeat, error undefined

I'm quite inexperienced with AngularJS.
I've read some answers that might be related but they were not clear enough for me.
My code- Plunker
I've created a table using ngRepeat and I want to change the websites name inside this table
only after clicking save button.
But I get error undefined.
My Code:
var webApp = angular.module('webApp', []);
//controllers
webApp.controller ('websitesCtrl', function ($scope, Websites) {
//$scope.x = new Website('1','3343','32434','name','privKey','pubKey','userID');
$scope.websites = Websites.get();
//This function displayes site details
$scope.expandWeb = function(website) {
console.log('expand');
$scope.websiteNew = angular.copy(website);
$scope.showName = true;
};
$scope.saveWeb = function(websiteNew) {
$scope.website.name = websiteNew.name;
$scope.showName = false;
};
});
//services
webApp.factory('Websites', function(){
var websites = {};
websites.get = function() {
return [{
id: '1',
created: '223112',
updated: '222212',
name: 'google.com',
secretKey: 'dhsd#22%$',
publicKey: '234233###',
userIdentification:'COOKIES'
},
{
id: '2',
created: '1111112',
updated: '444412',
name: 'walla.com',
secretKey: 'dhsd#22%$',
publicKey: '234233###',
userIdentification:'NONE-COOKIES'
},
{
id: '3',
created: '1111112',
updated: '444412',
name: 'Umms.com',
secretKey: 'dhsd#22%$',
publicKey: '234233###',
userIdentification:'NONE-COOKIES'
}
]
};
return websites;
});
My HTML:
<html ng-app="webApp">
<head>
<script data-require="angular.js#1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body ng-controller='websitesCtrl'>
<table >
<tr ng-repeat="website in websites">
<td>
{{website.name}}
<button ng-click='expandWeb(website)'>edit name</button>
</td>
</tr>
</table>
<div ng-show="showName">
<input ng-model="websiteNew.name"/>
<button ng-click='saveWeb(websiteNew)'>save</button>
</div>
</body>
</html>
$scope.webSite is undefined in saveWeb method. You should probably set it in expandWeb method.
$scope.expandWeb = function(website) {
console.log('expand');
$scope.website = website;
$scope.websiteNew = angular.copy(website);
$scope.showName = true;
};
http://plnkr.co/edit/beExyHMpXigVlCgJyfkF?p=preview

Resources