Angular Application not updating UI on save( ) - angularjs

I have the following simple CRUD based code (most taken from the AwesomeTodo tutorial).
It's one html file, just a grid, and when create() is called, I expect the UI to update.
THIS APP WORKS! It just doesn't refresh the view when i create new records, I have to refresh the browser which obviously reloads the resource.
<div id="apps-table" compile="html">
<table ng-controller="AppCtrl" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Name</th>
<th>URL</th>
<th>Label</th>
<th>Description</th>
<th>Status</th>
<th>Location</th>
<th><a ng-click="promptForNew()"><li class="icon-plus"></li> New App</a></th>
</tr>
</thead>
<tbody>
<tr style="display:none" id="add-app-row">
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.name"></td>
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.url"></td>
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.label"></td>
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.description"></td>
<td><select ng-model="app.fields.is_active" ng-change = "formChanged()">
<option value=1>Active</option>
<option value=0>Inactive</option>
</select>
</td>
<td><select ng-model="app.fields.is_url_external" ng-change = "formChanged()">
<option value=0>Internal</option>
<option value=1>External</option>
</select></td>
<td>
<a ng-click="create()" id="save_new" class="btn btn-small btn-primary disabled" href=""><i class="icon-save"></i></a>
</td>
</tr>
<tr ng-repeat="app in Apps.record" id="row_{{app.fields.id}}">
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.name"></td>
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.url"></td>
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.label"></td>
<td><input class="input-small" type="text" ng-change = "formChanged()" ng-model="app.fields.description"></td>
<td><select ng-model="app.fields.is_active" ng-change = "formChanged()">
<option value=1>Active</option>
<option value=0>Inactive</option>
</select>
</td>
<td><select ng-model="app.fields.is_url_external" ng-change = "formChanged()">
<option value=0>Internal</option>
<option value=1>External</option>
</select></td>
<td>
<a ng-click="save()" id="save_{{app.fields.id}}" class="btn btn-small btn-primary disabled" href=""><i class="icon-save"></i></a>
<a class="btn btn-small btn-danger" ng-click="delete()"><i class="icon-trash"></i></a>
</td>
</tr>
</tbody>
</table>
var AdminApp = angular.module("AdminApp", ["ngResource"]).
config(function($routeProvider) {
$routeProvider.
when('/', { controller: AppCtrl, templateUrl: 'applications.html' }).
otherwise({ redirectTo: '/' });
});
AdminApp.factory('App', function($resource) {
return $resource('/rest/system/app/:id/?app_name=admin', {}, { update: { method: 'PUT'}});});
var AppCtrl = function ($scope, App) {
$scope.Apps = App.get();
$scope.formChanged = function(){
$('#save_' + this.app.fields.id).removeClass('disabled');
};
$scope.promptForNew = function(){
$('#add-app-row').show();
};
$scope.save = function () {
var records = {};
var record = {};
record.fields = this.app.fields;
delete record.fields.name;
records.record = record;
var id = this.app.fields.id;
App.update({id:id}, records, function () {
$('#save_' + id).addClass('disabled');
});
};
$scope.create = function() {
var currentScope = $scope;
var records = {};
var record = {};
record.fields = this.app.fields;
records.record = record;
App.save(records, function() {
$('#add-app-row').hide();
});
};
$scope.delete = function () {
var id = this.app.fields.id;
App.delete({ id: id }, function () {
$("#row_" + id).fadeOut();
});
};
};
Here is the index.html:
<!DOCTYPE html>
<html ng-app="AdminApp">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="/lib/web-core/css/bootstrap.min.css" type="text/css"/>
<link rel="stylesheet" href="/lib/web-core/css/font-awesome.min.css" type="text/css"/>
<link rel="stylesheet" href="css/style.css" type="text/css"/>
</head>
<body>
<div class="container">
<div ng-view></div>
</div>
<script src="/lib/web-core/js/angular.js"></script>
<script src="js/app.js"></script>
<script src="js/resource.js"></script>
<script src="/lib/web-core/js/jquery.js"></script>
<script src="/lib/web-core/js/bootstrap.min.js"></script>
</body>
</html>
All my actions are through ng-click, so I dont appear to be doing anything "outside Angular"
Question is, is this code working as it should? Or should it actually update?
Only way i can update scope is manually using :
$scope.Apps.record.push($scope.app);
thats hideous. Cant use apply or digest, nor should I have to.

