Angular record the order of checkboxes as clicked - angularjs

I am trying to add the functionality in my angular application to record the order of the checkboxes as they are clicked and hence show them in same order as they were clicked.
Here is the snippet:
var app = angular.module('checkbox', []);
app.controller('homeCtrl', function ($scope) {
$scope.array = [1, 5];
$scope.array_ = angular.copy($scope.array);
$scope.list = [{
"id": 1,
"value": "apple",
}, {
"id": 3,
"value": "orange",
}, {
"id": 5,
"value": "pear"
}];
$scope.update = function () {
if ($scope.array.toString() !== $scope.array_.toString()) {
return "Changed";
} else {
return "Not Changed";
}
};
})
});
In this snippet array should show the numbers in the order they were clicked.

Not sure if I understood exactly what you need but please check this Plunker
The idea is to use a function like this to record the click order:
$scope.onClick = function(box) {
var index = $scope.model.clickOrder.indexOf(box);
if (index !== -1) {
$scope.model.clickOrder.splice(index, 1);
} else {
$scope.model.clickOrder.push(box);
}
updateOrderedList();
};
And a function like this to update the checkboxes order:
var updateOrderedList = function() {
$scope.model.orderedList = []
.concat($scope.model.clickOrder)
.concat($scope.model.list.filter(function(item) {
return $scope.model.clickOrder.indexOf(item) === -1;
}));
};

Related

AngularRest API wont return JSON property

I have 3 components, the web api, the controller, and the html. I can hit the web api just fine and i get back the results in JSON format, but when it then tries to render the JSON into the html, it looks like this.
{
"Projects": [{
"ProjectId": 1,
"Name": "Project1",
"Key": "Software",
"ProjectTypeKey": "1"
}, {
"ProjectId": 2,
"Name": "Project2",
"Key": "Hardware",
"ProjectTypeKey": "2"
}, {
"ProjectId": 3,
"Name": "Project3",
"Key": "Hardware",
"ProjectTypeKey": "3"
}]
}
WebApi
public IHttpActionResult Get()
{
listProjects.Add(new Project { ProjectId = 1, Name = "Project1", Key = "Software", ProjectTypeKey = "1" });
listProjects.Add(new Project { ProjectId = 2, Name = "Project2", Key = "Hardware", ProjectTypeKey = "2" });
listEmployeeProject.Add(new EmployeeProject {Projects = listProjects });
return Json(listEmployeeProject);
}
Controller
var myApp = angular.module('myApp', []);
myApp.service('dataService', function ($http) {
this.getData = function () {
// $http() returns a $promise that we can add handlers with .then()
return $http({
method: 'GET',
url: '/api/employee'
});
}
});
myApp.controller('ProjectController', function ($scope, dataService) {
$scope.Projects = [];
dataService.getData().then(function (result) {
$scope.Projects = result.data;
});
});
HTML
<div ng-app="myApp" ng-controller="ProjectController">
{{1 + 1}}
<div ng-repeat="project in Projects">
{{project}}
</div>
Even when i switch {{project}} to {{project.Name}}, nothing renders on the page.
console.log(results.data) looks like below
Its very clear from your console that you are returning an array of length 1 which has another array of length 3 in it
This is because of this line in your code
listEmployeeProject.Add(new EmployeeProject {Projects = listProjects });
Here you are retuning a EmployeeProject array and each element of that array has multiple projects. So do either of these things
a. Return listProjects like return Json(listProjects) (You should be returning Ok(model) ideally)
b. Or in angular promise do,
$scope.Projects = result.data[0].Projects;

Saving simple user input in angular JS

I have built this simple todo app, however it resets the data every time the users page is reset, how can I save the data in the checklist list upon page refresh so the user can reference it later? Can this be done within the app or do I need a server database setup?
var checkList = angular.module('checkList', []);
checkList.filter('checkedItems', function () {
return function (items, showComplete) {
var resultArr = [];
angular.forEach(items, function (item) {
if (item.done == false || showComplete == true) {
resultArr.push(item);
}
});
return resultArr;
}
});
checkList.controller('CheckListCtrl', function($scope){
$scope.check = {
user: "Jim",
items: [ {action: "item1", done: false },
{action: "item2", done: false },
{action: "item3", done: false },
{action: "item4", done: false }]
};
$scope.incompleteCount = function () {
var count = 0;
angular.forEach($scope.check.items, function (item) {
if (!item.done) { count++ }
});
return count;
}
$scope.warningLevel = function () {
return $scope.incompleteCount()
< 3 ? "label-success" : "label-warning";
}
$scope.addNewItem = function (actionText) {
$scope.check.items.push({ action: actionText, done: false });
}
});
The easiest way is:
To save data in browser's local storage: localStorage.setItem('itemName', data) and then, to retrieve it back localStorage.getItem('itemName', data)

