Angular, sort by enting property as text input - angularjs

I want to sort a list of comments by different properties such as rating, date,...
but not using buttons and get property as text in input.
by default the property is rating, but ng-repeat does not publish any thing.
my code is:
<div ng-controller="controller as Ctrl">
<p>SortBy: <input type="text" name="input" ng-model="Ctrl.dish.sortProperty"></p>
<blockquote ng-repeat="comment in Ctrl.dish.comments | orderBy:'{{Ctrl.dish.sortProperty}}'">
<p>{{comment.rating}}</p>
<p>{{comment.comment}}</p>
<footer>{{comment.author}} ,<cite title="Source Title">{{comment.date| date:'mediumDate'}}</cite></footer>
</blockquote>
</div>
and the controller as:
<script>
var app = angular.module('myApp',[]);
app.controller('controller', function() {
var dish={
sortProperty:'rating',
comments: [
{
rating:5,
comment:"blablalbla",
author:"John Lemon",
date:"2012-10-16T17:57:28.556094Z"
},
{
rating:4,
comment:"blablabla",
author:"Paul McVites",
date:"2014-09-05T17:57:28.556094Z"
},
{
// more comments
}
]};
this.dish = dish; });
</script>

Remove the single quotes and the interpolation {{ }}:
<blockquote ng-repeat="comment in Ctrl.dish.comments | orderBy: Ctrl.dish.sortProperty">

Related

AngularJS. Dynamically added inputs don't affect $valid