Glancing through your code and the jsFiddle, I've come across the following issues so far:
The framework loading needs to be set to "no wrap (head)" instead of "onLoad" in the jsFiddle settings
The ngApp directive is not used anywhere to bootstrap the application
There is an ngController directive specifying the AppCtrl controller, but that controller isn't registered with the application module using module.controller (it's just a free-standing variable)
The controller functions invoked via ngClick use jQuery to manipulate the DOM--not only is this a big no-no in Angular (use directives, not controllers, for your DOM manipulation), but the jQuery library isn't even included in the jsFiddle.

Related

Can't retrieve data from controller AngularJS

I am learning AngularJS (so I'm a noob to this) I'm following a tutorial on Udemy. I even looked at the docs and have tried the wiring they have presented. I just can't seem to get the customers data in the table. What am I doing wrong and an explanation would be appreciated so I can learn the right way of AngularJS for the interview? Any help is much appreciated. By the way I'm using the latest version of AngularJS 1.7
(function () {
var CustomersController = function($scope) {
$scope.sortBy = 'name';
$scope.reverse = false;
$scope.customers = [{name:'sanjeet', joined:'2000-12-02', orderTotal: 10.096, age: 26}, {name:'gurpreet', orderTotal:201.961, joined:'2005-12-07',age: 24}, {name:'nikki', orderTotal: 14.561, joined:'2001-11-02', age: 25}];
$scope.doSort = function(propName) {
$scope.sortBy = propName;
$scope.reverse = !scope.reverse;
};
};
angular.module('app').contoller('CustomersController', CustomersController)
}())
// app.controller('CustomersController', ['$scope', function($scope) {
// $scope.sortBy = 'name';
// $scope.reverse = false;
//
// $scope.customers = [{name:'sanjeet', joined:'2000-12-02', orderTotal: 10.096, age: 26}, {name:'gurpreet', orderTotal:201.961, joined:'2005-12-07',age: 24}, {name:'nikki', orderTotal: 14.561, joined:'2001-11-02', age: 25}];
//
// $scope.doSort = function(propName) {
// $scope.sortBy = propName;
// $scope.reverse = !scope.reverse;
// };
// }])
<!DOCTYPE html>
<html ng-app>
<head>
<title>My first AngularJS project</title>
<link href="styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h3>Customers</h3>
Filter: <input type="text" ng-model="customerFilter.name"/>
<br />
<table ng-controller="CustomersController">
<tr>
<th ng-click="doSort('name')">
Name
</th>
<th ng-click="doSort('age')">
Age
</th>
<th ng-click="doSort('joined')">
Joined
</th>
<th ng-click="doSort('orderTotal')">
Order Total
</th>
</tr>
<tr ng-repeat="customer in customers | filter: customerFilter | orderBy:sortBy:reverse" >
<td>
{{customer.name}}
</td>
<td>
{{customer.age}}
</td>
<td>
{{customer.joined | date: 'yyyy-MM-dd'}} <!--medium, longDate -->
</td>
<td>
{{customer.orderTotal | currency: 'y'}}
</td>
</tr>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
<script src="app/controllers/customersController.js"></script>
<script> var app = angular.module('app', [])</script>
</body>
</html>
You have multiple issues causing your app to not load. You should consider using angular.js rather than angular.min.js to receive better error messages in the console, which would help you to identify your errors.
You have a typo in your controller code. angular.module('app').contoller('CustomersController', CustomersController). .controller is missing an r.
You cannot use <html ng-app> in newer releases of angular (1.3+). This is a very old syntax. The correct syntax is to identify the app module. <html ng-app="app">.
Your App module must be defined before the controllers that use it. i.e.
<script> var app = angular.module('app', [])</script>
has to come before
<script src="app/controllers/customersController.js"></script>
Your Filter: <input type="text" ng-model="customerFilter.name"/> line is outside the controller, and thus won't function. You should move the ng-controller="CustomersController" to the body instead of the table, or a wrapping div.
I created a plunker and updated your code, showing the app in a functional status.

Response Data is not binding to table with ng-repeat

I am working on a sample app using angularJS.
I have a service which returns JSON object, I am trying to bind the response to a table.
Controller -
(function () {
var app = angular.module('searchApp', []);
app.config(function ($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist(['http://localhost:11838/SearchProfiles/get']);
});
var controller = app.controller('searchController', ["$scope", "$http", function ($scope, $http) {
$scope.profileName = "";
$http.get("http://localhost:11838/SearchProfiles/GetAllRuleSets")
.then(function (response) {
$scope.ruleSets = response.data;
});
$scope.searchProfiles = function () {
$http.get("http://localhost:11838/SearchProfiles/GetProfiles?profileName=" + $scope.profileName
+ "&ruleSetName=" + $scope.selectedRuleSet)
.then(function (response) {
$scope.showProfiles = true;
$scope.profiles = response.data;
console.log($scope.profiles);
});
};
$scope.clearForm = function () {
$scope.profileName = "";
$scope.selectedRuleSet = "";
};
}]);
})();
cshtml -
#{
ViewBag.Title = "Search Profiles";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Search Profiles</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
#Scripts.Render("~/Scripts/angular")
#Styles.Render("~/Content/css/scss")
</head>
<body ng-app="searchApp">
<div ng-controller="searchController" id="content">
<label>Profile Name: </label>
<input type="text" name="profileName" ng-model="profileName" /><br />
<label>RuleSet Name: </label>
<select ng-model="selectedRuleSet" ng-options="x for x in ruleSets"></select><br />
<button name="search" ng-click="searchProfiles()">Search</button>
<button name="clear" ng-click="clearForm()">Clear</button>
</div>
<div ng-controller="searchController" id="profilesDiv">
<table>
<tr ng-repeat="x in profiles">
<td>{{ x.ProfileName }}</td>
<td>{{ x.CreationDate }}</td>
</tr>
</table>
</div>
</body>
I am getting back following response -
[{"ProfileName":"Profile3","CreationDate":"1/9/2017"},{"ProfileName":"Profile4","CreationDate":"12/30/2016"}]
but even after setting $scope.profiles I am not seeing table on UI.
I'm fairly new to angular, am I missing something obvious.
Any help is appreciated.
The problem is that you are fetching the data in one scope (the div content where you declare ng-controller="searchController") and then trying to
view the data in another scope (the div profilesDiv where you again declare the ng-controller="searchController").
To solve the problem you need to remove ng-controller="searchController" from the profilesDiv and move the profilesDiv inside the content div.
Like this:
<body ng-app="searchApp">
<div ng-controller="searchController" id="content">
<label>Profile Name: </label>
<input type="text" name="profileName" ng-model="profileName" /><br />
<label>RuleSet Name: </label>
<select ng-model="selectedRuleSet" ng-options="x for x in ruleSets"></select><br />
<button name="search" ng-click="searchProfiles()">Search</button>
<button name="clear" ng-click="clearForm()">Clear</button>
<div id="profilesDiv">
<table>
<tr ng-repeat="x in profiles">
<td>{{ x.ProfileName }}</td>
<td>{{ x.CreationDate }}</td>
</tr>
</table>
</div>
</div>
</body

Angular JS - dynamically add custom href link to value in table using ng-repeat based on a different value.

I am creating a table using ng-repeat that display some tickets info. Currently in the column "Ticket No" I am adding href link (when user click on the "Ticket No" it will open new tab and the URL will take the ticket no as parameter.
This a plunker I've created so it can show functionality as described above http://plnkr.co/edit/GB8WWz?p=preview
The problem I have now is that the href link might vary and it depends on the account column value. So if my "account = foo" set the href link of the Ticket No to "http://myfoopage.foo/ticketid...etc". If my "account = boo" set the href link for the Ticket No to "http://myboopage.boo/ticketid...etc".
Any idea on how to approach that ?
scriptang.js
angular.module('plunker', ['ui.bootstrap']);
function ListCtrl($scope, $dialog) {
$scope.items = [
{ticket: '123', description: 'foo desc',account:'foo'},
{ticket: '111', description: 'boo desc',account:'boo'},
{ticket: '222', description: 'eco desc',account:'eco'}
];
}
// the dialog is injected in the specified controller
function EditCtrl($scope, item, dialog){
$scope.item = item;
$scope.save = function() {
dialog.close($scope.item);
};
$scope.close = function(){
dialog.close(undefined);
};
}
index.html
<!doctype html>
<html ng-app="plunker">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
<script src="http://angular-ui.github.com/bootstrap/ui-bootstrap-tpls-0.1.0.js"></script>
<script src="scriptang.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<div ng-controller="ListCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Ticket No</th>
<th>Description</th>
<th>Account</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items">
<td>{{item.ticket}}</td>
<td>{{item.description}}</td>
<td>{{item.account}}</td>
<td><button class="btn btn-primary" ng-click="edit(item)">Edit</button></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
Updated plnkr here, I've made use of the ng-attr directive in combination with a function that creates an url.
$scope.getUrl = function (item) {
var url = '';
if(item.account === 'foo')
url = 'http://mywebpage.foo';
else
url = 'http://mwebpage.boo';
url += '/ticketid='+item.ticket
return url;
}

Custom directive with 2 input sliders

I am in the process of learning how to make custom directives in AngularJS.
I have this simple program that works well and I would like to refactor it into a custom directive. I would like the directive element to be the div that is a child of the body tag. I am having trouble imagining how the slider values would be passed up and down the DOM from the input tags to the div and the other way around.
Code:
<html ng-app="DualSlidersApp">
<head>
<meta charset="UTF-8">
<title>DualSlidersApp</title>
<style>
.indented { margin-left: 61px; }
</style>
<script src="js/angular.js"></script>
<script>
'use strict';
var app = angular.module('DualSlidersApp', []);
app.controller('DualSlidersController', ['$scope', function($scope) {
$scope.firstRangeSliderValue = 10;
$scope.secondRangeSliderValue = 20;
$scope.reconcileSliders = function reconcileSliders(drivenByWhichSlider) {
if ($scope.firstRangeSliderValue > $scope.secondRangeSliderValue) {
if (drivenByWhichSlider === 'drivenByTheFirstSlider') {
$scope.secondRangeSliderValue = $scope.firstRangeSliderValue;
} else {
$scope.firstRangeSliderValue = $scope.secondRangeSliderValue;
}
}
}
}]);
</script>
</head>
<body ng-controller="DualSlidersController">
<div>
<table>
<tr>
<td>
<input type="range" min="10" max="30" step="2"
ng-model="firstRangeSliderValue"
ng-change="reconcileSliders('drivenByTheFirstSlider');">
</td>
<td>{{ firstRangeSliderValue }}</td>
</tr>
<tr>
<td>
<input type="range" min="20" max="40" step="2" class="indented"
ng-model="secondRangeSliderValue"
ng-change="reconcileSliders('drivenByTheSecondSlider');">
</td>
<td>{{ secondRangeSliderValue }}</td>
</tr>
</table>
</div>
</body>
</html>
I'd assume you meant sharing data between the directive and the controller when you wrote "how the slider values would be passed up and down the DOM from the input tags to the div and the other way around".
Binding is probably the best use of angular that you can implement here. bind 2 variables from the controller's scope to the directive's scope, each one represents the value of each slider. I would also suggest to pass a function to the directive's scope as well, so the ng-change would be calledback in the controller. It should look something like this:
directive.js
app.directive('dualSlidersDirective', function() {
return {
scope: {
sliderOneValue: '=',
sliderTwoValue: '=',
onSliderChange: '&'
},
templateUrl: 'directive.html'
};
});
directive.html:
<div>
<table>
<tr>
<td>
<input type="range" min="10" max="30" step="2"
ng-model="sliderOneValue"
ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheFirstSlider\'})">
</td>
<td>{{ sliderOneValue }}</td>
</tr>
<tr>
<td>
<input type="range" min="20" max="40" step="2" class="indented"
ng-model="sliderTwoValue"
ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheSecondSlider\'})">
</td>
<td>{{ sliderTwoValue }}</td>
</tr>
</table>
</div>
And don't forget to bind the relevant variables and function from your controller's scope in the main html, notice that functions receive their arguments as a hash (the key being the name of the parameter and the value being the value of it). it is very important to keep the same names of the variables throughout the script
So the entire thing should look something like this:
<html ng-app="DualSlidersApp">
<head>
<meta charset="UTF-8">
<title>DualSlidersApp</title>
<style>
.indented { margin-left: 61px; }
</style>
<script src="js/angular.js"></script>
<script>
'use strict';
var app = angular.module('DualSlidersApp', []);
app.controller('DualSlidersController', ['$scope', function($scope) {
$scope.firstRangeSliderValue = 10;
$scope.secondRangeSliderValue = 20;
$scope.reconcileSliders = function reconcileSliders(drivenByWhichSlider) {
if ($scope.firstRangeSliderValue > $scope.secondRangeSliderValue) {
if (drivenByWhichSlider === 'drivenByTheFirstSlider') {
$scope.secondRangeSliderValue = $scope.firstRangeSliderValue;
} else {
$scope.firstRangeSliderValue = $scope.secondRangeSliderValue;
}
}
}
}]);
app.directive('dualSlidersDirective', function() {
return {
scope: {
sliderOneValue: '=',
sliderTwoValue: '=',
onSliderChange: '&'
},
template:
'<div>' +
'<table>' +
'<tr>' +
'<td>' +
'<input type="range" min="10" max="30" step="2"' +
'ng-model="sliderOneValue"' +
'ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheFirstSlider\'})">' +
'</td>' +
'<td>{{ sliderOneValue }}</td>' +
'</tr>' +
'<tr>' +
'<td>' +
'<input type="range" min="20" max="40" step="2" class="indented"' +
'ng-model="sliderTwoValue"' +
'ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheSecondSlider\'})">' +
'</td>' +
'<td>{{ sliderTwoValue }}</td>' +
'</tr>' +
'</table>' +
'</div>'
};
});
</script>
</head>
<body ng-controller='DualSlidersController'>
<dual-sliders-directive slider-one-value='firstRangeSliderValue' slider-two-value='secondRangeSliderValue' on-slider-change='reconcileSliders(drivenByWhichSlider)'></dual-sliders-directive>
</body>
</html>

angular $injector:unpr Error

Im trying to use Resource in my app and I get the following error:
$injector:unpr
"errors.angularjs.org/1.3.15/$injector/unpr?p0=mkResourceProvider%20%3C-%20mkResource%20%3C-%20mkController".
Here is my Code
app
var logApp = angular.module('mkApp', ['ngResource','ngRoute']);
Controller
angular.module('mkApp').controller('mkController', function ($scope, mkResource) {
$scope.ddl = [];
mkResource.LookUp(function (data) {
$scope.ddl = data;
console.log($scope.ddl);
});});
Resource
angular.module('mkApp').factory('mkResource', function ($resource, $http) {
var lookup = $resource('api/HowOftens');
function LookUp() {
return lookup.query();
}
return {
LookUp: LookUp
}});
HTML
<head>
<script src="../Scripts/angular.min.js"></script>
<script src="../Scripts/jquery-2.1.3.min.js"></script>
<title></title>
<div ng-app="mkApp">
<div ng-controller="mkController">
<table>
<tr>
<td> F Name</td>
<td> L Name</td>
</tr>
<tr>
<td><input type="text" /></td>
<td><input type="text" /></td>
</tr>
<tr>
<td>
<slect></slect>
</td>
</tr>
</table>
</div>
</div>
<script src="MkApp/MkApp.js"></script>
<script src="Controller/MkController.js"></script>
<script src="../Scripts/angular-resource.min.js"></script>
<script src="../Scripts/angular-route.min.js"></script>
From all the reading ive been doing i understand its injection error. I don't understand what Im not injecting properly though. The final plan was to load ddls from data I will receive.
You would need to be loading your angular-route and angular-resource (your libs) before you actually load up your angular app files.

Resources