I am trying to populate an edit view in angular 1.x. My controllers are logging the value of "themenu" and "$scope.addmenu" but the data is not showing up in the text fields..
angular.module('sample.menu', [
'auth0'
]).controller('MenuCtrl', function HomeController($scope, $http, auth, $location, store) {
$scope.menus = {};
$scope.addmenu = {};
var res1 = $http.get('http://grubr2.webfacelab.com:8888/index.php/api/menu');
res1.success(function (data, status, headers, config) {
console.log(data);
$scope.menus = data;
});
res1.error(function (data, status, headers, config) {
alert("failure message: " + JSON.stringify({ data: data }));
});
$scope.addMenu = function () {
console.log('Adding Menu');
console.log($scope.menu);
var res = $http.post('http://grubr2.webfacelab.com:8888/index.php/api/menu', $scope.menu);
res.success(function (data, status, headers, config) {
$scope.message = data;
});
res.error(function (data, status, headers, config) {
alert("failure message: " + JSON.stringify({ data: data }));
});
}
$scope.editThisMenu = function (themenu) {
console.log("Edit this menu:");
console.log(themenu);
console.log("End menu data");
$scope.addmenu = themenu;
console.log($scope.addmenu);
}
$scope.updateMenu=function(){
alert('Update menu!!');
}
});
And in my views Menus.html
<ion-view title="Menus">
<ion-content padding="'true'" class="has-header">
<form class="list">
<div class="list card">
<div class="item item-divider">Menu Item</div>
<div class="item item-body">
<ion-list>
<ion-item menu-close="" class="item-thumbnail-left">
<img>
<h2>Menu Item</h2>
<p>**Delicious</p>
</ion-item>
</ion-list>
<button class="button button-stable button-block ">Upload Picture</button>
</div>
</div>
<ion-list>
<label class="item item-input">
<span class="input-label">Name</span>
<input ng-model="menu.name" type="text" placeholder="">
</label>
<label class="item item-input">
<span class="input-label">Description</span><textarea ng-model="menu.description" placeholder=""> </textarea>
</label>
<label class="item item-input">
<span class="input-label">Price</span>
<input ng-model="menu.price" type="number" placeholder="">
</label>
</ion-list>
<div class="spacer" style="height: 40px;"></div>
<button class="button button-stable button-block " ng-click="addMenu()">Add Menu</button>
</form>
<hr>
<div ng-repeat="menu in menus">
<li>{{menu.id}}: {{menu.name}}</li>
</div>
</ion-content>
And Menuedit.html which is supposed to display the content to be edited
<ion-view title="Menus">
<ion-content padding="'true'" class="has-header">
<form class="list">
<div class="list card">
<div class="item item-divider">Edit Menu Item</div>
<div class="item item-body">
<ion-list>
<ion-item menu-close="" class="item-thumbnail-left">
<img>
<h2>Menu Item</h2>
<p>**Delicious</p>
</ion-item>
</ion-list>
<button class="button button-stable button-block ">Edit Picture</button>
</div>
</div>
<ion-list>
<label class="item item-input">
<span class="input-label">Name</span>
<input ng-model="addmenu.name" type="text" placeholder="">
</label>
<label class="item item-input">
<span class="input-label">Description</span><textarea ng-model="addmenu.description" placeholder=""> </textarea>
</label>
<label class="item item-input">
<span class="input-label">Price</span>
<input ng-model="addmenu.price" type="number" placeholder="">
</label>
</ion-list>
<div class="spacer" style="height: 40px;"></div>
<button class="button button-stable button-block " ng-click="updateMenu()">Update Menu</button>
</form>
<hr>
</ion-content>
I'm not 100% sure what's causing your problem, because I don't think you've provided enough information to say for certain. But I think it has to do with your client-side routing. In this: {{menu.name}}</li> you have what appears to be a link to a piece of your application. The ng-click will not allow the route change so your template is most likely still Menus.html instead of Menuedit.html.
I want to address how you've constructed this module, though. You have a lot of repetition in your templates that can be reduced with a simple flag on your scope. Using a flag such as $scope.isNew would allow you to use the ng-if directive to toggle certain HTML elements off and on, depending if you're editing or creating a new menu. I've added a bit of functionality as well, so bear with me.
I would set up your controller like this:
$scope.isNew = true; // flag to keep track of form state
$scope.menus = []; // initialize an empty array for the menus
$scope.menu = {}; // initialize an empty object for the form
var res1 = $http.get('mock.json');
res1
.success(function(data) {
$scope.menus = data;
})
.error(function (data) {
console.log("failure message: " + JSON.stringify(data));
});
$scope.clear = function() {
$scope.isNew = true;
$scope.menu = {};
}
$scope.submit = function(menu) {
if ($scope.isNew) {
$scope.menus.push(menu);
} else {
$scope.menus[$scope.currentIndex] = menu;
}
$scope.clear();
}
$scope.remove = function() {
$scope.menus.splice($scope.currentIndex, 1);
$scope.clear();
}
$scope.edit = function(menu, index) {
$scope.currentIndex = index;
$scope.isNew = false;
// this has to be copied so it's not two-way bound to the template
$scope.menu = angular.copy(menu);
}
As you can see, I've add an isNew flag and a clear function to the scope. I've also added a currentIndex to the scope for when editing is enabled. currentIndex is used when deleting an item (part of the functionality I added), since the button for deletion is sitting outside of the scope of the item being deleted. The clear function will reset the form and switch the isNew flag. I've changed the addMenu function in your original code to submit so that the term better represents its ability to submit an edit or new menu.
And now for the template:
<section class="has-header" ng-controller="MenuCtrl">
<button ng-if="!isNew" class="button button-stable button-block" ng-click="clear()">New Menu</button>
<form class="list">
<div class="list card">
<div class="item item-divider">Menu Item</div>
<div class="item item-body">
<div>
<div menu-close="" class="item-thumbnail-left">
<img>
<h2>Menu Item</h2>
<p>**Delicious</p>
</div>
</div>
<button class="button button-stable button-block ">Upload Picture</button>
</div>
</div>
<div>
<label class="item item-input">
<span class="input-label">Name</span>
<input ng-model="menu.name" type="text" placeholder="">
</label>
<br>
<label class="item item-input">
<span class="input-label">Description</span><textarea ng-model="menu.description" placeholder=""> </textarea>
</label>
<br>
<label class="item item-input">
<span class="input-label">Price</span>
<input ng-model="menu.price" type="number" placeholder="">
</label>
</div>
<div class="spacer" style="height: 40px;"></div>
<button class="button button-stable button-block " ng-click="submit(menu)">
<span ng-if="isNew">Add Menu</span>
<span ng-if="!isNew">Update Menu</span>
</button>
<button ng-if="!isNew" class="button button-stable button-block" ng-click="remove()">Delete Menu</button>
</form>
<hr>
<div ng-repeat="menu in menus track by $index">
<li>{{menu.id}}: <a ng-click="edit(menu, $index)">{{menu.name}}</a></li>
</div>
</section>
I've added a button at the top to create a new menu, that's only displayed if you're in edit mode. All it does is clear the form. I've added a check for isNew for the submit button as well, changing whether it says "Update" or "Add" based on isNew. And I added a "Remove" button, only shown when in edit mode. Hopefully I've explained my thinking clearly, because I feel like this is a much better approach to your problem. Feel free to ask questions for any clarifications. I've also created a Plunk to demonstrate all of this. http://plnkr.co/edit/gvP2iUahPjhzTmByPQw6?p=preview
Related
Not sure if I am phrasing this correctly.
I have an observableArray and I can add to that array from an input and also remove the list item. but if I modify the created item I lose the connection to the array. How can I keep the binding to the array?
Fiddle Attached
HTML
<div class="group-settings-container mt-4">
<div class="row">
<div class="col-md-3">
<h4><i class="fas fa-object-group"></i> Create Groups</h4>
</div>
<div class="col-md-6">
<div class="input-group">
<input type="text" class="form-control create-group-name" data-bind="value: groupItemToAdd, valueUpdate: 'afterkeydown' " placeholder="Enter group name" value="">
<div class="input-group-append">
<button class="btn btn-primary add-group-btn" data-bind="click: addGroupItem, enable: groupItemToAdd().length > 0" type="button"><i class="fas fa-plus"></i>
Add group</button>
</div>
</div>
</div>
<div class="create-groups-container mb-4">
<ul class="list-group create-group-list my-2" data-bind="foreach: allGroupItems">
<li class="list-group-item">
<div class="input-group">
<input type="text" class="form-control created-group-input" data-bind="value: $data">
<div>
<button class="btn btn-danger remove-group-item-btn" data-bind="click: $parent.removeSelectedGroupItem" type="button"><i class="fas fa-times"></i>
Remove</button>
</div>
</div>
</li>
</ul>
</div>
<!-- end create groups container -->
</div>
<!-- end group settings container -->
JS
function ViewModel() {
var self = this;
self.groupItemToAdd = ko.observable("");
self.allGroupItems = ko.observableArray([]);
self.addGroupItem = function() {
if ((self.groupItemToAdd() != "") && (self.allGroupItems.indexOf(self.groupItemToAdd()) < 0)) {
self.allGroupItems.push(self.groupItemToAdd());
}
self.groupItemToAdd(""); // clear the input
}
self.removeSelectedGroupItem = function(index) {
// self.allGroupItems.splice(index, 1);
// console.log(self.allGroupItems.splice(index, 1));
self.allGroupItems.remove(index);
}
}
// end ViewModel
ko.applyBindings(new ViewModel());
You have an observableArray. Which means, any changes to the array are tracked and updated. The items inside it are just strings. They are not observables. Any changes you make from the UI is not updated back to the view model. This behaviour is not limited to strings. The same thing applies if you have an observableArray of regular javascript object literals.
From the documentation:
Simply putting an object into an observableArray doesn’t make all of that object’s properties themselves observable. Of course, you can make those properties observable if you wish, but that’s an independent choice. An observableArray just tracks which objects it holds, and notifies listeners when objects are added or removed.
So, instead of adding strings to the observableArray, you can push an object with an observable property to the observableArray. Now the changes to item property are tracked. It's important to make the property an observable, otherwise you'll run into the same issue.
function ViewModel() {
var self = this;
self.groupItemToAdd = ko.observable("");
self.allGroupItems = ko.observableArray([]);
self.addGroupItem = function() {
if (self.groupItemToAdd() && !self.allGroupItems().some(a => a.item() === self.groupItemToAdd())) {
self.allGroupItems.push({
item: ko.observable(self.groupItemToAdd())
});
}
self.groupItemToAdd(""); // clear the input
}
self.removeSelectedGroupItem = function(index) {
self.allGroupItems.remove(index);
}
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
<div class="group-settings-container mt-4">
<div class="row">
<div class="col-md-3">
<h4><i class="fas fa-object-group"></i> Create Groups</h4>
</div>
<div class="col-md-6">
<div class="input-group">
<input type="text" class="form-control" data-bind="value: groupItemToAdd, valueUpdate: 'afterkeydown' " placeholder="Enter group name">
<div class="input-group-append">
<button class="btn btn-primary add-group-btn" data-bind="click: addGroupItem, enable: groupItemToAdd().length > 0" type="button">Add group</button>
</div>
</div>
</div>
<ul class="list-group create-group-list my-2" data-bind="foreach: allGroupItems">
<li class="list-group-item">
<div class="input-group">
<input type="text" class="form-control created-group-input" data-bind="value: item">
<div>
<button class="btn btn-danger remove-group-item-btn" data-bind="click: $parent.removeSelectedGroupItem" type="button">Remove</button>
</div>
</div>
</li>
</ul>
</div>
<span data-bind="text: allGroupItems().map(a => a.item())"></span>
Note:
You need to change the input binding inside foreach from $data to item (observable property name)
To check if a group item is already added, use some like this: self.allGroupItems().some(a => a.item() === self.groupItemToAdd())
The last span demonstrates that the observable is updated
I have a form in Ionic and I am trying to return the value of the textbox, but am having issues. The rest of the form is loading and 'signup' is returned to the console. formData.email isn't bound in the template and neither is anything returned to the console when I type something in and click the button.
Any advice on what to do?
signup.html
<!-- Header -->
<div class="bar bar-header bar-light">
<button class="button icon-left ion-chevron-left button-clear button-dark">Back</button>
<h1 class="title">Signup</h1>
</div>
<!-- Space between header and signup form -->
<div class="spacer" style="width: 300px; height: 50px;"></div>
<div class="list list-inset">
<!-- Signup form text fields -->
<form>
<label class="item item-input">
<span class="input-label">Email</span>
<input type="email" ng-model="formData.email">
</label>
<label class="item item-input">
<span class="input-label">Username</span>
<input type="text">
</label>
<label class="item item-input">
<span class="input-label">Password</span>
<input type="password">
</label>
<!-- Submit button for signup form -->
<button on-touch="onTouch()" class="button button-positive button-block">
Sign up
</button>
</form>
</div>
{{formData.email}}
controllers.js
.controller('SignupCtrl', function($scope, $ionicLoading, $state, $stateParams){
console.log('signup');
$scope.formData = {};
//Go to the guessing page
$scope.onTouch = function(item,event){
console.log($scope.formData.email);
};
});
What you're doing is correct, the only thing that I could do to recreate this was if you haven't entered a valid email address it returns undefined. This is a validation feature built in to Angular itself and has nothing to do with Ionic. You can find out more about this here: https://docs.angularjs.org/api/ng/input/input%5Bemail%5D
I am using tabs from angular ui bootstrap.
All works fine but I noticed that text boxes inside the tab is not updating the scope although using ng-model.
I searched and found out that it is because of child scope and advise of using obj.property notation while binding.
but still my model is not being updated.
Please guide where I am going wrong.
wbProcess.controller ("createProCtrl", function ($scope, $http, global, user){
$scope.project = [{protitle :""},
{prodesc : ""},
{chkarchive : false}
];
$scope.tab = true;
$scope.upurl;
$scope.createpro = function(){
$http({
url: global.apiurl+"pro/create",
method: "POST",
data: {'name': $scope.project.protitle, 'prodesc': $scope.project.prodesc, 'owner': user.user_id , 'active': $scope.project.chkarchive}
}).success(function (data, status, headers, config) {
// assign $scope.persons here as promise is resolved here
//$log.log(data);
if(data.status > 0){
$scope.tab = false;
}
else{
}
}).error(function (data, status, headers, config) {
$scope.status = status;
$log.log(status);
});
}
});
HTML is
<tabset>
<tab>
<tab-heading>
<i class="green icon-edit bigger-110"></i>Description
</tab-heading>
<div>
<form name="createProForm" class="form-horizontal">
<div class="control-group span7">
<label class="control-label" for="form-field-1">Title</label>
<STYLE type="text/css">
.ng-dirty.ng-valid ~ span.ok { color:green; display:inline; }
.ng-dirty.ng-invalid ~ span.ko { color:red; display:inline; }
</STYLE>
<div class="controls">
<input type="text" name="protitle" id="projecttitle" ng-model="project.protitle" ng-unique="projects" placeholder="Title" required />
<span class="red" ng-show="createProForm.protitle.$error.unique" >
<i class="red icon-asterisk bigger-110"></i> Project Title already exists.</span>
<!--<span class="green" ng-show="createProForm.protitle.$error.unique === false" >
<i class="green icon-asterisk bigger-110"></i> Available</span>
-->
</div>
</div>
<div class="control-group span5">
<div class="controls">
<input class="ace" type="checkbox" id="id-disable-check" ng-model="project.chkarchive" tabindex="-1"/>
<label class="lbl" for="id-disable-check"> Archive Project</label>
</div>
</div>
<div class="control-group">
<label class="control-label" for="form-field-9">Project Description</label>
<div class="controls">
<textarea class="span7" id="projecttitle" ng-model="project.prodesc" maxlength="100" placeholder="100 Character Limit" required></textarea>
</div>
</div>
<div class="form-actions">
<button class="btn btn-info" type="button" ng-disabled="createProForm.protitle.$pristine || createProForm.protitle.$dirty && createProForm.protitle.$error.unique === true" ng-click="createpro()">
<i class="icon-ok bigger-110"></i>
Save
</button>
<button class="btn" type="reset">
<i class="icon-undo bigger-110"></i>
Reset
</button>
<button class="btn btn-default btn-sm" ng-click="tabs[1].disabled = ! tabs[1].disabled">Enable / Disable third tab</button>
</div>
</form>
</div>
</tab>
<tab disabled = "tab">
<tab-heading>
<i class="green icon-cogs bigger-110"></i>Configuration
</tab-heading>
<div>
<div class="span6">
hi
</div>
<div id="dropzone" class="span6">
<input type="hidden" id="upurl" value="{{ upurl }}" />
<form action="/not-found" class="dropzone">
<div class="fallback">
<input name="file" type="file" multiple/>
</div>
</form>
</div>
</div>
</tab>
<tab disabled = "tab">
<tab-heading>
<i class="green icon-group bigger-110"></i>Users
</tab-heading>
<div>
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade.</p>
</div>
</tab>
The text input is only in first tab.
There is one solution to access it.
You can access it using the following syntax in your controller.
$scope.$$childHead.$$nextSibling.yourVariablename.
For example you have ng-model="state" on html page you can access it like this
$scope.$$childHead.$$nextSibling.state
The advise of using obj.property notation while binding because of child scope is correct, but you wrongly defined $scope.project as a list of objects instead of just a simple object with multiple key/value pairs.
Try:
$scope.project = {
protitle: "",
prodesc : "",
chkarchive: false
};
See also here
i am using modal in angular but when modal open so here values are not binding with model i don't know why this happening must be appreciates if some corrected if there is any mistake thanx.
modal.html
<script type="text/ng-template" id="categoryModal.html">
<form class="form-horizontal" name="category_form" novalidate>
<div class="modal-header">
<a class="close" ng-click='cancel()'><i class="icon-remove-circle icon-bold"></i> </a>
<h3>Category</h3>
</div>
<div class="modal-body">
<div class="form-group">
<label for="category_Name" class="col-lg-3 form-label">Category Name:</label>
<div class="col-lg-8">
<input class="form-control" id="category_Name" ng-model="category.name" name="category_Name" placeholder="Category Name" required/>
<div class="error" ng-show="category_form.category_Name.$dirty && category_form.category_Name.$invalid">
<small class="error errorFields" ng-show="category_form.category_Name.$error.required">
Category Name is required.
</small>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button ng-click='saveCategory()' class="btn btn-primary" ng-disabled="category_form.$invalid">
<i class="icon-ok-sign icon-white"></i> Add
</button>
<button ng-click='cancel()' class="btn btn-warning">
<i class="icon-remove-circle icon-white"></i> Cancel
</button>
</div>
</form>
</script>
modalController.js
app.controller('brandModalCtrl', function ($rootScope, $scope, $modalInstance) {
// Save Brand
$scope.saveCategory = function () {
console.log($scope.category) // undefined
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
I would try adding the same $scope to modalOptions and letting the modal share the controller's $scope variables. Putting both onto $rootScope is too drastic. Ideally, your modal should have an isolate scope, but it sounds like you need some overlap.
I want CRUD operations so that when a user clicks on any particular item to edit this form will be filled with those values (that the user wants to edit).
Here is my code .
storeView.html
<tr data-ng-repeat="store in stores | filter:search | orderBy:'name'">
<td>
{{ store.name }}
</td>
<td class="icons-width">
<a href="#/Store/edit/{{store._id}}" id="edit" data-toggle="tooltip">
<i class="fa fa-pencil fa-fw colorInfo" ></i>
</a>
<a ng-click="deleteStore(store._id)" id="delete" data-toggle="tooltip">
<i class="icon-trash"></i>
</a>
</td>
</tr>
So when a user clicks on edit for a particular store it will go to another view which has the a form that will show which values the user wants to edit.
storeformView.html
form class="form-horizontal" name="store_form" novalidate ng-submit='AddStore()'>
<div class="modal-header">
<h3>Create/Edit Store</h3>
</div>
<div class="modal-body">
<div class="form-group">
<label for="store_Name" class="col-lg-4 form-label">Name:</label>
<div class="col-lg-7">
<input type="text" class="form-control" ng-model="store.name" id="store_Name" name="store_Name" placeholder="Store Name" required/>
<div class="error" ng-show="store_form.store_Name.$dirty && store_form.store_Name.$invalid">
<small class="error errorFields" ng-show="store_form.store_Name.$error.required">
Store Name is required.
</small>
</div>
</div>
</div>
<div class="form-group">
<label for="store_AddressLine1" class="col-lg-4 form-label">Address Line 1:</label>
<div class="col-lg-7">
<input type="text" class="form-control" ng-model="store.address1" id="store_AddressLine1" name="store_AddressLine1" placeholder="Address Line 1" required/>
<div class="error" ng-show="store_form.store_AddressLine1.$dirty && store_form.store_AddressLine1.$invalid">
<small class="error errorFields" ng-show="store_form.store_AddressLine1.$error.required">
Address is required.
</small>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" ng-disabled="store_form.$invalid">
<i class="icon-ok-sign icon-white"></i> Save
</button>
<a class="btn btn-default" href="#/Stores">
<i class="icon-remove-circle" ></i>
Cancel
</a>
</div>
</form>
storeFactory.js
app.factory('Store', function ($resource) {
return {
getAllStores : $resource("/store/list" ,{},{ TypeGetStores:{method: 'get', isArray:true}}),
getStore : $resource("/store/:id" ,{id : #id},{ TypeGetStore:{method: 'get'}}),
};
});
app.js
var app = angular.module('myApp', ['ngCookies','ngResource', 'ngRoute']);
app.config(function ($routeProvider) {
$routeProvider
.when('/Stores',
{
templateUrl: '/app/partials/StoresListView.html'
})
.when('/Store/edit/:storeId',
{
templateUrl: '/app/partials/StoreFormView.html'
})
.otherwise({ redirectTo: '/Login' });
});
For loading information about your store, you can use resolve parameter in routeProvider :
.when('/Store/edit/:storeId',
{
templateUrl: '/app/partials/StoresListView.html',
resolve: {app: function($scope,Store,$routeParams) {
$scope.store = Store.getStore({id:$routeParams.id})
}
}
If everything is right and that's how your factory will work (if that function really returns store. Maybe it returns JSON with root, then you should specify, how to get store from that JSON), than you will have $scope.store in your controller, and it will be two-way-binded with what you have in form.
Note: page will load only after resolve is fully done.