Angularjs: ng-model not displaying properly - angularjs

I have a parent and child controller relationship. This is a non-working mockup of the basic functionality. I'm sure anyone more competent than me can get it working for a Plunker or Fiddle model. (So there is probably something wrong with: $scope.data.contactList = [{ID: 1, Email: "someemail#email.com"}, {ID: 2, Email: "anotheremail#email.com"}];) I tried creating some objects for the contactList array.
Anyway. I want to be able to click a link in the second table in the code below to invoke EditShowContact. In my actual app, this will show a hidden div and it will obviously display more properties of a contact than just the email.
In my actual program, the values of the table are filled out properly (i.e. my ng-repeat directive is working fine), but I cant seem to get the ng-model directive to respond. I've tried various different ways and nothing seems to work.
<html ng-app="myApp"><head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('ContactsController', function ($scope)
{
this.currentContactID = null;
this.EditShowContact = function(intContactID)
{
this.currentContactID = intContactID;
//this.currentContact = $scope.data.contactList[intContactID]; unclear why this assignment fails
};
});
app.controller('ActionsController', function ($scope)
{ $scope.data = {};
$scope.data.contactList = [{ID: 1, Email: "someemail#email.com"}, {ID: 2, Email: "anotheremail#email.com"}];
});
</script>
</head>
<body ng-controller="ActionsController as ActCtrl">
<div ng-controller="ContactsController as ContactsCtrl">
<table border = "1";>
<tr><th>Email</a></th>
<th>Name</th></tr>
</table>
<div >
<table ng-repeat="Contact in ContactsCtrl.data.contactList" border="1">
<tr>
<td>{{Contact.Email}}</td>
<td>{{Contact.Name}}</td>
</tr>
</table>
</div>
<div>
<form>
<input type="input" ng-model="ContactsCtrl.data.contactList[currentContactID].Email"></input>
</form>
</div>
</div>
</body>
</html>

There are quite a few errors here such as :
ContactsCtrl has no information about the ContactList. You are trying to find object in array using ID in place in index using <table> element inside <div> and more..
Bascially, i have reduced the need of two controllers to one and made a Working Demo.

Related

angularJS what's a good way to put dictionary in input

I have a dictionary of data in the controller and I'm displaying it using ng-repeat. The key is the Title and the value is placed as the value field of an input. I want the user to be able to edit the values and then submit the form. What's the best way I can handle all the input? I've tried ng-model but I can't change the values of the dictionary directly so I'm leaning towards making another dictionary to store the new data. That doesn't seem very efficient though so I'm wondering if there's a better way.
edit: I have this interface and add some values.
export interface Iint {
[title: string] : string;
}
this is in the constructor
this.hashMap : Iint = {};
this.hashMap["Next Title"] = "data";
this.hashMap["Next Value"] = "more data;
In the html I want each of the values (data, more data) to appear in it's own input text box where the user can edit and change the values in the dictionary. I need validation and other things before the user can save and update the data so I'm unsure of if I should be making a duplicate array.
Check this example out. I implemented it in Angular v1 before you edited your answer.
https://plnkr.co/edit/9Y33BDQTngPZx2Vpx5Zz?p=preview
script.js
var app = angular.module('myApp',[]);
app.controller('MyCtrl', ['$scope', function($scope) {
$scope.dict = {
"Title1" : "Hello World !",
"Title2" : "Beautiful day",
"Title3" : "How about that!?"
};
$scope.submitForm = function() {
console.log($scope.dict);
alert(JSON.stringify($scope.dict));
};
}]);
index.html:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<h1>Dictionary Inputs</h1>
<div ng-controller="MyCtrl">
<form name="myForm" ng-submit="submitForm()">
<ul>
<li ng-repeat="(key,val) in dict">
{{key}} : <input type="text" ng-model="$parent.dict[key]">
</li>
</ul>
<button type="submit"> Submit</button>
</form>
<br/>
<div>
$scope.dict : {{dict}}
</div>
</div>
</body>
</html>
Implementing it in Angular v2 might be on similar lines.
It is possible to get ngRepeat to iterate over the properties of an object using the following syntax:
<div ng-repeat="(key, value) in myObj"> ... </div>
Reference: https://docs.angularjs.org/api/ng/directive/ngRepeat#iterating-over-object-properties

Lose the focus after inserting a letter

