Angularjs array push UI does not get's refreshed - angularjs

I want to show a list of movies and add a new movie by adding it to the array.
The html file:
<!-- FOREACH the movies -->
<div ng-controller="homeController" ng-repeat="movie in movies | orderBy : order">
<div class="movie-wrapper">
<h1>{{movie.name}}</h1>
<p>IMDB: IMDB</p>
</div>
</div>
<!-- ADD a new movie to the array -->
<div ng-controller="homeController">
<h3>Add new movie:</h3>
<form name="movieForm" ng-submit="addMovie(movieInfo)">
<div class="form-group new-movie">
<label for="Title">Title:</label>
<input type="text" class="form-control" name="title" ng-model="movieInfo.title">
</div>
<div class="form-group new-movie">
<label for="IMDB">IMDB:</label>
<input type="text" class="form-control" name="imdb" ng-model="movieInfo.imdb">
</div>
<button type="submit" class="btn btn-primary">Add movie</button>
</form>
</div>
The javascript file:
var app = angular.module('movie-app', ["ngRoute"]);
app.controller("homeController", function($scope) {
$scope.movies = getMovies();
// Method to add a new movie
$scope.addMovie = function(movieInfo) {
$scope.movies.push({
name : movieInfo.title,
imdb_url: movieInfo.imdb
});
console.log("Movie added");
}
});
function getMovies() {
return [{
name: 'Inception',
imdb_url: 'http://www.imdb.com/title/tt1375666/'
}];
}
After pressing the submit button, in the console I did not get any error message, but somehow the UI does not get's refreshed.
I think that somehow the controller does not get a reference/bind to the same array or dom element when I'm pushing the new entry.