How do you use a recursive function within a directive in angularJS?

I'd like to create a ToDo app in AngularJS that uses nested ToDos. As part of this, I'd like the ability to indent or outdent a ToDo with a click (ultimately, it will react to a 'tab' or 'shift-tab' keypress). When indenting, the function should also indent any child ToDos. This is where I'm having trouble. I'm not sure how to implement a recursive function in a directive. If anyone has seen this done or could offer any help, i would appreciate it. I've created a working JSFiddle that indents a single ToDo but doesn't indent it's children. JS Fiddle is here https://jsfiddle.net/t1ba50k6/16/
Also, please offer any best practices on coding convention or to clean up the directive.
Thanks!
My HTML:
<ul id=Todos ng-app="app" ng-controller="todos">
<div ng-repeat='todo in model' ng-init="todoIndex = $index">
<todo-item content="todo"></todo-item>
</div>
My javascript:
var app = angular.module('app', []);
app.controller("todos", function ($scope) {
$scope.model = [{
"title": "Task item one",
"indentLevel": 1
}, {
"title": "Task item two",
"indentLevel": 2
}, {
"title": "Task item three",
"indentLevel": 2
}];
});
app.directive('todoItem', function ($compile) {
var getTemplate = function (content) {
var startStr = '';
var endStr = '';
if (content.indentLevel == 1) {
startStr = '<li class="todolist"><table><tr><td><div class="checkboxpadding"><img width="16" height="16" class="checkbox"></div></td><td><div ng-click="indent()">';
endStr = '</div></td></tr></table></li>';
return startStr + content.title + endStr;
} else if (content.indentLevel == 2) {
startStr = '<li class="todolist indent_2""><table><tr><td><div class="checkboxpadding"><img width="16" height="16" class="checkbox"></div></td><td><div ng-click="indent()">';
endStr = '</div></td></tr></table></li>';
return startStr + content.title + endStr;
}
};
var linker = function (scope, element, attrs) {
element.html(getTemplate(scope.content)).show();
$compile(element.contents())(scope);
scope.indent = function () {
var childElem = angular.element(element.children()[0]);
if (scope.content.indentLevel < 3) {
childElem.removeClass("indent_" + scope.content.indentLevel);
childElem.addClass("indent_" + (scope.content.indentLevel + 1));
scope.content.indentLevel++;
// update Database
// indent child nodes?
// for (var i=0; i < nodes; i++) { }
}
};
};
return {
restrict: "E",
link: linker,
scope: {
content: '='
}
};
});
Mapping your data to a nested structure would greatly simplify this.
Always use the same property names for the child arrays:
$scope.model = [{
"title": "Task item one",
"indentLevel": 1,
"children": [
{
"title": "Task item two",
"indentLevel": 2,
"children":[]
}, {
"title": "Task item three",
"indentLevel": 2,
"children":[]
}
]
}];
One solution is using $broadcast(name, args) and $on(name, listener) of scope.
when cliked fire an event:
scope.$broadcast('indent');
in child directives,
scope.$on('indent', scope.indent);

$rootScope.$apply() ignores variable assignment