I have written a script which represent a json data in 2 ways: JSBin
<!DOCTYPE html>
<html>
<head>
<script src="https://handsontable.github.io/ngHandsontable/node_modules/angular/angular.js"></script>
</head>
<body ng-app="app">
<div ng-controller="Ctrl">
GUI:
<div ng-repeat="item in data">
<input ng-model="item.val">
</div>
<br><br><br>
Textarea:<br>
<textarea rows=10 cols=20 ng-model="dataString"></textarea>
</div>
<script>
var app = angular.module('app', []);
app.controller('Ctrl', ['$scope', '$filter', function($scope, $filter) {
$scope.data = [{val: "1"}, {val: "2"}];
$scope.$watch('data', function(data_new) {
$scope.dataString = $filter('json')(data_new);
}, true);
$scope.$watch('dataString', function(dataString_new) {
$scope.data = JSON.parse(dataString_new);
}, true);
}]);
</script>
</body>
</html>
Thus, modifying the value in GUI will change the string in the textarea (because of $watch('data'); and modifying the string in the textarea will change the GUI (because of $watch('dataString')).
However, the problem is that when we change the value in GUI, we lose the focus after inserting a letter.
Does anyone know how to amend this?
So the problem is that you are iterating over an array (ng-repeat) and changeing the items of the array. The items are removed from the DOM and new one are inserted because they are strings and thereby compared by value. This makes you loose focus.
It's pretty simple to fix though. Just track by index as the objects are in identical order.
Change:
<div ng-repeat="item in data">
To:
<div ng-repeat="item in data track by $index">

Angular - 2 dependent menus binding

I build a ASP.Net MVC 4 + angular website,
I have an upper menu, while a click on one link will open the left menu links accordingly.
suppose you click the first category (1) upper menu, the left menu will be exposed accordingly 1.1, 1.2, 1.3.
Currently, my upper menu is binded, but when clicking on a link (category), nothing happens on the left menu.
if i put alert, i see the data comes back to the client. it is not a data problem.
any idea guys ? :-)
my index.html to contain the 2 partials:
<html>
<head>
<script src="Scripts/angular.min.js"></script>
<script src="app/controllers/menuController.js"></script>
<title></title>
</head>
<body ng-app="generalApp">
<div ng-include="'partials/topMenu.html'"></div>
<div ng-include="'partials/sideMenu.html'"></div>
</body>
</html>
my upper menu (different html file - working):
<div ng-controller="menuController">
<table>
<tr ng-repeat="category in menuCategories">
<td>
<a href="#" ng-click="loadSideMenu('7')">
{{category.CategoryName}}
</a>
</td>
</tr>
</table>
</div>
my side menu (different html file):
<div ng-controller="menuController">
<table>
<tr ng-repeat="category in sideMenu">
<td>{{ category.CategoryName }}</td>
</tr>
</table>
</div>
my js controller code:
(function (angular) {
var generalApp = angular.module('generalApp', [])
.controller('menuController', function ($scope, $http) {
$http.post("Home/GetArticleCategories").then(function (response) {
$scope.menuCategories = response.data;
});
$scope.loadSideMenu = function(category) {
$http.post("Home/GetSideMenu?a="+category).then(function (response) {
$scope.sideMenu = response.data;
});
};
});
}(angular));
my mvc controller to return the faked links upon the category click :
public JsonResult GetSideMenu(int a)
{
var j = Json(new[]{
new { CategoryName = "link 1"},
new { CategoryName = "link 2"}
}
);
return j;
}
Thanks to any idea !!!
You must put upper and left menus inside the same ng-contoller. The way you did every div will have a different $scope. When you populate the data in the upper menu, the $scope from the upper menu will hold the list, and the left will not notice that, because they have different $scopes.
Think as they are the same kind of object, but different instances!

angularjs ng-repeat list not updated when element is added/removed

