AngularJS doesn't update view until refresh? - javascript

I am currently facing a problem, which has to do with views. I am making an app, which allows for users to create polls. When the poll that a user creates is submitted, I call a POST route to store it:
$scope.userVal = Auth.getCurrentUser();
$http.post('/api/users/update' + $scope.userVal._id, {polls: $scope.polls}).success(function(res){
//console.log("res: ", res);
});
Essentially, I get the user info,and use his id to store the new poll in a schema-defined value called polls.
Now, when a user clicks a button, I display the polls that were created via a ng-view:
$scope.pollView= function(){
$scope.userVal2 = Auth.getCurrentUser();
$scope.userVal2 = $scope.userVal2.polls;
$scope.button = true;
};
In the html, I simply iterate over $scope.userVal2. My problem comes when I try to view a newly created poll. The poll does not initially show up, but if I refresh the page, then it shows up. Is there any reason for this? Does this have to do with the async calls?
Any help would be appreciated!
edit:
Controller:
'use strict';
angular.module('voteApp')
.controller('WallCtrl', function ($scope, $http, Auth) {
$scope.items = [];
$scope.title;
$scope.button = false; //set default to the new poll
$scope.polls = [];
$scope.items.push({id:1, upvotes:0, text:""});
$scope.items.push({id:2, upvotes:0, text:""});
$scope.addOptions = function(){
$scope.items.push({id:$scope.items.length +1, upvotes:0, text:""});
};
$scope.process = function(name, values){
$scope.polls.push({title:name, options:values});
$scope.title = ""; //reset the values for the next poll
$scope.items = [];
$scope.items.push({id:1, upvotes:0, text:""});
$scope.items.push({id:2, upvotes:0, text:""});
$scope.userVal = Auth.getCurrentUser();
$http.post('/api/users/update' + $scope.userVal._id, {polls: $scope.polls}).success(function(res){
//console.log("res: ", res);
});
};
$scope.newView= function(){
$scope.button = false;
};
$scope.pollView= function(){
$scope.userVal2 = Auth.getCurrentUser().polls
$scope.button = true;
};
$scope.delete = function(val){
$scope.polls = $scope.polls.filter(function(returnableObjects){
return returnableObjects.title !== val.title;
});
};
});
html:
<div ng-include="'components/navbar/navbar.html'"></div>
<header class="hero-unit" id="banner">
<div class="container">
<h1>Dashboard</h1>
<p class="lead">What would you like to do today?</p>
<button ng-click="newView()" type="button" class="btn btn-lg newpoll">New Poll</button>
<button ng-click="pollView()"type="button" class="btn btn-lg mypolls">My Polls</button>
</div>
</header>
<div ng-show= "!button">
<form name="form" ng-submit="process(title, items)">
<h2 class="col-md-12 text-center">New Poll</h1>
<h5 class="col-md-12 text-center">Name your poll.</h1>
<input name="pollname" ng-model="title"type="text" class="form-control input_width" placeholder="Poll Name" required>
<br>
<h5 class="col-md-12 text-center">Options</h1>
<div ng-repeat="item in items">
<p>
<input name = "{{item.id}}" ng-model="item.text" type="text" class="form-control input_width" placeholder="Option {{item.id}}" required>
</p>
</div>
<br>
<div class="text-center">
<button type="button"ng-click="addOptions()" class="btn options" formnovalidate>More Options</button>
</div>
<br>
<div class="text-center">
<button type="submit" class="btn button" validate>Submit</button>
</div>
</form>
</div>
<div ng-show="button" >
<br>
<div ng-repeat="poll in userVal2">
<div class="polldeco">
{{poll[0].title}}
<button class="btn buttondeco" ng-click="delete(poll)">Delete</button>
</div>
</div>
</div>

Some ideas:
$scope.userVal2 = Auth.getCurrentUser().polls is using the old version prior to the creation of a new poll? Maybe this could be changed to something like Auth.getCurrentUser().then(...). Either way, ensure that the call to getCurrentUser() is returning new data.
ng-view is cached. When a template is initially requested, it gets stored in the $templateCache. If this template is rendered on the backend for display in as a partial (eg: ng-view) and it is not static content, then you will have to invalidate the cache to update the view.
Consider having the backend return the new poll from $http.post('/api/users/update' ...) and adding it to the list used by ng-repeat. Something like:
$scope.process = function(name, values) {
$scope.polls.push({title:name, options:values});
...
$http.post('/api/users/update' + $scope.userVal._id, {polls: $scope.polls}).success(function(poll){
$scope.polls.push(poll);
});
};
...
<div ng-repeat="poll in polls">
<div class="polldeco">
{{poll[0].title}}
<button class="btn buttondeco" ng-click="delete(poll)">Delete</button>
</div>
</div>

