The page is supposed to display the contents of the database in a table, with a "New" button to add a new entry to the db. When the user clicks the "New" button, a form appears below. It is a ModelForm for the user to input a new entry to the Model, and then submit via a "Submit" button below. This was working as expected, until I added in an AngularJS controller to handle the button click of "New". The problem is my "Submit" button does not work. I want it to submit the data to the ModelForm, but it appears to do nothing.
Here is my template:
{% extends "base.html" %}
{% block content %}
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2>Ratings</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>Beer Name</th>
<th>Beer Rating</th>
<th>Notes</th>
<th>Brewer</th>
</tr>
</thead>
<tbody>
{% for rating in ratings %}
<tr>
<td>{{ rating.beer_name }}</td>
<td>{{ rating.score }}</td>
<td>{{ rating.notes }}</td>
<td>{{ rating.brewer }}</td>
<td>Edit</td>
<td><a class="btn btn-primary" href="{% url 'rating-delete' rating.id %}" value="{{ rating.id }}" name="delete" >Delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div ng-app="myApp" ng-controller="myCtrl">
<a ng-model="buttonClick" ng-click="is_clicked()" class="btn btn-primary">New</a>
<div ng-show="buttonClick" ng-cloak>
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2>Enter a new rating</h2>
<form role="form" method="post">
{% csrf_token %}
<p>Beer Name: <input type="text" ng-model="myForm.beer_name"></input></p>
<p>Score: <input type="number" step="0.1" ng-model="myForm.score"></input></p>
<p>Notes: <input type="text" ng-model="myForm.notes"></input></p>
<p>Brewer: <input type="text" ng-model="myForm.brewer"></input></p>
<p><button ng-click="submit_to_form(myForm)" type="submit" class="btn btn-primary">Submit</button></p>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http){
$scope.myForm = {beer_name:'beer_name', score:'score', notes:'notes', brewer:'brewer'};
$scope.buttonClick = false;
$scope.is_clicked = function() {
$scope.buttonClick=true;
console.log($scope.buttonClick)
}
$scope.submit_to_form = function(data) {
$http.post('rating-home', data);
}
})
</script>
{% endblock %}
And urls.py:
from django.conf.urls import url
from django.contrib import admin
from ratings.views import home, RatingCreate, delete, edit
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', RatingCreate.as_view(), name='rating-home'),
url(r'rating/add/$', RatingCreate.as_view(), name='rating-add'),
url(r'rating/delete/(?P<row_id>[0-9]+)/$', delete , name='rating-delete'),
url(r'rating/edit/(?P<row_id>[0-9]+)/$', edit , name='rating-edit'),
]
And views.py for the Submit button:
class RatingCreate(View):
""" Create a new Rating """
form_class = RatingForm
template_name = 'home.html'
def get(self, request):
form = self.form_class()
context = {'ratings': Rating.objects.all(), 'form': form}
#return render(request, 'home.html', context)
return render(request, self.template_name, context)
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
_ = form.save()
return redirect('rating-home')
return render(request, self.template_name, {'form: form'})
You should handle submitting the form with angular
<form ng-submit="myFunc(myData)">
<input type="text" ng-model="myData.name">
<input type="text" ng-model="myData.phone">
<button type="submit">Submit</button>
</form>
OR
<form >
<input type="text" ng-model="myData.name">
<input type="text" ng-model="myData.phone">
<button ng-click="myFunc(myData)">Submit</button>
</form>
and your controller:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope){
$scope.myData= {name:'' , phone:''};
$scope.myFunc = function(data) {
console.log(data);
// data(myData array) can be send with angular $http.post() method
// e.g. : $http.post('/myUrl/', data)
}
})
Update:
Here is a good tutorial for angular $http service!
Related
I am trying to write a controller to handle the button click of the "New" button. The goal is for the lower block of code (starting <div ng-show="buttonClick">) to be initially hidden. When "New" button is clicked I would like the lower block of code to appear.
The problem I'm having is that the lower block of code is visible when I load the page. It is never hidden.
{% extends "base.html" %}
{% block content %}
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2>Ratings</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>Beer Name</th>
<th>Beer Rating</th>
<th>Notes</th>
<th>Brewer</th>
</tr>
</thead>
<tbody>
{% for rating in ratings %}
<tr>
<td>{{ rating.beer_name }}</td>
<td>{{ rating.score }}</td>
<td>{{ rating.notes }}</td>
<td>{{ rating.brewer }}</td>
<td>Edit</td>
<td><a class="btn btn-primary" href="{% url 'rating-delete' rating.id %}" value="{{ rating.id }}" name="delete" >Delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div ng-app="myApp" ng-controller="myCtrl">
<a ng-model="buttonClick" ng-click="is_clicked()" class="btn btn-primary">New</a>
</div>
</div>
</div>
<div ng-show="buttonClick" >
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2>Enter a new rating</h2>
<form role="form" method="post">
{% csrf_token %}
<p>Beer Name: {{ form.beer_name }}</p>
<p>Score: {{ form.score }}</p>
<p>Notes: {{ form.notes }}</p>
<p>Brewer: {{ form.brewer }}</p>
<p><button type="submit" class="btn btn-primary">Submit</button></p>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope){
$scope.buttonClick = false;
$scope.is_clicked = function() {
$scope.buttonClick=true;
console.log($scope.buttonClick)
}
})
</script>
{% endblock %}
On this line
app.controller('myCtrl', function($scope)) {
Remove the last )
When your code will work, you will probably have a "blink" effect with your block. It will appear at the loading of the page, then it will disappear when Angular will start. If you have the "blink" effect, you will need to add ng-cloack on the block.
<div ng-show="buttonClick" ng-cloak>
I'd probably try it like this (untested):
<div ng-app="myApp" ng-controller="myCtrl">
<div><a ng-click"buttonClick = !buttonClick" class="btn btn-primary">New</a></div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope)) {
$scope.buttonClick = false;
}
</script>
I recently included JMS Serializer bundle to handle and convert arrays into JSON
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
public function searchcarAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
// $car = array();
//$cars = $em->getRepository('VehicleBundle:Car')->findAll();
// foreach ($cars as $cart) {
// $car[] = array('id'=>$cart->getId(),'name' => $cart->getName());
// }
$cars = array('name' => 'john','middlename'=> 'gon');
$serializer = $this->container->get('jms_serializer');
$jsonGroup= $serializer->serialize($cars,'json');
return $jsonGroup;
}
public function carAction()
{
return $this->render('VehicleBundle:Car:car_search.html.twig');
}
car.yml
search_car:
path: /car-search
defaults: { _controller: "VehicleBundle:Car:searchcar" }
car_action:
path: /search
defaults: { _controller: "VehicleBundle:Car:car" }
The JMS Serialiser is registeres in AppKernel.php
This will return 500 error
The controller must return a response ({"name":"john","middlename":"gon"} given).
if (null === $response) {
$msg .=' Did you forget to add a return statement somewhere in your controller?';
}
throw new \LogicException($msg);
}
}
return $this->filterResponse($response, $request, $type);
Whats wrong with my controller?
template
car_search.html.twig
{% extends '::base.html.twig' %}
{% block body %}
<div ng-app="myApp" ng-controller="customersCtrl">
Search your Groups Here: <input type="text" ng-model="searchMe.name"/>
<input type="checkbox" ng-model="exactMatch"/>Exact Match<br/>
<div class="[ form-group ]">
<input type="checkbox" ng-model="click" name="fancy-checkbox-default" id="fancy-checkbox-default" autocomplete="off" />
<div class="[ btn-group ]">
<label for="fancy-checkbox-default" class="[ btn btn-default ]">
<span class="[ glyphicon glyphicon-ok ]"></span>
<span> </span>
</label>
<label for="fancy-checkbox-default" class="[ btn btn-default active ]">
Hide
</label>
</div>
</div>
<table id="table" class="table" ng-hide="click">
<thead>
<tr>
<th>Name</th>
<th>Model</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in names | filter:searchMe:exactMatch | limitTo: 2">
<td>//x.name//</td>
<td>//x.model//</td>
</tr>
</tbody>
</table>
</div>
{% endblock %}
{% block javascripts %}
{#{ parent() }#}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="//code.angularjs.org/1.4.8/angular.js"></script>
<script>
var app = angular.module('myApp', []);
app.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('//');
$interpolateProvider.endSymbol('//');
});
app.controller('customersCtrl',['$scope','$http',function($scope, $http) {
//$http.get("http://localhost:8093/voters/voters_angular")
$http.get("{{ path('search_car') }}")
.success(function (response) {
$scope.names= JSON.parse(response);
});
}]);
//console.log(names);
</script>
{% endblock %}
Is JMS serializer not working here?
You are returning a string instead of Response object from your controller.
Instead of:
return $jsonGroup;
You should return response, like so for example:
return new \Symfony\Component\HttpFoundation\Response($jsonGroup, 200, ['Content-Type' => 'application/json']);
In my app i list users in a table via angular smart table. Now i would like to update a record once i modified it. I can't find any function with which i can change the user in my rowCollection so my list view reflects those changes directly.
This is my Service to receive my users from the API
services.factory('Users', function($resource) {
return $resource('/api/v1.0.0/users/:id', {id: '#id'}, {
update: {
method: 'PUT'
}
});
});
This is my List Controller to get the data from my factory. It also has the delete function. The delete function works fine - it just removes one index from the usersRowCollection. The API removes it from the DB.
app.controller('UsersCtrl', ['$scope', '$resource', 'Users',
function ($scope, $resource, Users) {
// Defines how many items per page
$scope.itemsByPage = 10;
// Create a reference object of usersRowCollection for smart table
$scope.usersRowCollection = Users.query();
$scope.usersDisplayedCollection = [].concat($scope.usersRowCollection);
/**
* Function to send a DELETE request to the API
* and to remove the row from the table
* #param user The row item
*/
$scope.deleteUser = function (user) {
// Send request to the API
user.$delete(function () {
// Remove this line from the table
var index = $scope.usersRowCollection.indexOf(user);
if (index !== -1) {
$scope.usersRowCollection.splice(index, 1);
}
})
}
}]);
This is my edit controller where i try to make the magic happen. the $scope.user.$update() function sends the changed data to the API - now i just need to get the changes reflected in my users list to which i redirect via $state.go('app.users.list') (ui-router)
app.controller('UsersEditCtrl', ['$scope', '$state', '$stateParams', 'Users',
function ($scope, $state, $stateParams, Users) {
$scope.updateUser = function () {
$scope.user.$update(function () {
$state.go('app.users.list');
//console.log($scope.user);
//console.log($scope.usersRowCollection);
//var index = $scope.usersRowCollection.indexOf($scope.user);
//console.log($scope.user.id);
// Remove this line from the table
var index = $scope.usersRowCollection.indexOf($scope.user);
if (index !== -1) {
$scope.usersRowCollection.splice(index, 1);
}
//$scope.user = Users.get({id: $scope.user.id});
//$scope.usersRowCollection.update();
// todo Update the changed entry in the table
});
};
// Loads the users json data from the REST Api
$scope.loadUser = function () {
//console.log('loadUser');
$scope.user = Users.get({id: $stateParams.id});
};
$scope.loadUser();
}]);
Thats my HTML - as you can see i use the st-safe-src attribute. If i understood correctly, it should recognice the changes and reflect them automatically.
<table st-table="usersDisplayedCollection" st-safe-src="usersRowCollection" class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>EMAIL</th>
<th>ACTIVE</th>
<th>CREATED_AT</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in usersDisplayedCollection">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.active }}</td>
<td>{{ user.created_at }}</td>
<td>
<button type="button" confirm-button="deleteUser(user);" confirm-message="Wirklich löschen?"
class="btn btn-sm btn-danger">
<i class="fa fa-times"></i> Delete
</button>
<a href="#" ui-sref="app.users.edit({id:user.id})" class="btn btn-sm btn-warning">
<i class="fa fa-pencil"></i> Edit
</a>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="7" class="text-center">
<div st-pagination="" st-items-by-page="itemsByPage" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
Edit form:
<form class="form-horizontal" role="form" ng-submit="updateUser()">
<div class="form-group">
<label>ID</label>
<input type="text" disabled="disabled" class="form-control" value="{{ user.id }}">
</div>
<div class="form-group">
<label for="name">NAME</label>
<input type="text" ng-model="user.name" class="form-control" id="name" placeholder="YOUR NAME">
</div>
<div class="form-group">
<label for="email">E-MAIL</label>
<input type="email" ng-model="user.email" class="form-control" id="email" placeholder="YOUR EMAIL">
</div>
<div class="radio">
<label>
<input type="radio" ng-model="user.active" name="active" id="active" value="1">
ACTIVE
</label>
<label>
<input type="radio" ng-model="user.active" name="active" id="inactive" value="0">
INACTIVE
</label>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
ABBRECHEN
<button type="submit" class="btn btn-default">SPEICHERN</button>
</div>
</div>
</form>
So, how can i reflect the changes i do in my form view directly after saving in my list view?
my ng-repeat is not updating after attempting to add an item to scope.
Here is my html:
<div class="col-lg-12">
<div class="panel-group" id="accordion">
<div class="panel panel-collapse panel-default" id="topPanel">
<div class="panel-heading" data-toggle="collapse" data-target="#top-action-panel-body">
<h4 class="panel-title">
Collapsible Group Item #1
</h4>
</div>
<div id="top-action-panel-body" class="panel-collapse collapse">
<div class="panel-body">
<form class="ih_enterprise_api_stock_item_new form-horizontal form-stock-item-add" ng-submit="test()" ng-controller="InventoryAddCtrl" id="ihenterprise_logisticsbundle_stockItem">
<div class="form-group">
<label class="col-sm-4 control-label control-label required" for="ihenterprise_logisticsbundle_stockItem_name">Name</label>
<div class="col-sm-8">
<input type="text" id="ihenterprise_logisticsbundle_stockItem_name" name="ihenterprise_logisticsbundle_stockItem[name]" required="required" maxlength="255" ng-model="formData.name" class="form-control form-control">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label control-label required" for="ihenterprise_logisticsbundle_stockItem_itemNo">Item no</label>
<div class="col-sm-8">
<input type="text" id="ihenterprise_logisticsbundle_stockItem_itemNo" name="ihenterprise_logisticsbundle_stockItem[itemNo]" required="required" maxlength="255" ng-model="formData.itemNo" class="form-control form-control">
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<input type="submit" class="btn btn-success" value="Tilføj">
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-12" ng-controller="InventoryListCtrl">
<div class="panel panel-default" style="color: black; text-align: left">
<div class="panel-heading">
<h3>Lager liste</h3>
</div>
<div class="panel-body table-responsive">
<table class="table table-condensed table-expanding">
</table><table class="table table-condensed table-expanding">
<thead>
<tr>
<th> </th>
<th>Id</th>
<th>Created At</th>
<th>Navn</th>
</tr>
</thead>
<tbody>
<tr ng-repeat-start="stockItem in stockItems" data-toggle="collapse" data-target="#stockItem_{{stockItem.id}} " class="accordion-toggle">
<td>
<button class="btn btn-default btn-xs"><span class="glyphicon glyphicon-eye-open"></span></button>
</td>
<td>{{stockItem.id}} </td>
<td>{{stockItem.created_at}} </td>
<td>{{stockItem.name}} </td>
</tr>
<tr ng-repeat-end="">
<td colspan="6" class="hiddenRow">
<div class="accordian-body collapse" id="package_{{stockItem.id}} ">
test
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
Here is my code:
App.js
'use strict';
var app = angular.module('cmfApp', [
'ngRoute',
]);
angular.module('cmfApp.controllers', []);
InventoryRouting.js
angular.module('cmfApp').config(function($routeProvider){
$routeProvider.
when('/inventory', {
templateUrl: Routing.generate('ih_enterprise_user_dashboard_inventory'),
controller: 'InventoryListCtrl'
})
});
InventoryController.js
angular.module('cmfApp').controller('InventoryAddCtrl', ['$scope', '$http', '$timeout', function($scope, $http, $timeout){
$scope.submit = function() {
var postData = {
ihenterprise_logisticsbundle_stockItem: {
name: $scope.formData.name,
itemNo: $scope.formData.itemNo
}
}
$http({
method : 'POST',
url : Routing.generate('ih_enterprise_api_stock_item_new'),
data : $.param(postData), // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
.success(function(data) {
// the code you want to run in the next digest
$scope.$apply(function(data){
$scope.stockItems = $scope.stockItems.concat(data);
});
//console.log($scope.stockItems);
}).error(function(error) {
console.log(error);
});
};
$scope.test = function() {
console.log("here");
$scope.stockItems.push({
id: 1000,
name: 'potato',
created_at: '1111'
});
console.log($scope.stockItems);
}
}]);
Ignore the HTTP request, i was thinking it was a HTTP related issue, but it seems much more fundamental, as i attempted to just insert a plain object on submit.
You seem to be instantiating the InventoryListCtrl twice: Once in the route definition, and again in the HTML template. As a result, when you update the stockItems array, it's not updating the same array used in the view.
Try removing the ng-controller="InventoryListCtrl" from the template.
This will make InventoryListCtrl be the controller for the entire HTML template (b/c of the route definition). InventoryAddCtrl is used inside the template and it will inherit the scope of InventoryListCtrl. So when you update $scope.stockItems from either controller, you'll now be updating the same object.
I'm trying to implement a simple crud using Google App Engine (Cloud endpoints) and AngularJs.
I developed my backend and everything working well. My problem is Angular.
After I call my service to Save or Delete, my list doesn't refresh.
My code Controller:
app.controller('admEstadoController',['$scope','admEstadoService', function ($scope, admEstadoService) {
$scope.saveEstado = function() {
admEstadoService.saveEstado($scope);
}
$scope.deleteEstado = function(id) {
admEstadoService.deleteEstado(id,$scope);
}
$scope.listEstado = function() {
admEstadoService.listEstado($scope);
}
}]);
and this is my Service:
app.factory('admEstadoService',[function() {
var admEstadoService = {};
admEstadoService.listEstado = function($scope) {
gapi.client.enadepopapi.listEstado().execute(function(resp) {
$scope.estados = resp.items;
$scope.$apply();
});
}
admEstadoService.saveEstado = function($scope) {
estado = {
"estado" : $scope.estado,
"nome" : $scope.nome
};
gapi.client.enadepopapi.saveEstado(estado).execute();
}
admEstadoService.deleteEstado = function(id,$scope) {
estado = {
"estado" : id
};
gapi.client.enadepopapi.deleteEstado(estado).execute();
}
return admEstadoService;
}]);
And my view is:
<div class="panel panel-default" ng-show="is_backend_ready">
<div class="panel-heading">Manutenção de Estados</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form role="form">
<div class="form-group">
<label for="txtEstado">Estado</label>
<input type="text" class="form-control" id="txtEstado" placeholder="" ng-model="estado">
</div>
<div class="form-group">
<label for="txtNome">Nome</label>
<input type="text" class="form-control" id="txtNome" placeholder="" ng-model="nome">
</div>
<button type="submit" ng-click="saveEstado()" class="btn btn-default">Gravar</button>
</form>
<div class="bs-example-bg-classes" style="margin-top:10px;">
<p class="bg-success"> Registro Gravado com sucesso!</p>
</div>
<div class="clearfix"></div>
<div ng-init="listEstado()">
<table class="table table-hover">
<tbody class="table-hover">
<tr>
<th>Estado</th>
<th>Nome</th>
<th>Operação</th>
</tr>
<tr ng-repeat="estado in estados">
<td>{{estado.estado}}</td>
<td>{{estado.nome}}</td>
<td><button type="button" ng-click="deleteEstado(estado.estado)" class="btn btn-default">Excluir</button>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
Even though you update the data in the backend, AngularJS is not aware of this. You need to call listEstado() after save or delete.
However, if you're using ndb as your datastore, be aware that your updated data may not be available instantly because of ndb's eventual consistency behavior.