There is a list of users retrieved from a rest api. Here is the template
<div ng:controller="UserController">
<a ng-click="createUser()">Create User</a>
<div ng-view>
<ul>
<li ng-repeat="user in users">
{[{user.first_name}]} {[{user.last_name}]}
</li>
</ul>
</div>
</div>
The JS:
function UserController($scope, User, Group){
$scope.users = User.query();
$scope.createUser = function(){
//$scope.users = null;
//$scope.users.pop();
//$scope.users.push(new User({id:'5'}));
console.log($scope.users);
}
}
The service: http://dpaste.com/1065440/
All users a retrieved and listed correctly. The problem is that I cannot manipulate the rendered list at all. No matter what I do push, pop or set to null. The list does not change in the template. However the last log statement shows the changes, it prints e.g. NULL when the users array is set to null.
Any ideas where the problem is?
The object you push into the array should be an instance of User
function UserController($scope, User){
$scope.users = User.query();
$scope.createUser = function(){
$scope.users.push(new User({first_name:'Bob', last_name: 'Schmitt'}));
}
}
So, use new User({})
From our conversation, it seems the problem was in the routing. The same outer controller was assigned to the partial that was being loaded in the ng-view. Removing ng:controller="UserController" and moving the createUser button to the partial would solve the problem, but if there's really a need to call the createUser method from outside of ng-view, then all the data related to it will need to be in the outer controller. So, you can keep your outer controller as it is, and change your route to use an empty placeholder controller.
make sure createUser is being called. IE ng-click or something.
<button type="button" ng-click="createUser()">Create User</button>
Your push function looks correct, but your binding in html looks wrong. It should be double curly brackets.
<li ng-repeat="user in users">
{{user.first_name}} {{user.last_name}}
</li>
Added Example I've used previously on adding object.
<div ng-app="main">
<div ng-controller="MyCtrl">
<button ng-click="add()" >Add</button>
<div id="container">
<div ng-repeat="test in tests>{{test.name}}</div>
</div>
</div>
</div>
$scope.tests = {};
$scope.add = function() {
var newTest = {name: 'Test Message'};
$scope.tests.push(newTest);
};

CRUD model with nested model