In my Service I have this code:
setInterval(function () {
$rootScope.$apply(function () {
cart.push({
"Id": 4,
"Name": "Some item",
"Price": 4
});
});
}, 3000);
Working example: http://jsfiddle.net/ko8edf32/7/
This works correctly: the cart change is pushed to the view.
However, when I assign a new value to cart, the change is NOT pushed to the view:
setInterval(function () {
$rootScope.$apply(function () {
var newcart = [{
"Id": 4,
"Name": "Some item " + Math.random(),
"Price": 4
}];
cart = newcart;
});
}, 3000);
example: http://jsfiddle.net/ko8edf32/8/
What is the reason of this?
What is the best/most elegant solution to solve this issue?
EDIT
This is the working solution I've built, based on the answers below:
jsfiddle.net/ko8edf32/11
I've changed your cart into more "angular" way with two-way databinding. I've added the second controller to show you how nice it works all together without getters/setters and generally with a bit of magic
http://plnkr.co/edit/U5pUlAPJTNd9V0baSjAu?p=preview
homeApp.factory("cartService", function($rootScope) {
var service = null
service = {
all: function() {
return service.cart;
},
add: function(item) {
service.total += item.Price
service.cart.push(item);
},
remove: function(item) {
service.total -= item.Price
service.cart.splice(service.cart.indexOf(item), 1);
},
cartUpdated: function(newValue) {
service.cart = newValue;
},
cart: [],
total: 0
}
return service;
});
You have to use objects on $scope.
$scope.model = { cart: [] };
Here is a working example: http://jsfiddle.net/56vkqygn/2/
Here is a explanation: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

How to manually add a datasource to Angularjs-select2 when select2 is set up as <input/> to load remote data?

I am using the Select2 as a typeahead control. The code below works very well when the user types in the search term.
However, when loading data into the page, I need to be able to manually set the value of the search box.
Ideally something like: $scope.selectedProducerId = {id:1, text:"Existing producer}
However, since no data has been retrieved the Select2 data source is empty.
So what I really need to be able to do is to add a new array of data to the datasource and then set the $scope.selectedProducerId, something like: $scope.producersLookupsSelectOptions.addNewData({id:1, text:"Existing producer}) and then
$scope.selectedProducerId = 1;
Researching this I have seen various suggestions to use initSelection(), but I can't see how to get this to work.
I have also tried to set createSearchChoice(term), but the term is not appearing in the input box.
I would be most grateful for any assistance.
Thanks
This is the html
<div class="col-sm-4">
<input type="text" ui-select2="producersLookupsSelectOptions" ng- model="selectedProducerId" class="form-control" placeholder="[Produtor]" ng-change="selectedProducerIdChanged()"/>
</div>
This is the controller
angular.module("home").controller("TestLookupsCtrl", [
"$scope", "$routeParams", "AddressBookService",
function($scope, $routeParams, AddressBookService) {
$scope.producersLookupsSelectOptions = AddressBookService.producersLookupsSelectOptions();
}
]);
This is the service:
angular.module("addressBook").service("AddressBookService", [
"$http", "$q", function($http, $q) {
var routePrefix = "/api/apiAddressBook/";
//var fetchProducers = function(queryParams) {
// return $http.get(routePrefix + "GetClientsLookup/" + queryParams.data.query).then(queryParams.success);
//};
var _getSelectLookupOptions = function(url, minimumInputLength, idField, textField) {
var _dataSource = [];
var _queryParams;
return {
allowClear: true,
minimumInputLength: minimumInputLength || 3,
ajax: {
data: function(term, page) {
return {
query: term
};
},
quietMillis: 500,
transport: function(queryParams) {
_queryParams = queryParams;
return $http.get(url + queryParams.data.query).success(queryParams.success);
},
results: function(data, page) {
var firstItem = data[0];
if (firstItem) {
if (!firstItem[idField]) {
throw "[id] " + idField + " does not exist in datasource";
}
if (!firstItem[textField]) {
throw "[text] " + textField + " field does not exist in datasource";
}
}
var arr = [];
_.each(data, function(returnedData) {
arr.push({
id: returnedData[idField],
text: returnedData[textField],
data: returnedData
});
});
_dataSource = arr;
return { results: arr };
}
},
dataSource: function() {
return _dataSource;
},
getText: function (id) {
if (_dataSource.length === 0) {
throw ("AddressBookService.getText(): Since the control was not automatically loaded the dataSource has no content");
}
return _.find(_dataSource, { id: id }).text;
}
//initSelection: function(element, callback) {
// callback($(element).data('$ngModelController').$modelValue);
//},
//createSearchChoice:function(term) {
// return term;
//},
addNewData:function(data) {
this.ajax.results(data,1);
};
};
return {
producersLookupsSelectOptions: function() {
var url = routePrefix + "GetClientsLookup/";
return _getSelectLookupOptions(url, 2, "Id", "Name");
},
}
}
]);

Resources