trying to make a dynamically composed form.
Fields and attributes come from server. Angular should get them and generate a form.
I decided to use directive and runtime compilation.
Mostly all works except a few things.
The problem: $valid is undefined for compiled fields.
For static fields is works.
Please give me an advice.
app = angular.module('dyno', []);
app.controller("fieldCompilation", function($scope) {
$scope.list = [{
id: "0",
name: "A"
},
{
id: "1",
name: "B"
},
{
id: "2",
name: "C"
},
];
});
app.directive("otcDynamic", function($compile) {
return {
link: function(scope, element) {
// Add Text Input with pattern validation and required attr
var template = "<input type='text' name='input3' ng-model='input3' placeholder='input3 [0-9]{2} required' pattern='[0-9]{2}' required='true'/>";
var linkFn = $compile(template);
var content = linkFn(scope);
angular.element(document.getElementById("f3")).append(content);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js"></script>
<div ng-app="dyno">
<div ng-controller="fieldCompilation">
<br><b> Static Input:</b>
<ng-form name="form1">
<div id="f1">
1. Input <input type='text' name='input1' ng-model='input1' placeholder='input1 [0-9]{2} required' pattern='[0-9]{2}' required='true' /><br>
</div>
<br><b> Dynamic Inputs:</b>
<div otc-dynamic>
<div id="f3">
3. Input
</div>
</div>
<br> <b>Details</b>
<div>
Counter: {{ message }}
</div>
<div>
Model Input1: {{ input1 }}
</div>
<div>
Model input3: {{ input3 }}
</div>
<div>
Validation input1: {{ form1.input1.$valid }}<br> Validation input3: {{ form1.input3.$valid }}<br> Validation form1: {{ form1.$valid }}<br>
</div>
</ng-form>
</div>
</div>
Fiddle

ng-model saving with a recursive display

Setup
I have a frontend that displays a JSON. One can edit the values in the JSON via an ng-model textarea with a save button.
It does this by recursively calling an object like this:
In tree-object-edit.html:
...
<h4 class="capitalize">{{ item_key }}: </h4>
<textarea rows="2" cols="60" ng-model="item"></textarea>
<div ng-if="item.properties">
<ul>
<li ng-repeat="(item_key, item) in item.properties" ng-include="'html/tree-object.html'"></li>
</ul>
</div>
This is called by this my-definition.html
<form name="form"
role="form"
novalidate
class="ng-scope ng-invalid ng-invalid-required ng-dirty ng-valid-minlength"
ng-controller="MyDefinitionDetailController">
<h4>My Definition Edit: {{myDefinition.name}}</h4>
<ul ng-repeat="(item_key,item) in myDefinition" ng-include="'html/tree-object-edit.html'"></ul>
<button type="submit" ng-click="create()">
<span>Save</span>
</button>
</form>
My my-defintion-detail.controller.js contains
$scope.create = function () {
MyDefinition.save($scope.myDefinition,
function () {
});
};
Problem
Clicking save does not save the myDefintion because myDefiniton has not been changed. Only item has been changed.
Question
Is there a way to make the myDefiniton change when the item "within it" changes?
UPDATE 1
I have found that:
<textarea rows="2" cols="60" ng-model="myDefinition[item_key]"></textarea>
Does work for the first level of the recursion
You correctly noted, that ng-model="myDefinition[item_key]"(parent[key] at my example) works, - changes will reflect into desired model, if you will make them on property level not on direct value. So, besides passing value and key to nested template, you also should pass parent object, that way chain of references from actual value to "head" will be created:
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.value = {
identity: {
name: 'Max',
surName:'Smith',
},
birthDay: {
year: 1990,
month: 2,
day: {
weekDay: 'friday',
number: '13'
}
},
firstLevel: 'abc'
}
$scope.isObject = function(value){
return angular.isObject(value);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller="ctrl">
<script type="text/ng-template" id="template">
{{key}}
<ul ng-init='temp=value'>
<input ng-if='!isObject(value)' type='text' ng-model='parent[key]'/>
<li ng-if='isObject(temp)' ng-repeat='(key, value) in temp' ng-init='parent=temp' ng-include="'template'"></li>
</ul>
</script>
<ul ng-init='parent=value'>
<li ng-repeat='(key, value) in value' ng-include="'template'"></li>
</ul>
{{value | json}}
</div>
Just to add a perhaps more elegant solution: (Note if you update angular recursive directives also becomes a thing)
angular.module('app', ['dotjem.angular.tree']).controller('ctrl', ['$scope', function($scope) {
$scope.value = {
identity: {
name: 'Max',
surName:'Smith',
},
birthDay: {
year: 1990,
month: 2,
day: {
weekDay: 'friday',
number: '13'
}
},
firstLevel: 'abc'
}
$scope.isObject = function(value){
return angular.isObject(value);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://rawgithub.com/dotJEM/angular-tree-bower/master/dotjem-angular-tree.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul dx-start-with="value as parent">
<li ng-repeat="(key, value) in parent">
{{key}}
<input ng-if="!isObject(value)" type="text" ng-model="parent[key]"/>
<ul ng-if="isObject(value)" dx-connect="value"></ul>
</li>
</ul>
<pre>{{value | json}}
</pre>
</div>

Filter data on click function in AngularJS

I have data on some bikes in my HTML page. I have to filter that data via an on click function. I have used a filter in the text box area, but I want the same functionality via an on click function.
So how can I bind the filter with the click function?
http://jsfiddle.net/3G7Kd/114/
<div ng-app='app' class="filters_ct">
<ul class="nav" ng-controller="selectFilter">
<li ng-repeat="filter in filters" ng-click="select($index)" ng-class="{sel: $index == selected}">
<span class="filters_ct_status"></span>
{{filter.name}}
<ul class="subul" ng-if=filter.lists.length>
<li ng-repeat="list in filter.lists" ng-click=" $event.stopPropagation()">
<input type="checkbox"> {{list}}
</li>
</ul>
</li>
</ul>
<input type="text" ng-model="search">
<div ng-controller="listctrl">
<div class="list" ng-repeat="list in lists | filter:{brand:search}">
{{list.brand}}
{{list.year}}
</div>
</div>
</div>
Angular
var app = angular.module('app', []);
app.controller('selectFilter', function($scope) {
$scope.filters = [
{
"name": "brand",
'lists': ['yamaha','ducati','KTM','honda']
},
{
'name': "year",
'lists': [2012,2014,2015]
}
];
$scope.selected = 0;
$scope.select= function(index) {
if ($scope.selected === index)
$scope.selected = null
else
$scope.selected = index;
};
});
app.controller('listctrl', function($scope) {
$scope.lists = [
{
"brand": "ducati",
'year': 2012
},
{
'brand': "honda",
'year': 2014
},
{
'brand': "yamaha",
'year': 2015
},
{
'brand': "KTM",
'year': 2012
}
];
});
You already knew how to use the filter when given an object within the partial. I moved one of your controllers so that you have an outer and an inner controller.
<div ng-app='app'ng-controller="MainCtrl as mainCtrl">
<div ng-controller="listCtrl">
<!-- your filter object is now accessible here -->
</div>
</div>
I added a scope variable to the outer controller $scope.activeFilters (filling this you should be able to do on your own, see the plunker for one possible solution.
This object is now changed when clicking on the checkboxes. As $scope.activeFilters is now accessible from the inner controller we can pass it to the filter as before:
<div class="list" ng-repeat="list in lists | filter:activeFilters">
{{list.brand}}
{{list.year}}
</div>
Note that there are probably nicer solutions (using the checkbox with a model among other things).
Working plunker:
http://jsfiddle.net/ywfrbgoq/

AngularJS ui-bootstrap accordion cannot access variable outside accordion in filtered ng-repeat

I am using AngularJS UI Bootstrap's accordion. I am using ng-repeat with filter inside the accordion. Now the problem is that I cannot access the filtered variable outside <accordion>. If I ditch the accordion and use simple HTML it works fine.
<div ng-app="myApp">
<div ng-controller="AccordionCtrl">
<div>outside accordion{{filteredCampaigns}}</div>
<input type="text" ng-model="q" placeholder="filter" />
<accordion class="accordion" close-others="true">
<div>inside accordion{{filteredCampaigns}}</div>
<accordion-group ng-repeat="campaign in filteredCampaigns=(campaigns | filter:q)">
<accordion-heading>
<h3>{{campaign.title}}</h3>
</accordion-heading>
<p>{{campaign.content}}</p>
</accordion-group>
</accordion>
</div>
</div>
<script>
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('AccordionCtrl', ['$scope',
function ($scope) {
$scope.campaigns = [{
title: "Test1",
content: "file1.html"
}, {
title: "Test2",
content: "file2.html"
}, {
title: "Test3",
content: "file3.html"
}];
}]);
</script>
I have also created the fiddle here
This is becuase the sub-directives comes with angular-ui have child scopes that does not reflect this new filteredCampaigns variable to the controller's scope.
1st option - Hack it,
1) define an object to contain the filteredCampaigns dynamic variable
$scope.context = {};
2) change your accordion-group to:
<accordion-group ng-repeat="campaign in context.filteredCampaigns=(campaigns | filter:q)">
http://jsfiddle.net/Lryuvm9m/1/
<script>
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('AccordionCtrl', ['$scope',
function ($scope) {
$scope.context = {};
$scope.campaigns = [{
title: "Test1",
content: "file1.html"
}, {
title: "Test2",
content: "file2.html"
}, {
title: "Test3",
content: "file3.html"
}];
}]);
</script>
<div ng-app="myApp">
<div ng-controller="AccordionCtrl">
<div>outside accordion {{context.filteredCampaigns}} </div>
<input type="text" ng-model="q" placeholder="filter" />
<accordion class="accordion" close-others="true">
<div>inside accordion{{context.filteredCampaigns}}</div>
<accordion-group ng-repeat="campaign in context.filteredCampaigns=(campaigns | filter:q)">
<accordion-heading>
<h3>{{campaign.title}}</h3>
</accordion-heading>
<p>{{campaign.content}}</p>
</accordion-group>
</accordion>
</div>
</div>
2nd option - use Controller as,
i would recommend using Controller as since it will give you more controll of what's defined on the controller's scope and what's not
http://jsfiddle.net/gntt6h5b/
<script>
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('AccordionCtrl', ['$scope',
function () {
var vm = this;
vm.campaigns = [{
title: "Test1",
content: "file1.html"
}, {
title: "Test2",
content: "file2.html"
}, {
title: "Test3",
content: "file3.html"
}];
}]);
</script>
<div ng-app="myApp">
<div ng-controller="AccordionCtrl as vm">
<div>outside accordion {{vm.filteredCampaigns}} </div>
<input type="text" ng-model="q" placeholder="filter" />
<accordion class="accordion" close-others="true">
<div>inside accordion{{vm.filteredCampaigns}}</div>
<accordion-group ng-repeat="campaign in vm.filteredCampaigns=(vm.campaigns | filter:q)">
<accordion-heading>
<h3>{{campaign.title}}</h3>
</accordion-heading>
<p>{{campaign.content}}</p>
</accordion-group>
</accordion>
</div>
</div>
<accordion> creates an isolated scope and the filteredCampaigns are only available in that scope, not in the parent scope. To add it to the parent scope, you use $parent:
ng-repeat="campaign in $parent.filteredCampaigns=(campaigns | filter:q)">
Another solution would be creating an creating an object an object in the parent scope with a parameter for the filteredCampaings:
// AccordionCtrl
$scope.filteredValues = {
filteredCampaings: []
};
ng-repeat="campaign in filteredValues.filteredCampaigns=(campaigns | filter:q)"
Since you're using it outside of the ng-repeat, you can't see that variable as ng-repeat creates its own scope.
You can use the controller and use the filter manually to retrieve the filtered objects:
$scope.getFilteredCampaings = function () {
$scope.filteredCampaigns = $filter('filter')($scope.campaigns, $scope.q);
return $scope.filteredCampaigns;
}
Fiddle

ng-repeat not working in script block

I need to call a js function when an ng-repeat template is created:
<div ng-repeat="item in items">
<input id="ip{{item.id}}">
<script>$(function () { $('#ip{{item.id}}').kendoDatePicker(); });</script>
</div>
The id is replaced as expected, but angular doesn't seem to work inside script tags.
That is correct, Angular will not evaluate expressions in script tags. You will need to use a directive that will initialize the Kendo plugin for each element.
The good news is Kendo already has a module for integrating with Angular, so you might as well just use that. Here is a plunk I put together showing it in a repeater.
<div ng-repeat="item in items">
<label for="{{item.id}}">{{item.id}}</label>
<div>
<input kendo-date-picker ng-model="item.value" />
</div>
</div>
Controller:
angular.module("demo", ['kendo.directives'])
.controller('DemoCtrl', ['$scope',
function($scope) {
$scope.items = [{
id: 'item1',
value: null
}, {
id: 'item2',
value: null
}, {
id: 'item3',
value: null
}, {
id: 'item4',
value: null
}];
}
]);

Resources