First of all, i'm pretty sure you should have an error in your console
You make 3 mistakes :
1. You have an error in your getMovies() function (missing curly brackets {})
function getMovies() {
return [{
name: 'Inception',
imdb_url: 'http://www.imdb.com/title/tt1375666/'
}];
}
Why curly brackets are important ?
name and imdb_url are properties object. In JavaScript, an object must have curly brackets before and after. But your function getMovies returns and array of 1 element, so you have to surround your object with brackets []
2. You also have an error when you call console.log (missing quote ")
console.log("Movie added);
3. And the last one : you have to remove the parenthesis }) (the line after console.log)
The result :
angular.module('movieApp', [])
.controller("homeController", function($scope) {
$scope.movies = getMovies();
// Method to add a new movie
$scope.addMovie = function(movieInfo) {
$scope.movies.push({
name: movieInfo.title,
imdb_url: movieInfo.imdb
});
console.log("Movie added");
};
});
function getMovies() {
return [{
name: 'Inception',
imdb_url: 'http://www.imdb.com/title/tt1375666/'
}];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="movieApp" ng-controller="homeController">
<!-- FOREACH the movies -->
<div ng-repeat="movie in movies | orderBy : order">
<div class="movie-wrapper">
<h1>{{movie.name}}</h1>
<p>IMDB: IMDB
</p>
</div>
</div>
<!-- ADD a new movie to the array -->
<div>
<h3>Add new movie:</h3>
<form name="movieForm" ng-submit="addMovie(movieInfo)">
<div class="form-group new-movie">
<label for="Title">Title:</label>
<input type="text" class="form-control" name="title" ng-model="movieInfo.title">
</div>
<div class="form-group new-movie">
<label for="IMDB">IMDB:</label>
<input type="text" class="form-control" name="imdb" ng-model="movieInfo.imdb">
</div>
<button type="submit" class="btn btn-primary">Add movie</button>
</form>
</div>
</div>

Related

angularjs - Update ng-model value from controller?

View 1:
<div ng-controller="ctrl1">
<button ng-click="goToExtendedForm({'name':'aaa'})">
</button>
</div>
ctrl1:
$scope.selectedList = {
name: ""
};
$scope.goToForm = function(e) {
$scope.selectedList.name = e.name;
$state.go('view2');
console.log(e); // prints updated value
};
View 2:
<div ng-controller="ctrl1">
<input
id="name"
name="name"
type="text"
ng-model="selectedList.name"
ng-readonly="true"
/>
</div>
But the input box is always empty, even though to get to the view the goToForm() is called. Why doesn't it update the HTML value?
Views are changed with ui.router's $state.
From your description, your code is supposed to work. Check if you are passing the right parameter into the function. Here is a working demo:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.selectedList = {
name: ""
};
$scope.goToForm = function(e) {
$scope.selectedList.name = e.name;
console.log(e); // prints updated value
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<button ng-click="goToForm({'name':'aaa'})">Change</button>
<br>
<input type="text" ng-model="selectedList.name" ng-readonly="true" />
</div>
Try adding $scope.$apply() at the end of your $scope.goToForm function
Try this ;
HTML Code
<div ng-app>
<div ng-controller="ClickToEditCtrl">
<input
id="name"
name="name"
type="text"
ng-model="selectedList.name"
ng-readonly="true"
/>
<button ng-click="goToForm(testUserDetails)" >Go To</button>
</button>
</div>
</div>
Define controller like this;
function ClickToEditCtrl($scope) {
$scope.selectedList = {
name: ""
};
$scope.testUserDetails ={
name: "nimal"
}
$scope.goToForm = function(e) {
$scope.selectedList.name = e.name;
console.log(e); // prints updated value
};
}

Angular Two-Way data-binding not refreshing ng-repeat on $scope change

My functionality is very simple, and I have to imagine very common. I have a page that lists a collection of 'age groups', and provides functionality to add an 'age group'. I simply would like to have the page immediately reflect the change when a new 'age group' is added.
In early versions, I had simply used the $rootScope in my service to put all 'age groups' in the $rootScope when the $http request was completed. On this latest refactoring I am removing all use of $rootScope in my app, and it is becoming problematic.
The code for the HTML view is as follows: [NOTE: all code considered extraneous has been removed from all code snippets]
<div id="tabContent" class="tab-pane fade in active" ng-show="subIsSelected(1)">
<div class="row">
<div class="col-xs-12">
<form class="form-inline">
<button type="button" class="btn btn-success" ng-click="openAddAgeGroup()">Add Age Group</button>
</form>
</div>
</div>
<hr />
<div class="row row-content">
<div class="col-xs-12">
<h4 ng-show="!areAgeGroups()">No current age groups.</h4>
<ul class="media-list" ng-show="areAgeGroups()">
<li class="media" ng-repeat="ageGroup in ageGroups" style="padding:10px; background: lightGray;">
<div class="media-left media-top" >
<img class="media-object img-thumbnail" style="width: 75px;" ng-src="./images/trfc.png" alt="club logo">
</div>
<div class="media-body">
<h3 class="media-heading" style="padding-top: 20px;">{{ageGroup.name}}</h3>
<button type="button" class="btn btn-xs btn-primary" style="width: 50px;" ng-click="openEditAgeGroup(ageGroup)">Edit</button>
<button type="button" class="btn btn-xs btn-danger" style="width: 50px;" ng-click="deleteAgeGroup(ageGroup)">Delete</button>
</div>
</li>
</ul>
</div>
</div>
</div>
when first loaded, the page correctly shows all 'age groups' that are in the $scope.ageGroups array.
On clicking the button to add an 'age group', an ng-dialog is created as follows:
$scope.openAddAgeGroup = function() {
console.log("\n\nOpening dialog to add age group");
ngDialog.open({ template: 'views/addAgeGroup.html', scope: $scope, className: 'ngdialog-theme-default', controller:"HomeController" });
};
and that dialog is populated as such:
<div class="ngdialog-message">
<div class="">
<h3>Add a New Age Group</h3>
</div>
<div> </div>
<div>
<form ng-submit="addAgeGroup()">
<div class="form-group">
<label class="sr-only" for="name">Age Group Display Name</label>
<input type="text" class="form-control" id="name" placeholder="age group name" ng-model="ageGroupForm.name">
</div>
<div class="form-group">
<label class="sr-only" for="birthyear">Birth Year</label>
<input type="text" class="form-control" id="birthyear" placeholder="birth year" ng-model="ageGroupForm.birthyear">
</div>
<div class="form-group">
<label class="sr-only" for="socceryear">Soccer Year</label>
<div class="input-group">
<div class="input-group-addon">U</div>
<input type="text" class="form-control" id="socceryear" placeholder="soccer year" ng-model="ageGroupForm.socceryear">
</div>
</div>
<button type="submit" class="btn btn-info">Add</button>
<button type="button" class="btn btn-default" ng-click=closeThisDialog("Cancel")>Cancel</button>
</form>
</div>
</div>
When the form is submitted, the 'age group' is added to the database, from the controller:
'use strict';
angular.module('ma-app')
.controller('HomeController', ['$scope', 'ngDialog', '$state', 'authService', 'coreDataService', 'userService', '$rootScope', 'clubService', 'schedulingService', function($scope, ngDialog, $state, authService, coreDataService, userService, $rootScope, clubService, schedulingService) {
...
$scope.addAgeGroup = function() {
coreDataService.addAgeGroup($scope.ageGroupForm)
.then(function(response) {
coreDataService.refreshAgeGroups()
.then(function(response) {
coreDataService.setAgeGroups(response.data);
$scope.ageGroups = coreDataService.getAgeGroups();
console.log("\n\n\nretrieved age groups and put them in scope");
console.log($scope.ageGroups);
ngDialog.close();
});
}, function(errResponse) {
console.log("Failed on attempt to add age group:");
console.log(errResponse);
});
};
The coreDataService is defined as follows:
'use strict';
angular.module('ma-app')
.service('coreDataService', ['$http', 'baseURL', 'googleGeolocateBaseURL', 'googleGeocodeKey', 'googleMapsBaseURL', function($http, baseURL, googleGeolocateBaseURL, googleGeocodeKey, googleMapsBaseURL) {
var ageGroups = {};
var ageGroupsLoaded = false;
this.getAgeGroups = function() {
return ageGroups;
};
this.setAgeGroups = function(newAgeGroups) {
ageGroups = newAgeGroups;
ageGroupsLoaded = true;
};
this.addAgeGroup = function(formData) {
//post age group:
var postString = '{ "birth_year": "' + formData.birthyear + '", "soccer_year": "U' + formData.socceryear + '", "name": "' + formData.name + '" }';
console.log("Posting age group with string: " + postString);
return $http({
url: baseURL + 'age_groups/',
method: 'POST',
headers: {
'content-type': 'application/json'
},
data: postString
});
};
}]);
So, when an 'age group' is added, console logging indicates that the new 'age group' is in the collection now stored in the $scope.ageGroups array, but the HTML's ng-repeat does not reflect the new 'age group'. Only when I navigate to another tab in the interface, and then return to the tab containing the 'age groups' is the newly added 'age group' displayed.
Updated:
After you update your value you need to tell angular that its been updated. If you wrap your code in a $timeout. On the next digest cycle the view will get updated. See here for more information.
...
$timeout(function() {
$scope.ageGroups = coreDataService.getAgeGroups();
// anything you want can go here and will safely be run on the next digest.
})
...
The $timeout basically runs on the next $digest cycle thus updating your value in the view.
I would suggest wrapping your ng-repeat part in a directive, fire an event on add method and listen to it from the directive:
this.addAgeGroup = function(formData) {
// do stuff
$scope.$broadcast('itemadded');
}
Inside your directive link function:
$scope.$on('itemadded', function() {
$scope.ageGroups = coreDataService.getAgeGroups();
});
I would recommend using the ControllerAs syntax which will refresh the ng-repeat, if the array to which it is bound to gets updated.
Refer the answer given here - ng-repeat not updating on update of array

how to remove the value of an input field using angularjs

<div class="info-block" ng-app="">
<div ng-controller="Note">
<div class="checkbox">
<label>
<p><b>Primary Publication: </b>
{{ form_widget(form.input_ppubs, { 'attr': {'class': 'valOption'}}) }}
</p>
</label>
</div>
<div ng-repeat="item in items">
<input type="text" placeholder="new input" ng-model="item.primaryPub">
</div>
<button type="button" ng-click="add()">New Item</button>
</div>
I am trying to retrieve the value of the html input field and remove it from input upon a button click
I am able to get the code, but I don't know how to remove the code.
var Note = function($scope){
$scope.items = [];
$scope.add = function () {
//angular way of implementing document.getElementByID();
pub1 = angular.element('#form_input_ppubs').val();
$scope.items.push({
primaryPub: pub1
});
};
}
You don't have to retrieve your items like this. It's ugly and not the angular way. angular.element('#form_input_ppubs').val();
Instead, simply reference it in your input using ngModel.
Declare it in your scope.
$scope.inputItem = null;
HTML
<input ng-model="inputItem " />
Use it in your function:
$scope.addItem = function(item) {
$scope.items.push(item);
//reset the model
$scope.inputItem = null;
}
Call it using ng-click
<button type="button" ng-click="addItem(inputItem)">Add Item</button>
If you do:
console.log($scope.items);
You should see an entry for primaryPub, the model for your input. Then you can target it by nulling the model, so:
$scope.items.primaryPub = null;
However you're using this inside an ng-repeat:
<div ng-repeat="(i, item) in items">
<input type="text" placeholder="new input" ng-model="items[i].primaryPub">
</div>
So your console.log (if you have more than one item in 'items') should show an array-like structure for primaryPub.

validate dynamic form before submitting angular

I'm dynamically creating forms with ng-repeat and they have some validation attributes (simplified version):
<div class="row" ng-repeat="defect in model.defects">
<form name="form_{{defect.id}}" novalidate>
<input ng-model="defect.name" required/>
<input type="submit" ng-click="saveDefect(defect)"/>
</form>
</div>
Basically what I want to do is this:
$scope.saveDefect = function (defect) {
if ($scope.<how to get the form name here>.$invalid) {
return;
}
}
Since the form name has been created dynamically with an id how do I access it? Other ways of doing the same are also welcome ofcourse :)
You can use the bracket notation to access it :
$scope["form_"+defect.id]
What I advise you to do is :
var app = angular.module("App", []);
app.controller("Ctrl", function($scope) {
$scope.forms = {};
$scope.list = [{id: 1}, {id: 2}];
$scope.save = function(item) {
if ($scope.forms["form_" + item.id].$invalid) {
alert("error on form_" + item.id);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="App" ng-controller="Ctrl">
<div class="row" ng-repeat="item in list">
<form name="forms.form_{{item.id}}" novalidate>
<input ng-model="item.name" required/>
<input type="submit" ng-click="save(item)" />
</form>
</div>
</body>

How to validate form when the forms are inserted using ng-include

I have 3 different form pages which are inserted using ng-include into DOM within a bootstrap modal window. What is the best way to do validation in every form and do a complete form submit(for all the 3 forms) in scenario like this?
HTML
<div ng-switch on="page">
<div ng-switch-when="Games">
<div ng-include="'Games.html'"></div>
</div>
<div ng-switch-when="Music">
<div ng-include="'Music.html'"></div>
</div>
<div ng-switch-when="Videos">
<div ng-include="'Videos.html'"></div>
</div>
</div>
Demo : http://plnkr.co/edit/D1tMRpxVzn51g18Adnp8?p=preview
There is to find a way to validate data yet
(may be with jqueryvalidation) but can be a starting point.
I think there is no way to get the value of games.$valid
so I've thought of
var app = angular.module("myApp", [])
app.controller("FormsCtrl", function($scope) {
//console.log($scope);
// $scope.items = ['Games', 'Music', 'Videos'];
$scope.$on('someEvent',function(e,a){
console.log(a);
})
});
app.directive("myform", function() {
return {
restrict: "A",
link:function(scope,element,attrs){
element.bind('submit',function(e){
var isValid = false; // TO DO :)
scope.$emit('someEvent', [attrs.fname,isValid]);
});
}
}
});
<div ng-controller="FormsCtrl">
<div ng-switch on="page">
<div ng-switch-when="Games">
<div ng-include="'Games.html'"></div>
</div>
<div ng-switch-when="Music">
<div ng-include="'Music.html'"></div>
</div>
<div ng-switch-when="Videos">
<div ng-include="'Videos.html'"></div>
</div>
</div>
</div>
<form name="games" class="simple-form" myform fname="games">
<input type="text" ng-model="prefix.games.name" name="uName" required /><br>
</form>
EDIT
A smarter quicker way :)
app.controller("FormsCtrl", function($scope) {
$scope.mySubmit = function(isValid){
console.log(isValid);
}
});
<form name="games" class="simple-form" ng-submit="mySubmit(games.$valid)">
<input type="text" ng-model="prefix.games.name" name="uName" required /><br>
</form>

Resources