Knockout Clone Whole Item In foreach - html

I am trying to clone elements when clicking a button. I was trying to use ko.toJS. On page load it works fine, but when I want clone the items, it is unable to bind the items (like, value, Text, etc.).
Here is the HTML:
<div class="stockItems-inner" data-bind="foreach: StockItems">
<div data-bind="if: Type=='Input'">
<div class="stock_container_input">
<input type="text" data-bind="value: Value" />
</div>
</div>
<div data-bind="if: Type=='Radio'">
<div class="stock_container_control">
<div data-bind="foreach: Options">
<div class="stockLbl">
<input type="radio" data-bind="text: Text, checked:$parent.Value, attr:{'id':Id, 'name': $parent.Text, 'value': Value}" />
<label data-bind="attr:{'for':Id}, text: Text"></label>
</div>
</div>
</div>
</div>
</div>
<div class="addItem">
<button type="button" data-bind="click: CloneItem"><img src="images/add.png" alt="" /></button>
</div>
The View Model:
ConfigurationStockViewModel = function() {
var self = this;
this.StockItems = ko.observableArray();
this.ApplyData = function(data){
self.StockItems(data.Items);
}
this.CloneItem = function(StockItems){
self.StockItems.push(ko.toJS(StockItems));
};
};
When clicking the button, an error is thrown: Unable to process binding. I am using JSON data for binding.

Not exactly sure what end result you want without working code, but sounds like you want to clone the last item in array and add to array?
If so, I think you have an error - your add button click binding will never pass anything to the function you defined, since it is outside the foreach. You need something like this:
this.CloneItem = function() {
var toClone = self.StockItems()[self.StockItems().length - 1]
self.StockItems.push(toClone);
};
Here is a simplified example without radio buttons, etc:
http://jsfiddle.net/5J47L/

Related

Ng-show will not update when ng-click is inside div

I have a DIV with ng-show.
When I run ng-click on an element outside of the DIV, it works fine and I can hide it.
When I run ng-click on an element inside of the DIV, it does not work. I can see the variable beeing changed when i console.log it, but the view will not update.
I have tried to use $scope.$apply() but it gets an error and says it is already running $apply().
Parts of controller:
$scope.selectedActivity = {
"dayNr": 0,
"actNr": 0
};
$scope.resetSelectedActivity = function () {
console.log("SelAct: ", $scope.selectedActivity);
$scope.selectedActivity.dayNr = -1;
$scope.selectedActivity.actNr = -1;
console.log("SelAct: ", $scope.selectedActivity);
};
$scope.setSelectedActivity = function (dayNr, actNr) {
console.log("SelAct: ", $scope.selectedActivity);
$scope.selectedActivity.dayNr = dayNr;
$scope.selectedActivity.actNr = actNr;
console.log("SelAct: ", $scope.selectedActivity);
};
Parts of HTML:
<div ng-repeat="x in xs">
<ion-scroll>
<div ng-repeat="y in ys track by $index">
<div ng-click="setSelectedActivity($parent.$index, $index)">
<!--THE PROBLEM IS HERE-->
<div ng-show="selectedActivity.dayNr == $parent.$index && selectedActivity.actNr == $index">
<div>
<!--THIS LOGS OUT CORRECT VALUES BUT NG-SHOW IS NOT UPDATED-->
<div ng-click="resetSelectedActivity()">
Reset
</div>
</div>
</div>
<div>
<img src="img/checkButtonOverlay.png" />
</div>
</div>
<!--THIS LOGS OUT CORRECT VALUES AND NG-SHOW _IS_ UPDATED-->
<button ng-click="resetSelectedActivity()">reset</button>
</div>
</ion-scroll>
</div>
Please note that i have removed A LOT from the code because of confidentiality, but the principle should be the same.
Thank you!
Found the problem!
I had a ng-click that showed the DIV outside. When I clicked both ng-clicks got entered.
So First resetSelectedActivity() and then it got set again in setSelectedActivity().
Fixed it using:
<div ng-click="resetSelectedActivity($parent.$index, $index, $event)">
...
</div>
and:
$scope.setSelectedActivity = function (dayNr, actNr, event) {
$scope.selectedActivity.dayNr = dayNr;
$scope.selectedActivity.actNr = actNr;
//This cancel the mouseclick
event.stopPropagation();
};