I'm currently building a CRUD for my app admin. I'm using AngularJS with a RESTfull API.
I can sucessfully save a simple model. But when I have Many-to-many relationship I'm a bit lost when it comes to set the update/create form.
I have build a Plunker to showcase the attempt:
http://plnkr.co/edit/okeNuYBJ5f33gtu6WBoW
EDIT:
Now using checkbox instead of dropdown as Jason suggested:
http://plnkr.co/edit/okeNuYBJ5f33gtu6WBoW
But my problem #3 is still not fixed. How can I save those updated/created relationships?
So I have that User model that has a Many-to-Many relationship with a Role model. I am able to display/list the model with its relationship. When editing a User I load all Roles so UI can build a dropdown of Roles to be selected. I want to have as many dropdown as there is a relationship. So I nested my dropdown in a repeat="userRole in user.role".
When doing an update
First problem: if a user has many roles it display as many dropdown as there is but the selected role for each one is the first relationship.
Second problem: I have a button to add a new role to the loaded user. I'm not sure I made it right since when saving I don't see any trace of the new attached role.
Third problem: when saving I loose the connection from my roles. Only the user is updated. My form is wrong but where is the problem?
When doing a create
I'm not able to link a role to a new user. When I clic on "Add a new role" the first role of the Roles list is pushed to the user. But user is not yet created. So I get an error. Once again my form is wrong. What is the error?
When saving a new user how can I also POST the related roles?
Here is some code in case the Plunker doesn't work:
index.html
<!DOCTYPE html>
<html lang="en" xmlns:ng="http://angularjs.org" data-ng-app="CRUD">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap.no-icons.min.css" />
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/2.0/css/font-awesome.css" />
</head>
<body>
<div class="span6" ng-view></div>
<script src="http://code.angularjs.org/1.1.0/angular.min.js"></script>
<script src="http://code.angularjs.org/1.1.0/angular-resource.js"></script>
<script src="/crud.js"></script>
</body>
</html>
crud.js My AngularJS specific code
var users = [
{'id':1,'name':'User 1', 'role':[{'id':1,'name':'Role 1'},{'id':2,'name':'Role 2'}]},
{'id':2,'name':'User 2', 'role':[{'id':2,'name':'Role 2'}]},
{'id':3,'name':'User 3', 'role':[{'id':1,'name':'Role 1'}]},
{'id':4,'name':'User 4', 'role':[{'id':3,'name':'Role 3'},{'id':2,'name':'Role 2'}]}
];
var roles = [
{'id':1,'name':'Role 1'},
{'id':2,'name':'Role 2'},
{'id':3,'name':'Role 3'}
];
/* Route */
angular.module('CRUD', []).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/create', {templateUrl: 'create.html',controller: ctrlCreate}).
when('/read', {templateUrl: 'read.html',controller: ctrlRead}).
when('/update/:userId', {templateUrl: 'update.html', controller: ctrlUpdate}).
otherwise({redirectTo: 'read'});
}]);
/* Controller CREATE */
function ctrlCreate($scope, $http, $location) {
// dirty hack to find the user to update (in real life it would be loaded via REST)
$scope.user = null;
$scope.roles = roles;
$scope.save = function() {
// dirty hack to change the user (in real life it would be trigger a POST request to the server with updated model)
users.push($scope.user);
//if a scope digestion is already going on then it will get picked up and you won't have to call the $scope.$apply() method
if(!$scope.$$phase) { //this is used to prevent an overlap of scope digestion
$scope.$apply(); //this will kickstart angular to recognize the change
}
$location.path('/');
};
$scope.addRole = function(){
$scope.user.role.push(roles[0]);
};
}
ctrlCreate.$inject = ['$scope','$http','$location'];
/* Controller READ */
function ctrlRead($scope, $http, $location) {
// dirty hack to find the user to update (in real life it would be loaded via REST)
$scope.users = users;
$scope.roles = roles;
}
ctrlRead.$inject = ['$scope','$http','$location'];
/* Controller UPDATE */
function ctrlUpdate($scope, $http, $location, $routeParams) {
$scope.user = null;
$scope.roles = roles;
var id=$routeParams.userId;
// dirty hack to find the user to update (in real life it would be loaded via REST)
for (var i = 0; i < users.length; i++) {
if (users[i].id==id) {
$scope.user=users[i];
console.debug($scope.user.role);
}
}
$scope.save = function() {
// dirty hack to change the user (in real life it would be trigger a PUT request to the server with updated model)
for (var i = 0; i < users.length; i++) {
if (users[i].id==id) {
users[i] = $scope.user;
}
}
//if a scope digestion is already going on then it will get picked up and you won't have to call the $scope.$apply() method
if(!$scope.$$phase) { //this is used to prevent an overlap of scope digestion
$scope.$apply(); //this will kickstart angular to recognize the change
}
$location.path('/');
};
$scope.addRole = function(){
$scope.user.role.push(roles);
console.debug($scope.user.role);
};
}
ctrlUpdate.$inject = ['$scope','$http','$location', '$routeParams'];
Now my templates:
create.html
<form>
<div class="control-group">
<label class="control-label">Name</label>
<input type="text" ng-model="user.name" placeholder="Enter a name here">
</div>
<div ng-repeat="userRole in user.role">
<div class="control-group">
<label class="control-label">Role</label>
<select ng-selected="userRole.id">
<option ng-repeat="role in roles" value="{{role.id}}">{{role.name}}</option>
</select>
</div>
</div>
<button ng-click="addRole()">Attach another role</button>
<br />
<br />
<input type="submit" value="Submit" ng-click="save()" class="btn btn-primary">
Cancel
</form>
read.html
<br />
<table class="table table-bordered table-striped table-centred table-condensed table-hover">
<thead>
<tr>
<th>User Name</th>
<th>Role Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users">
<td>{{user.name}}</td>
<td>
<span ng-repeat="role in user.role">{{role.name}}</span>
</td>
<td>
<a title="edit" href="#/update/{{user.id}}"><i class="icon-edit"></i></a>
</td>
</tr>
</tbody>
</table>
<br />
<i class="icon-plus"></i> Create a new user
update.html
<form>
<div class="control-group">
<label class="control-label">Name</label>
<input type="text" ng-model="user.name" placeholder="Enter a name here">
</div>
<div ng-repeat="userRole in user.role">
<div class="control-group">
<label class="control-label">Role</label>
<select ng-selected="userRole.id">
<option ng-repeat="role in roles" value="{{role.id}}">{{role.name}}</option>
</select>
</div>
</div>
<button ng-click="addRole()">Attach another role</button>
<br />
<br />
<input type="submit" value="Submit" ng-click="save()" class="btn btn-primary">
Cancel
</form>
Please made advice if you see some bad coding or wrong architecture (I think I could do some directive for instance when it comes to add a new role maybe?). I hope this is clear enough.
Thanks!
You can solve the first 2 problems you are having by redesigning your UI. Instead of using dropdowns use a checkbox field. Example plnkr:
http://plnkr.co/edit/hgq2hmbRty7B9oryQnkm
Once you have less moving parts on your page, hopefully it will be easy to debug the 3rd problem.

Resources