Related

Getting a 405 (Method Not Allowed) in AngularJS

So, I am creating a web app, where one page I have a user list and on the second page, I have the users details page. On the second page, I have a confirm button where I want to remove that user when the "Confirm" button is clicked with a 200 Status code. However, I am getting a DELETE : 405 (Method Not Allowed). So, here is my code down below. Please tell me or help me fix this problem. Thank you in advance.
Here is my code.
<div ng-controller="MyCtrl">
<div ng-repeat="person in userInfo.lawyers | filter : {id: lawyerId}">
<a class="back" href="#/lawyer">Back</a>
<button type="button" class="edit" ng-show="inactive" ng-click="inactive = !inactive">
Edit
</button>
<button type="submit" class="submit" ng-show="!inactive" ng-click="inactive = !inactive">Save</button>
<button class="btn btn-primary" ng-click="doDelete(id)">Confirm</button>
<div class="people-view">
<h2 class="name">{{person.firstName}}</h2>
<h2 class="name">{{person.lastName}}</h2>
<span class="title">{{person.email}}</span>
<span class="date">{{person.website}} </span>
</div>
<div class="list-view">
<form>
<fieldset ng-disabled="inactive">
<legend>Basic Info</legend>
<b>First Name:</b>
<input type="text" ng-model="person.firstName">
<br>
<b>Last Name:</b>
<input type="text" ng-model="person.lastName">
<br>
<b>Email:</b>
<input type="email" ng-model="person.email">
</fieldset>
</form>
</div>
</div>
</div>
Services
app.factory('people', function ($http) {
var service = {};
service.getUserInfo = function () {
return $http.get('https://api-dev.mysite.io/admin/v1/unconfirmed_lawyers');
};
service.confirmUser = function (lawyerId) {
return $http.put('https://api-dev.mysite.io/admin/v1/lawyers/{lawyerId}/confirm');
};
return service;
});
LawyerController
app.controller('LawyerController', ['$scope', 'people', '$routeParams',
function ($scope, people, $routeParams) {
$scope.lawyerId = $routeParams.id;
people.getUserInfo().then(function (response) {
$scope.userInfo = response.data;
});
}]);
HomeController
var isConfirmed = false;
app.controller('HomeController', function($scope, people, $http) {
if (!isConfirmed) {
people.getUserInfo().then(function (response) {
$scope.userInfo = response.data;
}, function (error) {
console.log(error)
});
}
});
App.js
$scope.doDelete = function(lawyer) {
var index = $scope.userInfo.lawyers.indexOf(lawyer);
$scope.userInfo.lawyers.splice(index, 1);
location.href = '#/lawyer';
};
If you changed your HTML, so you passed the person instead.
<button class="btn btn-primary" ng-click="doDelete(person)">Confirm</button>
You can use this to find the index within the lawyers, then remove it.
$scope.doDelete = function(lawyer) {
var index = $scope.userInfo.lawyers.indexOf(lawyer);
$scope.userInfo.lawyers.splice(index, 1)
};
The issue is your are using $http.delete which performs an HTTP Delete request. This doesn't sound like something you intended.

Angular $apply does not update the view