Angular html not loading properly

Sorry if this has a really obvious answer, I'm fairly new to Angular JS and this is something that I have been stuck on for an annoyingly long time.
To give you a bit of background, I am calling ng-repeat on a directive as follows:
<div ng-controller = 'salesctrl'>
<salecell ng-repeat = "kpi in kpis" kpi ="kpi"></salecell>
</div>
With the directive being described as follows:
.directive("salecell", function(){
return{
templateUrl: "sale-cell-template.html" ,
restrict: 'E',
scope: {
kpi: '='
},
link: function(scope){
return scope;
},
controller: function($scope){
if(typeof $scope.kpi != 'undefined'){
$scope.kpi.value = 0;
$scope.increment = function(){
$scope.kpi.value++;
}
$scope.decrement = function(){
if($scope.kpi.value != 0){
$scope.kpi.value--;
}
}
}
}
};
})
and the attached controller:
.controller("salesctrl", function($scope, $rootScope, SalesSvc){
SalesSvc.query().$promise.then(function(data){
$scope.kpis = data;
});
$scope.submit = function(){
SalesSvc.save($scope.kpis).$promise.then(function(){
for(var i = 0; i < $scope.kpis.length; i++){
$scope.kpis[i].value = 0;
}
});
$rootScope.$emit('salecreate');
}
})
The issue that I am having is that, regardless of the contents of my associated template, only the outer element is being rendered. For example if I have:
<tr>
<div class="col-md-6"> <label class="control-label">{{kpi.title}}</label></div>
<div ng-show="!kpi.dollar" class="col-md-6">
<div id="spinner4">
<div class="input-group" style="width:150px;">
<div class="spinner-buttons input-group-btn">
<button ng-click="decrement()" type="button" class="btn spinner-up btn-warning">
<i class="fa fa-minus"></i>
</button>
</div>
<input ng-model="kpi.value" type="text" class="spinner-input form-control" maxlength="3" >
<div class="spinner-buttons input-group-btn">
<button ng-click="increment()" type="button" class="btn spinner-down btn-primary">
<i class="fa fa-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div ng-show="kpi.dollar" class="col-md-6">
<div class="input-group m-bot15" style="width: 150px;">
<span class="input-group-addon">$</span>
<input ng-model="kpi.value" type="text" class="form-control">
</div>
</div>
Nothing will load on the page, and likewise, if I have:
<tr>
<h1> Hello World! </h1>
</tr>
Nothing is loaded either.
Things I have already checked:
-I have made sure that both $scope.kpis and $scope.kpi are defined
-I have ensured that the template is actually being called (both by inspecting elements and by calling a console.log from within the template)
-A bit of searching around suggested that it might be an error within the template, but this seems strange given that it doesn't work even with a near-empty template.
The only other thing that I can think to add is that when I was using console.log in the template that was visible in the element inspector (Chrome), but nothing else has been.
Let me know if you need anything else, and once again I hope this isn't something really stupid that I have missed.
Ensure that the content inside the table are wrapped in <td> tags. Currently they are directly inside <tr> tags and is invalid HTML.
<tr>
<td>
<div class="col-md-6"> <label class="control-label">{{kpi.title}}</label></div>
...
</td>
</tr>

show a div on a button click using angularjs and HTML

