I am looking for a way to synchronize arrays within an AngularJS controller.
Example:
var input = [1];
var synchArray = DatabindToModifiedInput()
// synchArray is something like this:
// [{name:someObject}, {name:inputElement, Id:1}]
input.push(2);
// synchArray should be updated automatically:
// [{name:someObject}, {name:inputElement, Id:1}, {name:inputElement, Id:2}]
Obviously i could register $watches and modify synchArray when input changes but that doesn't feel very angular-like.
Question:
I am tempted to write a filter which i can apply to the input-array. However this still feels like i am missing some obvious way to bind the data together within a controller/service.
Is there some way to utilize ngRepeat or some databinding-mechanism for this? Or should i maybe approach this in a completely different way?
You should likely make an extended array object as demonstrated in this post by Jacob Relkin.
That way you could do more than just one array or event when something happens.
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.myClonedArray = [];
$scope.myExtendedArray;
// Extended array type
function EventedArray(handler) {
this.stack = [];
this.mutationHandler = handler || function() {};
this.setHandler = function(f) {
this.mutationHandler = f;
};
this.callHandler = function(event, obj) {
if (typeof this.mutationHandler === 'function') {
this.mutationHandler(event, obj);
}
};
this.push = function(obj) {
this.stack.push(obj);
this.callHandler('push', obj);
};
this.pop = function() {
var obj = this.stack.pop();
this.callHandler('pop', obj);
return obj;
};
this.getArray = function() {
return this.stack;
}
}
var handler = function(event, item) {
console.log(event, item);
if (event === 'push') {
$scope.myClonedArray.push(item);
} else if (event === 'pop') {
$scope.myClonedArray.pop();
}
};
$scope.myExtendedArray = new EventedArray(handler);
//or
// $scope.myExtendedArray = new EventedArray();
// $scope.myExtendedArray.setHandler(handler);
$scope.addItem = function() {
$scope.myExtendedArray.push($scope.inputValue);
};
$scope.popItem = function() {
$scope.myExtendedArray.pop();
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="myApp">
<body ng-controller="MyCtrl">
<input type="text" ng-model="inputValue" />
<button ng-click="addItem()">Add</button>
<button ng-click="popItem()">Pop</button>
<p>Custom Array</p>
<ul>
<li ng-repeat="item in myExtendedArray.stack track by $index">
{{item}}
</li>
</ul>
<p>Cloned Array</p>
<ul>
<li ng-repeat="item in myClonedArray track by $index">
{{item}}
</li>
</ul>
</body>
</html>
Related
I am using angularjs I have two list when I click first one I will push the value into another scope and bind the value to second list. Now my requirement is when first list values which are moved to second list, I need to change the color of moved values in list1
Here I attached my fiddle
Fiddle
You can use findIndex and ng-class together to check if the second list contains the same item as first. If present apply css class to the first list item.
JS:
$scope.checkColor = function(text) {
var index = $scope.linesTwos.findIndex(x => x.text === text);
if (index > -1) return true;
else return false;
}
HTML:
<li ng-click="Team($index,line.text)" ng-class="{'change-color':checkColor(line.text)}">{{line.text}}</li>
Working Demo: https://jsfiddle.net/7MhLd/2659/
You can do something like this:
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.lines = [{
text: 'res1'
},
{
text: 'res2'
},
{
text: 'res3'
}
];
$scope.linesTwos = [];
$scope.Team = function(index, text) {
var obj = {};
obj.text = text;
$scope.linesTwos.push(obj)
}
$scope.Team2 = function(index, text2) {
$scope.linesTwos.splice(index, 1)
}
$scope.containsObj = function(obj, list) {
var i;
for (i = 0; i < list.length; i++) {
if (angular.equals(list[i], obj)) {
return true;
}
}
return false;
};
}
.clicked {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<ul ng-repeat="line in lines">
<li ng-class="{'clicked': containsObj(line,linesTwos)}" ng-click="Team($index,line.text)">{{line.text}}</li>
</ul>
<ul>
<li>__________</li>
</ul>
<ul ng-repeat="line in linesTwos">
<li ng-click="Team2($index,line.text)">{{line.text}}</li>
</ul>
</div>
you have to achieve it using ng-class and create a dynamic class style for pushed data please check my working example fiddle
JS fiddle sample
in HTML nedd to do these changes
<li ng-click="Team($index,line.text,line)" ng-class="{'pushed':line.pushed}">
<li ng-click="Team2($index,line.text,line)">
In css
.pushed{color:red;}
In Controller
`$scope.Team=function(index,text,line){
var obj={};
obj = line;
$scope.linesTwos.push(obj)
line.pushed = true;
}`
`scope.Team2 = function(index,text2,line){
$scope.linesTwos.splice(index,1)
line.pushed = false;
}
`
its because angular two way binding
I am trying to do an edit field in angular js but I don't know how to do that one help me
below is my Crud operation code
var app = angular.module('myApp', [])
app.controller('myCtrl', ['$scope', function($scope) {
$scope.products = ["venu", "balaji", "suresh"];
$scope.addItem = function() {
$scope.errortext = "";
if (!$scope.addMe) {
return;
}
if ($scope.products.indexOf($scope.addMe) == -1) {
$scope.products.push($scope.addMe)
} else {
$scope.errortext = "The item is already in your names list.";
}
}
$scope.removeItem = function(x) {
$scope.errortext = "";
$scope.products.splice(x, 1);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<ul>
<li ng-repeat="x in products">{{x}}<span ng-click="removeItem($index)">×</span>
</li>
</ul>
<input ng-model="addMe">
<button ng-click="addItem()">ADD</button>
<p>{{errortext}}</p>
<p>Try to add the same name twice, and you will get an error message.</p>
</div>
</div>
I am doing crud operations in angular js. i have done Delete and Add but I dont know how to do Edit operation in angular js
var app = angular.module('myApp', [])
app.controller('myCtrl', ['$scope', function($scope) {
$scope.products = ["venu", "balaji", "suresh"];
$scope.addItem = function() {
$scope.errortext = "";
if (!$scope.addMe) {
return;
}
if ($scope.products.indexOf($scope.addMe) == -1) {
$scope.products.push($scope.addMe)
} else {
$scope.errortext = "The item is already in your names list.";
}
$scope.addMe = "";
}
$scope.removeItem = function(x) {
$scope.errortext = "";
$scope.products.splice(x, 1);
}
$scope.edit = function(index){
$scope.addMe = $scope.products[index];
$scope.products.splice(index, 1);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<ul>
<li ng-repeat="x in products">{{x}}
<span ng-click="removeItem($index)">×</span>
<span style="color:blue;cursor:pointer;" ng-click="edit($index)">Edit</span>
</li>
</ul>
<input ng-model="addMe">
<button ng-click="addItem()">ADD</button>
<p>{{errortext}}</p>
<p>Try to add the same name twice, and you will get an error message.</p>
</div>
</div>
Try this.
The solution is here:
HTML
<li ng-repeat="x in products" ng-click="preEdit(x, $index)">{{x}}<span ng-click="removeItem($index)">×</span></li>
<input ng-model="addMe">
<button ng-if="isAdd" ng-click="addItem()">ADD</button>
<button ng-if="!isAdd" ng-click="editItem()">EDIT</button>
JS
$scope.isAdd = true;
$scope.preEdit = preEdit;
var index = '';
function preEdit(x, i){
$scope.addMe = x;
index = i;
$scope.isAdd = false;
}
$scope.editItem = editItem ;
function editItem (){
$scope.products[index] = $scope.addMe;
$scope.isAdd = true;
$scope.addMe = '';
index = '';
}
Look my solution in filddle: https://jsfiddle.net/tfx8njw6/
At first you need to add a edit option on the <li> say,
<ul>
<li ng-repeat="x in products">{{x}}
<span ng-click="removeItem($index)">×</span>
<span ng-click="editItem($index)">Edit</span>
</li>
</ul>
Then add a controller function for edit editItem() like
$scope.editItem= function(index) {
$scope.addMe = $scope.products[index];
//this variable will allow you to check whether the operation is an edit or add
$scope.editIndex = index;
}
You can then reuse your addItem() function like this
$scope.addItem = function() {
$scope.errortext = "";
if (!$scope.addMe) {
return;
}
if(angular.isDefined($scope.editIndex)){
$scope.products[$scope.editIndex] = $scope.addMe;
//reset the variable to undefined after using it
$scope.editIndex = undefined;
}else{
if ($scope.products.indexOf($scope.addMe) == -1) {
$scope.products.push($scope.addMe);
} else {
$scope.errortext = "The item is already in your names list.";
}
}
}
If you want to take it to the "next level", check out xeditable. :)
https://vitalets.github.io/angular-xeditable/#text-simple
Good luck!
For Edit what you can do:
0.Pass the id on edit click and get data of that id and assign to the same variable you have used for add and hide add button and show update button.
1.Either use the same text field which you are using for add and show/hide button add/update accordingly.
2.You can use separately div which includes one text box and button to update.Show/hide as per you action.
Maybe this question was asked before, but i tried the solutions i saw in other posts such as disabling the fast watch, but nothing seems to work...
I have a grid using Angular ui-grid in my web page and the behavior i'm seeking for is that after click on a button the data must be updated. The issue is i can see that gridOptions.data is updated, the columnDefs too, even the length but view doesn't update, also the displaying becomes a bit messy and i have to scroll to get it right.
Here's my code
Controller :
(function(app) {
'use strict';
app.controller('Ctrl', Ctrl);
function Ctrl($scope, $routeSegment, $log, $filter) {
$log.debug('Controller - init()');
// Set ViewModel Object - will be exported in View
var viewModel = this;
viewModel.gridOptionsData = [];
viewModel.gridOptions = {};
viewModel.gridOptions.paginationPageSizes = [25, 50, 75];
viewModel.gridOptions.paginationPageSize = 25;
viewModel.gridOptions.data = [];
viewModel.gridOptions.rowIdentity = function(row) {
return row.id;
};
viewModel.gridOptions.getRowIdentity = function(row) {
return row.id;
};
$scope.showgrid = false;
$scope.filterText;
viewModel.refreshData = function() {
viewModel.gridOptions.data = $filter('filter')(viewModel.gridOptionsData, $scope.filterText, undefined);
};
$scope.$watch('dataStructure', function(data) {
if (angular.isDefined(data) && !angular.equals(data, [])) {
var struct = [];
for (var key in data[0]) {
if (angular.equals(key, 'id')) {
struct.push({
field: key,
visible : false
});
} else {
struct.push({
field: key,
});
}
}
viewModel.gridOptionsData = data;
viewModel.gridOptions.data = data;
viewModel.gridOptions.columnDefs = struct;
$scope.showgrid = true;
}
});
}
}(angular.module('app')));
View :
<div class="gridStyle" ui-grid="pageViewModel.gridOptions" ui-grid-pagination ></div>
<input type="text" ng-model="$parent.filterText" ng-change="pageViewModel.refreshData()" placeholder="Search / Filter" />
Appreciate your help
In my javascript file i have mix of knockout and jquery which contains two different view models and i am having trouble displaying the results:
Javascript:
POSITION ViewModel
var positionViewModel = function (data) {
var _self = this;
_self.PositionName = ko.observable(data.PositionName);
_self.PositionRank = ko.observable(data.PositionRank);
_self.ContentRole = ko.observable(data.ContentRole);
}
positionViewModel.AddPositions = function (data) {
$.each(data, function (index, value) {
positionViewModel.PushPosition(value);
});
};
positionViewModel.PushPosition = function (postion) {
viewModel.PositionTypes.push(new positionViewModel(position));
};
USER ViewModel
// the ViewModel for a single User
var userViewModel = function (data) {
var _self = this;
_self.ID = ko.observable(data.ID);
_self.Name = ko.observable(data.Name);
_self.Email = ko.observable(data.Email);
_self.ContentRole = ko.observable(data.ContentRole);
};
userViewModel.AddUsers = function (data) {
$.each(data, function (index, value) {
userViewModel.PushUser(value);
});
};
userViewModel.PushUser = function (user) {
viewModel.Users.push(new userViewModel(user));
};
Positions and Users
ko.utils.arrayForEach(viewModel.PositionTypes(), function(position){
var usersInPosition = ko.utils.arrayFilter(viewModel.Users(), function(user){
return user.ContentRole() == position.ContentRole();
});
ko.utils.arrayForEach(usersInPosition, function(user){
});
});
Binding
// Binds the main ViewModel
var bindModel = function (data) {
var _self = viewModel;
viewModel.TotalUser = ko.computed(function () {
return _self.Users().length;
});
userViewModel.AddUsers(data);
ko.applyBindings(viewModel, $('#UserView')[0]);
};
View Page
<ul data-bind="foreach:PositionTypes">
<li>
<div>
<span data-bind="text:PositionName"></span>
</div>
<ul data-bind="template: { name: 'grid', foreach: Users}">
</ul>
</li>
</ul>
Result example:
CEO
James
Vice President
John
Workers
Amy
Betsy
How can i alter my view to properly display results from javascript file?
So your architecture was wrong to start out. In your example you are showing a list of type Position and each Position has another list of type User. I have whipped up a fiddle with the correct architecture for you to be able to add on whatever functionality you need. I would seriously look into the knockout documentation as well as design a little bit before you start coding.
http://jsfiddle.net/zBmSN/1/
In my view I am looping through an observableArray (itemGroup) that has one property that is also an observableArray (item). I have a method to remove an entire itemGroup and one to remove an item from and itemGroup but I would like to add in some logic along the lines of it there is only 1 item left in the group removing that item should also remove the itemGroup.
here is an example of the relevant parts of my view model and view.
my JS
var ItemModel = function(item) {
var self = this;
self.name = ko.observable(item.name);
self.price = ko.observable(item.price);
};
var ItemGroupModel = function(itemGroup) {
var self = this;
self.order = ko.observable(itemGroup.order);
self.items = ko.observableArray(ko.utils.arrayMap(itemGroup.items, function(item){
return new ItemModel(item);
}));
self.type = ko.observable(item.type);
self.removeItem = function(item) {
self.items.remove(item);
}
};
var ViewModel = function(data) {
var self = this;
self.itemGroups = ko.observableArray(ko.utils.arrayMap(data.itemGroups, function(itemGroup) {
return new ItemGroupModel(item);
}));
// some other properties and methods
self.removeItemGroup = function(itemGroup) {
self.itemGroups.remove(itemGroup);
}
};
My View
<ul data-bind="foreach: {data: VM.itemGroups, as: 'itemGroup'}">
<li>
<button data-bind="click: $root.VM.removeItemGroup">X</button>
<ul data-bind="foreach: {data: itemGroup.items, as: 'item'}">
<li>
<!-- ko if: itemGroup.items().length > 1 -->
<button data-bind="click: itemGroup.removeItem">X</button>
<!-- /ko -->
<!-- ko ifnot: itemGroup.items().length > 1 -->
<button data-bind="click: function () { $root.VM.removeItemGroup($parent) }">X</button>
<!-- /ko -->
</li>
</ul>
</li>
</ul>
This works but to me it isnt ideal. It is my understanding that knockout should help me get away from using an anonymous function like "function () { $root.VM.removeItemGroup($parent) }" but I am not sure how to do it another way. Also removing the if and ifnot statements would be good to clean up as well.
I would like to give my solution
send index of itemGroups and items as argument to remove method.
Hope you know how to send index
Then check the length of itemGroups
self.remove(itemGroupsIndex,itemsIndex) {
var itemGroupsLength = self.itemGroups()[itemGroupsIndex].items().length;
if(itemGroupsLength = 1) {
self.itemGroups.remove(itemGroupsIndex);
}
else {
self.itemGroups()[itemGroupsIndex].items.remove(itemsIndex);
}
};