I have a form, when I submit it, it pushes some object to my array. Beneath that form I have a table that shows all items in that array. I want my table to update automatically (without refreshing the page) when new item pushed.
Submit button:
<button type="submit" class="btn btn-default" ng-click="updateTable()">Pay</button>
In my controller:
$scope.updateTable = function() {
setTimeout(function () {
$scope.$apply();
$scope.$digest();
}, 0);
};
However, it does not work.
I tried different approaches like $watch service, but i`ve got the same result.
Table
<div class="row paytable">
<div class="col-xs-10 col-xs-offset-1">
{{payments.length}}
<table class="table table-hover ">
<tr>
<td>Id</td>
<td>Amount</td>
<td>Cause</td>
</tr>
<tr ng-repeat="item in payments">
<td>{{item.id}}</td>
<td>{{item.amount}}</td>
<td>{{item.cause}}</td>
</tr>
</table>
</div>
</div>
Controller
app.controller('mainController', [ 'user', '$rootScope', '$scope', 'payment', '$timeout', function(user, $rootScope, $scope, payment, $timeout) {
user.getUsers();
user.newUser();
$rootScope.currentUser = user.currentUser();
$scope.payments = payment.getPayments();
$scope.newPayment = payment.newPayment;
$scope.updateTable = function() {
setTimeout(function () {
console.log('apply ------------');
$scope.$apply();
$scope.$digest();
}, 0);
};
$scope.showPayMessage = function() {
console.log('im here');
$scope.showSM = true;
$timeout(function() {
$scope.showSM = false;
}, 2000);
};
}]);
payment - my service for array manipulation.
Form
<div class="newpay row" >
<div class=" col-xs-10 col-xs-offset-1">
<h1>Hello, {{currentUser.name}}</h1>
<h4 ng-show="showSM" class="bg-success">Payment confirmed</h4>
<form name="inputform" ng-submit="newPayment(amount, cause); showPayMessage();">
<div class="form-group">
<label for="exampleInputEmail1">Amount</label>
<input type="number" name="amount" ng-model="amount" class="form-control" id="exampleInputEmail1" placeholder="Amount" required>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Cause</label>
<input type="text" name="cause" ng-model="cause" class="form-control" id="exampleInputPassword1" placeholder="Cause" required>
</div>
<button type="submit" class="btn btn-default" ng-click="updateTable()">Pay</button>
</form>
</div>
</div>
payments: {{payments.length}}
<payments-table payments="payments"></payments-table>
To display that table I created directive.
$scope.$apply and $scope.$digest are better suited for working with 3rd party libraries or testing. In your case Angular is well aware to your changes. The thing is, your payments array, that resides in a service should be queried again after submitting a new item (unless you have a direct reference to the array, then no query should be made).
Like this:
View
<form name="inputform" ng-submit="onSubmit()">
Controller
$scope.onSubmit = function() {
newPayment($scope.newItemAmount, $scope.newItemCause); // Assuming they are properties in the controller
showPayMessage();
$scope.payments = payment.getPayments(); // getting the updated array
}

Angular - Form won't submit

I seem to be overlooking something simple here but it has me stumped.
Why does nothing happen when i hit the submit button?
<section ng-controller="SavingsController as savingsCTRL">
<form name="createSavingForm" class="form-horizontal" novalidate>
<fieldset>
<!-- Title Box Start-->
<div class="form-group new-deal-form" show-errors>
<label for="title">Title</label>
<input name="title" type="text" ng-model="savingsCTRL.title" id="title" class="form-control" placeholder="Title" required>
<div class="sub-label">Enter the Title of the Deal.</div>
<div ng-messages="savingForm.savingsCTRL.title.$error" role="alert">
<p class="help-block error-text" ng-message="required">Saving title is required.</p>
</div>
</div>
<!-- Title Box End-->
<!--Submit Button Start-->
<div class="form-group buttons-cancel-submit">
<button class="btn btn-default " ng-click="savingsCTRL.cancel()">Cancel</button>
<input type="submit" class="btn btn-success " ng-click="savingsCTRL.create(); submitForm(createSavingForm.$valid)" >
</div>
</fieldset>
</form>
</div>
</div>
</section>
for simplicity i took most of the forms out but what else is wrong?
Savings Controller Function
// Create new Saving
$scope.create = function () {
$scope.error = null;
alert("create");
// Create new Saving object
var saving = new Savings({
title: this.title,
details: this.details,
retailer: this.retailer,
price: this.price,
link: this.link,
image: $scope.user.imageURL,
urlimage: this.urlimage,
tags: this.tags
//startdate: this.startdate,
//enddate: this.enddate
});
// Redirect after save
saving.$save(function (response) {
$location.path('savings/' + response._id);
// Clear form fields
$scope.title = '';
$scope.details = '';
$scope.retailer = '';
$scope.price = '';
$scope.link = '';
$scope.image = '';
$scope.urlimage = '';
$scope.tags = '';
}, function (errorResponse) {
$scope.error = errorResponse.data.message;
});
};
Main issue is, you are mixing controller as syntax with $scope.
According to documentation, we should use this instead of $scope.
... binds methods and properties directly onto the controller using this: ng-controller = "SettingsController1 as settings"
Than, submitForm is not a predefined method, it should be defined in controller first
this.submitForm = function(isValid){
console.log('Submitting form: ' + isValid)
}
In addition to that, bind that to form with ng-submit= "savingsCTRL.submitForm(createSavingForm.$valid)"
See Plunker, with working code. (I took ng-click="savingsCTRL.create()", since we don't have all parts of your application)
Bind the form submit event to ng-submit.
Example: ng-submit="submitForm(createSavingForm.$valid)"

AngularJS multi-step form validation

I've followed this tutorial about AngularJS Multi-Step Form Using UI Router. The form works and I can save my data but now I'm having questions about how to validate each step in the form.
I have the following form with input fields:
Step 1
License Plate
Step 2
Name
Street
Zipcode
City
Email
Telephone
Step 3
Choose a date & time from a calendar
It looks somewhat like this:
I have a general base view like this:
<body ng-app="formApp">
<div id="top"></div>
<div class="container">
<!-- views will be injected here -->
<div ui-view></div>
</div>
</body>
In my app.js I have the following (not complete, left the non important things out):
// app.js
// create our angular app and inject ngAnimate and ui-router
// =============================================================================
angular.module('formApp', ['ngAnimate', 'ui.router', 'ui.calendar'])
// configuring our routes
// =============================================================================
.config(function($stateProvider, $urlRouterProvider, $interpolateProvider) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
$stateProvider
// route to show our basic form (/form)
.state('form', {
url: '/form',
templateUrl: 'views/form.html',
controller: 'formController'
})
// nested states
// each of these sections will have their own view
// url will be /form/interests
.state('form.license', {
url: '/license',
templateUrl: 'views/form-license.html'
})
// url will be nested (/form/profile)
.state('form.profile', {
url: '/profile',
templateUrl: 'views/form-profile.html'
})
// url will be /form/payment
.state('form.appointment', {
url: '/appointment',
templateUrl: 'views/form-appointment.html'
})
// url will be /form/success
.state('form.success', {
url: '/success',
templateUrl: 'views/form-success.html'
});
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form/license');
})
// our controller for the form
// =============================================================================
.controller('formController', function($scope, $http, $compile, $location, uiCalendarConfig) {
$scope.formData = {};
$scope.formData.profile = {};
$scope.next = function(step){
if(step == 1)
{
}
else if(step == 2)
{
}
};
// function to process the form
$scope.processForm = function(isValid) {
};
});
My general form.html:
<!-- form.html -->
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div id="form-container">
<form id="appointment-form" name="appointmentform" ng-submit="processForm(appointmentform.$valid)">
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view></div>
</form>
</div>
</div>
</div>
The first step in my form is in form-license.html:
<!-- form-license.html -->
<label>Nummerplaat ingeven</label>
<div class="form-group">
<div class="col-xs-8 col-xs-offset-2">
<input required type="text" class="form-control" name="license" ng-model="formData.license">
</div>
</div>
<div class="form-group row">
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1)" ui-sref="form.profile" class="btn btn-next btn-block">
Volgende
</a>
</div>
</div>
But now I'm wondering how I can validate this when they click on next button ... . It's not working with the normal required attribute.
Can somebody help me with this?
UPDATE:
Now I have in my first step the following:
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1, processForm)" ui-sref="form.profile" ng-disabled="!licenseValidated" class="btn btn-next btn-block">
Volgende
</a>
</div>
In my controller:
var validateLicense = function (newVal) {
var validated = false;
// Run your custom validation checks
if(newVal)
{
validated = true;
}
return validated;
};
$scope.$watch('formData.license', function (newVal) {
$scope.licenseValidated = validateLicense(newVal);
});
Ok, that works. But in my second step I have multiple fields like this:
<div class="profile">
<div class="form-group">
<label class="col-sm-3 control-label" for="name">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="name" ng-model="formData.profile.name">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="street">Street</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="street" ng-model="formData.profile.street">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="zipcode">Zipcode</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="zipcode" ng-model="formData.profile.zipcode">
</div>
</div>
<div class="form-group row">
<div class="col-xs-8 col-xs-offset-2">
<a ng-click="next(1)" ui-sref="form.license" class="btn btn-block btn-previous col-xs-3">
VORIGE
</a>
<a ng-click="next(2)" ui-sref="form.appointment" class="btn btn-block btn-next col-xs-3">
Volgende
</a>
</div>
</div>
</div>
Do I need to create for every one of them a $scope.watch? And do I need to add them to ng-disabled of my button?
You could simply disable the next button if any of the validation steps doesn't pass.
Something like:
// Inside your controller.
// Don't overload the scope.
// Only assign what will be needed through your HTML or other AngularJS Scopes
var validateLicense = function (newVal) {
// If you are only checking for content to be entered
return (newVal !== '' && newVal !== undefined);
};
var validateInfo = function (newVal) {
if (newVal.length > 0) {
// Check to make sure that all of them have content
for (var i = 0, l = newVal.length; i < l; i++) {
if (newVal[i] === undefined || newVal[i] === '') {
return false;
}
}
// We didn't find invalid data, let's move on
return true;
}
return false;
};
var validateDate = function (newVal) {
var validated = false;
// Run your custom validation checks
return validated;
}
// Initialize the disabled "Next" buttons
$scope.licenseValidated = false;
$scope.infoValidated = false;
// Watch a single item in a form, if filled in we will let them proceed
$scope.$watch('formData.license', function (newVal) {
$scope.licenseValidated = validateLicense(newVal);
});
// Watch a multiple items in a form, if ALL are filled in we will let them proceed
// Note that the order in this array is the order the newVal will be,
// So further validation for formData.number would be on newVal[1]
$scope.$watchGroup(['formData.name', 'formData.number', 'formData.address'], function (newVal) {
$scope.infoValidated = validateInfo(newVal);
});
form-license.html add the ng-disabled attribute on your next button:
<a ng-click="next(1, appointmentform)" ui-sref="form.profile" class="btn btn-next btn-block" ng-disabled="!licenseValidated">
Volgende
</a>
form-info.html repeat above steps
<a ng-click="next(1, appointmentform)" ui-sref="form.profile" class="btn btn-next btn-block" ng-disabled="!infoValidated">
Volgende
</a>
And so on...
See this Fiddle for Demo
You have a couple of options available to you depending on how you want to approach it.
To start, you should use ng-form for each of the 3 form steps. This will allow you to validate each individually without having to worry about the other sections.
So as an example your first form step might turn into:
<ng-form name="LicenseForm">
<label>Nummerplaat ingeven</label>
<div class="form-group">
<div class="col-xs-8 col-xs-offset-2">
<input required type="text" class="form-control" name="license" ng-model="formData.license">
</div>
</div>
<div class="form-group row">
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1, LicenseForm)" ui-sref="form.profile" class="btn btn-next btn-block">
Volgende
</a>
</div>
</div>
</ng-form>
This gives you access to the form validation properties for just this step. At this point you can update your controller to use .$invalid or .$valid on the form object that is now being passed in the next submit button, meaning you can now do something like:
$scope.next = function(step, form) {
if (form.$invalid) {
console.log('Form is invalid!');
return;
}
// move to next section
...
};

Can't access form inside AngularJS controller

Can't access form variable from my controller, when i try to access it by $scope.locationForm i've got 'undefined', but when i call console.log($scope) i can see in console there have loactionForm.
My HTML code
<div ng-controller="LocationsController as ctrl">
<form class="form-inline" name="locationForm">
<div class="form-group">
<!-- <div class="input-group"> -->
<label for="location-name">Название населенного пункта</label>
<input required
name="name"
ng-model="ctrl.location.name" type="text" class="form-control" id="location-name" placeholder="Название населенного пункта">
<label for="location-name">Район</label>
<select required
name="region_id"
ng-model="ctrl.location.region_id"
ng-options="region.id as region.name for region in ctrl.regions" class="form-control" placeholder="Название района"></select>
<input ng-click="ctrl.save()"
ng-disabled="locationForm.$invalid" type="submit" class="btn btn-default" value="Cохранить">
<a class="btn btn-default" ng-click="ctrl.reset()" ng-show="locationForm.$dirty">Сброс</a>
<!-- </div> -->
</div>
</form>
My Controller code:
function LocationsController($scope, Location, Region, $q) {
var lc = this,
l_index;
lc.form ={};
lc.regions = lc.locations = [];
lc.regions = Region.query();
lc.regions.$promise.then(function(data) {
lc.locations = Location.query();
});
lc.getRegion = function (id) {
return lc.regions.filter(function(obj) {
return obj.id == id;
})[0].name;
};
console.log($scope);
// console.log($scope.locationForm);
lc.reset = function () {
lc.location = new Location;
}
lc.reset();
};
The problem is when the LocationsController is initialized the form element is not yet compiled. So one possible hack is to use a timeout like
function LocationsController($scope, Location, Region, $q, $timeout) {
//then later
$timeout(function(){lc.reset();})
}

Categories