I want to show a div on a button click using angularjs and HTML
I am learning angularJs.
I want to design a webpage in which on click of 'Add' button a div(EmployeeInfoDiv) is shown.
When I fill the textboxes in it and save it the data should reflect in table within the div(EmpDetTable).
Here is the HTML page code:
<body ng-app="MyApp">
<div ng-controller="EmpDetCtrl">
<div ng-model="EmpDetTable" class="container body" ng-hide="addEmp">
<label class="TabHeadings">Employee Details</label>
<div class="EmployeeInfo">
<table ng-model="Employee" border="1">
<thead><tr><th>Name</th><th>Designation</th><th>Salary</th></tr></thead>
<tbody>
<tr ng-repeat="emp in employees">
<td>{{emp.name}}</td>
<td>{{emp.desg}}</td>
</tr>
</tbody>
</table>
<button ng-model="AddEmp" ng-click="ShowAddEmployee()">Add</button>
</div>
</div>
<div ng-model="EmployeeInfoDiv" class="popover right EmployeeInfo" style="width:1500px;" ng-show="addEmp" >
<label class="TabHeadings" style="width:830px;">Employee Details</label>
<div class="EmployeeInfo">
<table>
<tr><td><label class="table_label">Name:</label></td><td><input type="text" ng-model="name" class="textbox"/></td>
<td><label class="table_label">Designation:</label></td><td><input type="text" ng-model="desg" class="textbox"/></td>
</tr>
</table>
<div ng-model="botton_container">
<button ng-model="save" class="save_buttons" ng-click="SaveData();">Save</button>
<button ng-model="cancel" class="cancel_buttons" ng-click="ShowViewEmployee();">Cancel</button>
</div>
</div>
</div>
</div>
</body>
Controller Code:
function EmpDetCtrl($scope) {
$scope.employees = [
{ name: 'A',desg: 'C'}
];
$scope.addNew = function () {
$scope.employees.push({
name: $scope.name,desg: $scope.desg
});
}
$scope.ShowAddEmployee= function () {
return $scope.EmployeeInfoDiv=true;
}
$scope.ShowViewEmployee=function () {
return $scope.EmployeeInfoDiv = false;
}
}
I don't know whether this code is correct or not.
I just tried it.
Can anyone please help me out.
Thanks in advance.
Your ng-click is updating the EmployeeInfoDiv variable. So, reference that in ng-show and ng-hide:
<div ng-hide="EmployeeInfoDiv" class="container body">
...
<div ng-show="EmployeeInfoDiv" class="popover right EmployeeInfo" style="width:1500px;">
You do not need ng-model in the div to make that work.
ng-model="EmployeeInfoDiv"
Update
A few more issues. Most important is that (at least in the code posted) MyApp isn't defined anywhere, and your controller is defined on the global scope. So you need to change your html to:
<div ng-app>
<div ng-controller="EmpDetCtrl">
...
However, note that this is not the recommended method. See the angular documentation for controllers.
Here is a working demo: http://jsfiddle.net/wittwerj/xrB77/

Angular JS - Having an empty array

I have a form that submits data into a function called ng-submit="newBirthday() this pushes data - $scope.bdayname, $scope.bdaydate; into an array called bdaes
My issue is that with all of the tutorials I have seen the array has predefined data is there a way that it can be an empty array that gets filled with data when it is submitted?
app.js:
var app = angular.module('birthdayToDo', []);
app.controller('main', function($scope){
// Start as not visible but when button is tapped it will show as true
$scope.visible = false;
// Create the array to hold the list of Birthdays
$scope.bdays = [{}];
// Create the function to push the data into the "bdays" array
$scope.newBirthday = function(){
$scope.bdays.push({name:$scope.bdayname, date:$scope.bdaydate});
$scope.bdayname = '';
$scope.bdaydate = '';
}
});
HTML:
<body ng-app="birthdayToDo" ng-controller="main">
<div id="wrap">
<!-- Begin page content -->
<div class="container">
<div class="page-header">
<h1>Birthday Reminders</h1>
</div>
<ul ng-repeat="bdays in bdays">
<li>{{bdae.name}} | {{bdae.date}}</li>
</ul>
<form ng-show="visible" ng-submit="newBirthday()">
<label>Name:</label>
<input type="text" ng-model="bdayname" placeholder="Name"/>
<label>Date:</label>
<input type="date" ng-model="bdaydate" placeholder="Date"/>
<button class="btn" type="submit">Save</button>
</form>
</div>
<div id="push"></div>
</div>
<div id="footer">
<div class="container">
<a class="btn" ng-click="visible = true"><i class="icon-plus"></i>Add</a>
</div>
</div>
<script type="text/javascript" src="js/cordova-2.5.0.js"></script>
<script type="text/javascript">
app.initialize();
</script>
</body>
Okay, there were a few of small issues.
An empty array must have no items; [{}] is an array with one item: an empty object. Changing to [] gets rid of the extra bullet.
The bigger issue was your ngRepeat. You used ng-repeat="bdays in bdays". What ngRepeat does is take an array and allow you to do a "for each" on the array, assigning each value to that temporary local variable. You named them the same. Instead, ng-repeat="bday in bdays" will add the DOM nodes inside of it for each item in bdays, giving you a local variable called bday to use to reference each item.
Inside the ngRepeat template, you used bdae, which doesn't reference anything.
I don't know what app.initialize() is, so I removed it. It was just erroring out in the console.
Here's a fixed Plunker: http://plnkr.co/edit/OFWY7o?p=preview

