http://jsfiddle.net/cnnMQ/2/
You can see here I have a pretty nice functioning Add/Remove/Edit functionality for removing objects from an array.
What I am struggling with is
Editing inline and pushing the changes back into the array.
Adding new input fields to the DOM in order to push new objects into the array.
http://jsfiddle.net/cnnMQ/2/
app = angular.module("sparta", []);
window.CompetitionController = function($scope) {
$scope.activities = [{
id: 6431,
name: "Meeting",
points: 20
}, {
id: 6432,
name: "Deal",
points: 100
}];
$scope.addNewActivity = function() {
//This function should create 2 new input fields
//The user should input the name and points
//We can ignore the id for now
//Then the object should be craeted and pushed in as you see below with the mock data.
var updatedActivities = {
id: 6433,
name: "Call",
points: 5
};
$scope.activities.push(updatedActivities);
}
$scope.editActivity = function(activity) {
var selectedActivity = activity;
console.log(selectedActivity);
}
$scope.removeActivity = function(activity) {
activityId = activity.id; //the activity id
var i = 0;
for (var item in $scope.activities) {
if ($scope.activities[item].id == activityId)
break;
i++;
}
$scope.activities.splice(i, 1);
}
}
The HTML is as follows:
<body ng-app="sparta">
<div class="container" ng-controller="CompetitionController">
<div ng-repeat="activity in activities">
{{activity.name}} - {{activity.points}}
<button ng-click="editActivity(activity)">Edit</button>
<button ng-click="removeActivity(activity)">Remove</button>
</div>
<div class="addNew">
<button ng-click="addNewActivity()">Add New</button>
</div>
</div>
</body>
I've tried to give as much as possible in the fiddle - what I would love is some guidance on the addNewActivity() function and the editActivity() function and how to inline edit the two input fields and save the changes back into the array.
Thanks in advance!
You can change your html from:
{{activity.name}} - {{activity.points}}
To:
<input type="text" ng-model="activity.name"/> - <input type="text" ng-model="activity.points"/>
So you get 2-way binding.
Working example: http://jsfiddle.net/CFx7m/
Here's another simple example: http://jsfiddle.net/A5xZ9/2/
Basically you hide input field until activity edit button is clicked, in which case you show input field and hide text:
<div ng-show="activity.isEdited">
<input type="text" ng-model="activity.name"/> - <input type="text" ng-model="activity.points"/>
<button ng-click="activity.isEdited = false">Ok</button>
</div>
<div ng-hide="activity.isEdited">
{{activity.name}} - {{activity.points}}
<button ng-click="activity.isEdited = true">Edit</button>
<button ng-click="removeActivity(activity)">Remove</button>
</div>
There's a lot of improvement possible, for example editing local copy of the activity and updating original attributes only when user presses Ok, and providing Cancel button as well.
Related
I am new to angular js. In my code user changes the value of radio buttons. And depending on the value of the selected radio button, a piece of code is loaded from the ng-switch
HTML:
<body ng-app="">
<div ng-repeat="button in modes">
<label>
<input type="radio" ng-model="data.mode" value="{{button.value}}" ng-click="clearObjectIdModal()" name="e_modes">
{button.label}}
</label>
</div>
<div ng-switch on="data.mode">
<div ng-switch-when="client">
<label for="e_selected_object_item_id">Select Client name: </label>
<select id="e_selected_object_item_id" name="e_selected_object_item_id" ng-model="currentDataItem.object_id" required>
<option ng-repeat="item in customersListArr" value="{{ item.id }}">{{ item.Name }}</option>
</select>
</div>
<div ng-switch-when="agent">
// This part is similar to the previous one
</div>
</div>
</body>
Controller part:
$scope.data = {};
$scope.setFile = function () {
if ($scope.data.mode == 'client')
return 'client';
else if ($scope.data.mode == 'agent')
return 'agent';
$scope.modes = [{
value: 'client',
label: 'Client'
},{
value: 'agent',
label: 'Agent'
}];
$scope.currentDataItem = data; // data is preloaded from inputs in form
There is also a ng-click="clearObjectIdModal()" that clears the model when switching radio buttons:
$scope.clearObjectIdModal = function() {
$scope.currentDataItem = "";
}
The problem is that every time when the radio button is switched to the select value, which dynamically changes, the value of the first option in it becomes equal to undefined. Because in the array from where these options are built there is no such object_id (This is the id that is not there, so an empty field is drawn).
That is, there are all works. But the first option in the select(after switching to another radio button) is rendered as an empty string.
There are thoughts, how it can be fixed?
I'm not sure if I understand you problem correctly but I would suggest a few improvements.
change your setFile function to as follows
$scope.setFile = function (){return $scope.data.mode;}
I also do not see the closing brackets for your function in your code. Besides if your function will only return the data.mode then why need the function?
I would suggest initialize your data object properly like:
$scope.data = {mode:'client'};
Change your clearObjectIdModal function as:
$scope.clearObjectIdModal = function(mode)
{
$scope.currentDataItem = "";
$scope.data.mode=mode;
}
and in your HTML use it as ng-click="clearObjectIdModal(button.mode)"
So in function clearObjectIdModal() I wrote:
$scope.clearObjectIdModal = function() {
if ($scope.e_data["mode"] == 'client') {
if ($scope.customersListArr.length > 0) {
$scope.currentDataItem.object_id = $scope.customersListArr[0]['id'];
}
}
else if ($scope.e_data["mode"] == 'agent') {
if ($scope.agentsListArr.length > 0) {
$scope.currentDataItem.object_id = $scope.agentsListArr[0]['id'];
}
}
}
And after this when I change radio buttons the first option in current select(which every time is changed) will be not empty.
Also the problem with an additional empty option is possible to solve when you add a title as the first item in the list:
<option value="" disabled>Select</option>
Hi I have two form in one page one for reference of previous data and one is a actual form. So i have to assign same json(which actually are come from database) to two different form in a page. I have a problem when I change the option value in main form the reference form value also change. What I want is even the main form change value, reference form should retain old value. please check my code.
https://jsfiddle.net/sanuman/kts7je89/24/
thank you for your any help and suggestions.
var app = angular.module('myApp',[])
.controller('MyCtrl', function($scope, $http) {
$scope.muni=[
{
"id": 24001,
"VDC_Muni_Code": "24001",
"VDC_Muni_Name_Eng": "Anaikot",
"tbl_district_id": 24
},
{
"id": 24002,
"VDC_Muni_Code": "24002",
"VDC_Muni_Name_Eng": "Baldthali",
"tbl_district_id": 24
},
{
"id": 24003,
"VDC_Muni_Code": "24003",
"VDC_Muni_Name_Eng": "Balting",
"tbl_district_id": 24
},
{
"id": 24004,
"VDC_Muni_Code": "24004",
"VDC_Muni_Name_Eng": "Baluwapati",
"tbl_district_id": 24
}
];
$scope.service_data=[
{
"tbl_vdc_municipality_id": 24001
},
{
"tbl_vdc_municipality_id": 24004
},
{
"tbl_vdc_municipality_id": 24002
},
{
"tbl_vdc_municipality_id": 24003
}
];
$scope.municipalities_ref = $scope.muni;
$scope.municipalities = $scope.muni;
$scope.wspVdcMuniTbls = $scope.service_data;
$scope.wspVdcMuniTblsRef = $scope.service_data;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<h2>
Main Form
</h2>
<div ng-repeat="wspVdcMuniTblRef in wspVdcMuniTblsRef">
<select
ng-model="wspVdcMuniTblRef.tbl_vdc_municipality_id"
options="municipalities_ref"
ng-options="municipality_ref.id as municipality_ref.VDC_Muni_Name_Eng for municipality_ref in municipalities_ref">
</select>
</div>
<h2>
Reference Form
</h2>
<div ng-repeat="wspVdcMuniTbl in wspVdcMuniTbls">
<select
ng-model="wspVdcMuniTbl.tbl_vdc_municipality_id"
options="municipalities"
ng-options="municipality.id as municipality.VDC_Muni_Name_Eng for municipality in municipalities">
</select>
</div>
</div>
</div>
The example you've provided work as expected. The thing is that both $scope.municipalities and $scope.municipalities_ref points to the same object (same for $scope.wspVdcMuniTbls and $scope.wspVdcMuniTblsRef) when this assigment is made:
$scope.municipalities = $scope.muni;
$scope.municipalities_ref = $scope.muni;
$scope.wspVdcMuniTbls = $scope.service_data;
$scope.wspVdcMuniTblsRef = $scope.service_data;
You should create a copy of $scope.muni and $scope.service_data like this:
$scope.municipalities_ref = angular.copy($scope.muni);
$scope.wspVdcMuniTblsRef = angular.copy($scope.service_data);
The documentation of angular.copy(source, [destination]); can be find there.
Hej, I've got an "almost" working fiddle. I have a list of items and I want to change their value if their radiobutton is selected. Here's the code:
CodePen:
http://codepen.io/anon/pen/MyvQoP
Html:
<div ng-app="myapp" ng-controller="myController">
<div ng-repeat="food in foodList">
<span>{{food.name}}</span>
<input type="radio" ng-model="food.selected" name="radiofood" ng-value="true">
</div>
</div>
JS:
angular.module('myapp', []).controller("myController", myController)
function myController($scope) {
$scope.foodList = [
{
name: 'banana',
selected: 'false'
},
{
name: 'orange',
selected: 'false'
},
{
name: 'apple',
selected: 'false'
}
]
}
The problem:
A radiobutton once clicked, changes it's value to true but clicking another one does not change the previous one to false. So if you click each one of them, one by one, all of them will be true. I only want one to have the true value.
Thanks
--- Edit 2016-03-31 ---
I was looking for a solution without writing a custom fuction but it turns out this can't be done. I've marked #Ankit Pundhir answer as the best one but it wasn't exaclty what i was aiming for.
Add method to controller file:
$scope.selectFood = function(selectedFood){
angular.forEach($scope.foodList,function(food){
if(food != selectedFood){
food.selected = false;
}
})
};
and add ng-change="selectFood(food)" to radio button.
The most simple solution is to add ng-change event on the input and then write a function which takes selectedFood as param and do the following:
Iterates through foodList and changes every value to false
Toggle status of selectedFood (if true set false end vice versa)
Something like this:
$scope.toggleParam = function(selectedFood){
loopThroughAndSetToFalse();
findAndSetReverseValue(selectedFood);
}
function loopThroughAndSetToFalse(){
for(var i=0; i<$scope.foodList.length; i++){
$scope.foodList[i].selected = false;
}
}
function findAndSetReverseValue(selectedFood){
for(var i=0; i<$scope.foodList.length; i++){
if($scope.foodList[i].name === selectedFood.name){
$scope.foodList[i].selected = !(selectedFood.selected);
}
}
}
And your html now will look like this:
<div ng-app="myapp" ng-controller="myController">
<div ng-repeat="food in foodList">
<span>{{food.name}}</span>
<input type="radio" ng-model="food.selected" name="radiofood" ng-change="toggleParam(food)" ng-value="true">
</div>
<br><br>
{{foodList[0]}}<br>
{{foodList[1]}}<br>
{{foodList[2]}}
</div>
Any ideas how to reset input fields when they are dynamically generated? Couldn't find any information about this. If I would know in advance how many fields there will be it would be easy with ref or just have unique value attributes on every input element and the call getInitialState().
https://jsfiddle.net/69z2wepo/15407/
Size of the product array can vary so how would you reset all of the input fields?
If you wrap your (uncontrolled) inputs in a HTML <form> element, there's always
<button type="reset">Reset</button>
Otherwise, you'll probably want to bind the input value to a prop or state item, and then use handleReset to retrieve and (re)set these values. When an item in state or prop changes, React will automatically update the affected parts of your DOM.
Read more about form inputs in the docs: http://facebook.github.io/react/docs/forms.html#controlled-components
you can set this.setState by array of values
https://jsfiddle.net/amb223fx/
Edit: As mgrim said, you can use HTML form reset. Or, if you need something more sophisticated, keep reading.
Take a look at this:
var products = [
{name: 'Chocolate'},
{name: 'Chips'},
{name: 'Candy'}
];
// Size of the products array can vary!
var Hello = React.createClass({
handleReset: function(){
var products = this.state.products.map(function (product) {
return Object.assign({}, product, {
value: 0
});
});
this.setState({products: products});
},
handleChange: function(index){
return function(e){
var products = this.state.products.slice();
var product = products[index];
products[index] = Object.assign({}, product, {
value: parseInt(e.target.value, 10)
});
this.setState({products: products});
}.bind(this);
},
getInitialState: function () {
return {
products: this.props.initialProducts.map(function (product) {
return {
name: product.name,
value: 0
}
})
}
},
render: function() {
var prods = this.state.products.map(function(item, id){
return (
<li key={id}>
{item.name}
<input type="number" value={item.value} onChange={this.handleChange(id)} />
</li>
)
}.bind(this));
return (
<ul>
{prods}
<button onClick={this.handleReset}>Reset</button>
</ul>
);
}
});
React.render(<Hello initialProducts={products} />, document.getElementById('container'));
You'll have to call setState every time there is a change in any of the input boxes, and setState when the user intends to reset all inputs.
Also, I did some refactoring. I renamed the products property to initialProducts property. This is because assigning a property to a state is an anti-pattern, unless we clarify that the inputed property will be a part of the state that will change over time.
The idea here is just to get all input and use a for to clean them up. Please note that I'm using Jquery to get all inputs.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
function cleanForm(){
var allInputs = $( ":input" );
for (i = 0; i < allInputs.length; i++) {
allInputs[i].value = "";
}
}
</script>
</head>
<body>
<form>
<input type="text" name="1"/>
<input type="text" name="2"/>
<input type="text" name="3"/>
<input type="text" name="4"/>
<input type="button" name="clean" onclick="cleanForm()" value="clean"/>
</form>
</body>
I want to flow different data through a user clickable ul but I can't reset the state of the li's which have the isactive style set. Stripping down to bare minimum to demonstrate the input box takes two numbers separated by '-', the first is the number of clickable boxes, the second is the number of unclickable boxes at the beginning.
Note when new input is sent the li's that are currently active remain active. I want to reset the li's to inactive. [ note: trying to do this without jQuery to learn "The Angular Way". I have a pure jQuery version of this ]. angular.copy has not worked (though that could be ignorance)
I'm starting to think this might have to go but I'm keeping the graphic representation exclusively in the .html:
html
<div ng-controller="BoxScreen">
<input type="text" ng-model="inbox" />
<button ng-click="getBox()" /></button>
<div>
<br />
<h2>{{dys}}, {{dst}}</h2>
<div>
<ul class="smallbox">
<li data-ng-repeat="s in skip"> </li>
<li data-ng-repeat="d in ar" ng-class="{'button': !isActive, 'button active': isActive}" ng-init="isActive = false" ng-click="isActive = !isActive; clickMe(d)">{{d}}</li>
</ul>
</div>
</div>
</div>
javascript
angular.module('myApp', [])
.controller('BoxScreen', ['$scope', function($scope) {
$scope.getBox = function() {
indat = $scope.inbox.split('-');
$scope.dys = indat[0];
$scope.dst = indat[1];
$scope.ar = [];
$scope.skip = [];
for(var s=0; s < $scope.dst; s++) {
$scope.skip.push(s);
}
for(var d=1; d <= $scope.dys; d++) {
$scope.ar.push(d);
}
}
$scope.clickMe = function(did) {
//
}
}]);
I believe your problem is related to ng-repeat creating new child scopes for the child elements it attaches to the DOM. When you expand the list with new elements, ng-repeat doesn't actually destroy the old elements (as long as they're unchanged, as is true in your case), but reuse them. See more here.
The way you have designed your structures on the scope seems very messy to me. A better approach is to create all the data beforehand, and not introduce all the logic in the HTML.
Example:
<li data-ng-repeat="d in ar.items" ng-class="{'button': !d.isActive, 'button active': d.isActive}" ng-click="ar.toggle(d)">{{d.text}}</li>
where ar here is an object:
$scope.ar = {
items: [
{
text: '1',
isActive: false
},
more items...
],
toggle: function(d) {
d.isActive = !d.isActive;
}
}
This way you have access to the data in other places as well, and not some hidden away variables set on the child scope.