Toggle specific div and change img jquery

I have a problem with toggle on this list:
<div id="list">
<div id="segment"> // <--- when clicked, toggle segm_content and opener
<div id="opener">
<img src="images/open.png" /> // changes when toggled
</div>
<div id="segm_content">
// content to hide/show
</div>
</div>
<div id="segment"> // <--- when clicked, toggle segm_content and opener
<div id="opener">
<img src="images/open.png" /> // changes when toggled
</div>
<div id="segm_content">
// content to hide/show
</div>
</div>
... //and so on
</div>
I want clicked "#segment" to toggle child *"#segm_content"* and change img in "#opener".
I made it working with this code:
$('#segment').toggle(function() {
$('#opener').html('<img src="images/open.png"/>');
$('#segm_content').hide(500);
}, function() {
$('#opener').html('<img src="images/close.png"/>');
$('#segm_content').show(500);
});
But I can't figure out how to do it only for one "#segment" at a time.
This code toggles everything, which I don't want.
I am stuck at this point, any suggestions please?
Many thanks!
I really wouldn't recommend this. The point of an id is to reference a unique element. If you want to select multiple elements, you should define a class instead and have jQuery call that. Multiple ids is invalid HTML. But you could, per sé, do this by using changing your jQuery code to the following.
(Here is my jsFiddle: http://jsfiddle.net/KzVmK/)
$('[id="segment"]').toggle(
function(){
$(this).find('[id="opener"]').html('<img src="open.png" alt="Close" />');
$(this).find('[id="segm_content"]').hide(500);
},
function(){
$(this).find('[id="opener"]').html('<img src="close.png" alt="Open" />');
$(this).find('[id="segm_content"]').show(500);
}
);​
Again, let me stress again that this is a bad idea, because you will not have unique id selectors in your document. This is really bad practice. There are times when you will want to select an individual element in the DOM and this will make that next to impossible. I would highly advise you to define a class for the elements (you can still define CSS classes, e.g. <div class="opener my-class" /> or <div class="segm_content my-class" />).
(Also, a helpful tip with this code: rather than populating the HTML elements with the same image that is also in the jQuery code [which is redundant], leave the <div id="opener" /> elements empty. Then, right after you define the toggle function, run the click event, like so: $('[id="$segment"]').toggle(...).click();
http://jsfiddle.net/XPXBv/).
General Theme Settings
Back-Ground Color
Text Color
<div class="Settings" id="GTSettings">
<h3 class="SettingsTitle"><a class="toggle" ><img src="${appThemePath}/images/toggle-collapse-light.gif" alt="" /></a>Content Theme Settings</h3>
<div class="options">
<table>
<tr>
<td><h4>Back-Ground Color</h4></td>
<td><input type="text" id="body-backGroundColor" class="themeselector" readonly="readonly"></td>
</tr>
<tr>
<td><h4>Text Color</h4></td>
<td><input type="text" id="body-fontColor" class="themeselector" readonly="readonly"></td>
</tr>
</table>
</div>
</div>
$(document).ready(function(){
$(".options").hide();
$(".SettingsTitle").click(function(e) {
var appThemePath = $("#appThemePath").text();
var closeMenuImg = appThemePath+'/images/toggle-collapse-light.gif';
var openMenuImg = appThemePath+'/images/toggle-collapse-dark.gif';
var elem = $(this).next('.options');
$('.options').not(elem).hide('fast');
$('.SettingsTitle').not($(this)).parent().children("h3").children("a.toggle").children("img").attr('src', closeMenuImg);
elem.toggle('fast');
var targetImg = $(this).parent().children("h3").children("a.toggle").children("img").attr('src') === closeMenuImg ? openMenuImg : closeMenuImg;
$(this).parent().children("h3").children("a.toggle").children("img").attr('src', targetImg);
});
});