Angular.js Infinite Loop of calling functions - html

My problem is in the following template :
<div class="container">
<h1>Process Definitions</h1>
<table class="table table-stripped" >
<tr>
<td><strong>Name</strong></td>
<td><strong>Id</strong></td>
<td><strong>Version</strong></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr ng-repeat="def in defs | filter: global.search">
<td>{{def.name}}</td>
<td>{{def.id}}</td>
<td>{{def.version}}</td>
<td><button type="button" class="btn btn-warning" ng-click="redirectToInstance(def.id)">Instances</td>
<!--<td><h4><small>{{'Current :' + getCount(def.id) }}</small></h4></td>-->
<td><button type="button" class="btn btn-warning" ng-click="redirectToTasks(def.id)">Tasks</td>
</tr>
</table>
</div>
The problem is the function call inside the expression inside the comment-tag.
the function called is in this controller :
BPMNControllers.controller('ProcessDefinitionCtrl', ['$scope','$location', 'ProcessDefinitions','ProcessInstances',
function($scope,$location, ProcessDefinitions,ProcessInstances) {
$scope.defs = ProcessDefinitions.query();
$scope.redirectToInstance = function(id){$location.path('/process-definitions/' + id);};
$scope.redirectToTasks = function(id){$location.path('/tasks/def'+id)};
$scope.getCount = function (id){
var countObj = ProcessInstances.getCount({processDefinitionId : id});
return countObj.count;
};
console.log('ProcessDefinitionCtrl');
}]);
And the following service is important, cause it's querying a rest-api.
BPMNServices.factory('ProcessInstances', ['$resource','restUrl',
function($resource,restUrl){
var url = restUrl+'engine-rest/process-instance/:id'
return $resource(url,{id:'#id'},{
query : {method:'GET',isArray:true},
get : {method:'GET'},
getCount : {method:'GET',url:restUrl+'engine-rest/process-instance/count'}
},{})
}]);
This results in an infinite loop of ajax calls.I think i know that the problem is the asynchronous call, and since it's not directly bound the call is not fast enough and thus called and called again. How can i do this?
And the ProcessDefinitions as requested:
BPMNServices.factory('ProcessDefinitions', ['$resource','restUrl',
function($resource,restUrl){
var url = restUrl + 'engine-rest/process-definition/:id'
return $resource(url, {id : '#id'}, {
query: {method:'GET', isArray:true},
get : {method:'GET'},
start: {method:'POST',url:url+'/start'}
},{});
}]);
And the model for the global search filter :
<input type="text" class="form-control" placeholder="Search" ng-model="global.search">

The infinite loop is a result of your watched view expression calling a function which itself results in the triggering of a $digest cycle.
A good explanation, and another example, is provided in Infinite loop with Angular expression binding
As for your particular code, you could use a directive in place of the view expression:
HTML
<!-- results in infinite digest Count: {{getCount(def.id)}} -->
Count: <count></count>
JS
.directive('count', function(ProcessInstances){
return {
restrict: 'E',
replace: true,
link: function(scope) {
scope.countRes = ProcessInstances.getCount(scope.def.id);
},
template: '<span>{{countRes.count}}</span>'
}
});
Demo

Related

Laravel pass id to my sql on button click from view

I am trying to activate or deactivate the products for a form using $product->status
The active button shows if $product->status is 0 and
The deactive button shows if $product->status is 1
I want to toggle the value of $product->status in the mysql database every time I click on the button
<form action="{{route('invetory.create')}}" method="POST">
#csrf
<table class="table table-bordered" id="dynamicTable">
<tr>
<th>item</th>
<th>tax</th>
<th>Action</th>
</tr>
#forelse($products as $product)
<input type="text" name="item" value="{{$product->id}}
class="form-control" hidden />
<td>
<input type="text" name="item" value="{{$product->item}}
class="form-control" />
</td>
<td>
<input type="text" name="tax" value="{{$product->tax}}
class="form-control" />
</td>
#if($product->status =='0')
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">active</button>
</td>
#else
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-danger remove-tr deactive_btn">Deactive</button>
</td>
#endif
</table>
</form>
here i have given the route i have used
web.php
Route::post('/update', 'App\Http\Controllers\InventoryController#update')>name('invetory.update');
here i have added the controler i have used
InventoryController.php
public function update(Request $REQUEST){
dd($REQUEST->all());
Inventory::update( $REQUEST->invetory as $key => $value);
return redirect()->action([InventoryController::class, 'index']);
}
i am geting 500 error when i click button
You can achieve this using POST request which will refresh the page each time you toggle a product or you can use AJAX to do the change asynchronously. Using Javascript and AJAX would be the preferred way so you don't lose selected filters, pagination etc.
You don't need external packages to implement that, you can use JavaScript's fetch method. Also, instead of having 2 separate functions and routes, I would suggest having one route that would toggle the product's status, i.e. if it is active, make it inactive and vice versa. That method by definition should be a POST request, by I prefer doing GET requests for this in order to avoid CSRF protection and use middleware to protect the request.
Here is the complete code.
Register a web route that toggles the state inside web.php
Route::get('projects/toggle', [ProjectController::class, 'toggle'])->name('projects.toggle');
Implement the toggle method in ProjectController.php
public function toggle(Request $request) {
$project = Project::find($request->project_id);
$project->status = !$project->status;
$project->save();
return response()->json(['status' => (int) $project->status]);
}
Notice that I am returning a json response which includes the new project status. We will use this in the JavaScript code to dinamically update the column where the status is shown.
Finally, in the blade file, when iterating through the projects, the button click calls a function that will do the AJAX request. Notice that I am also adding an id attribute to the columns that contains the status so I can access it dinamically in order to update it.
#foreach($projects as $project)
<tr>
<td>{{$project->title}}</td>
<td id="project-status-{{$project->id}}">{{$project->status}}</td>
<td><button onClick="toggleStatus('{{$project->id}}')">Toggle</button></td>
</tr>
#endforeach
In this same file, we add the following JavaScript code. It accepts project_id as parameter which is passed from the button click, makes the ajax request to backend which updates the status and then updates the appropriate DOM element to show the new status.
function toggleStatus(project_id) {
fetch("{{ route('projects.toggle') }}?project_id=" + project_id)
.then(response => response.json())
.then(response => {
document.querySelector("#project-status-" + project_id).innerHTML = response.status;
})
}
As I mentioned, you can use multiple options in the JavaScript part. Instead of calling a function you can register an event listener to each button, but this approach with function call is a bit quicker. Also, I am passing the project_id as GET parameter, you can define the route to contain it as route parameter, but then you'll need to do some string replacements in order to do in dinamically in JavaScript. All in all, the proposed is a good solution that will serve your purpose.
p.s. For stuff like this, LiveWire is a perfect fit.
Using dd (e.g. in your controller) will throw a 500 error. It literally stands for "dump and die".
check you routes in form use{{route('invetory.create')}}
and in routes you given inventory.update
public function Stauts(Request $request)
{
$product= Product::findOrFail($request->id);
$product->active == 1 ? $product->active = 0 : $product->active = 1 ;
$product->update();
return response()->json(['status' => true,'msg' => 'Staut updated']);
}
in blade use ajax
<script>
$(document).on('click', '.status-product', function (e) {
e.preventDefault();
var product_id = $(this).data('id');
var url ="{{ route('product.status') }}";
$.ajax({
type: "post",
url: url,
data: {
'_token': "{{csrf_token()}}",
'id': product_id
},
success: function (data) {
if (data.status == true) {
$('#deactive_ajax').show();}
}
})
})
</script>
Route::post('product/stauts/', [productController::class,'Stauts'])->name('product.Stauts');
First of all
Using a form with tables is not ideal and some browsers already made changes to prevent that.
Secondly
The best way is as DCodeMania said, the ajax request is the best way to solve this, I'll just modify his answer a bit and use Patch instead of PUT, so it'll look like this:
$(document).on('click', '.active_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PATCH',
data: {
id: id,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
and you'll only be needing one button so no need to make the check for $product->status he added, just a single button for the toggle will make your code cleaner.
As for using PATCH instead of PUT, because you're only updating one single column and not the whole thing getting updated, and no need for the status parameter, you'll just reverse what's inside the database
$product = Product::find($request->id);
Product::where('id', $product->id)->update([
'status' => $product->status ? 0 : 1,
]);
You'll also need one button with different text based on status
like this
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">{{ $product->status == 1 ? 'deactivate' : 'activate' }}</button>
</td>
I did it this way and it works for me
First in view does this
<td>
#if ($producto->estado == '1')
<a href="{{ url('/status-update-producto', $producto->id) }}"
class="btn btn-success" id="btn_estado">Activo</a>
#else
<a href="{{ url('/status-update-producto', $producto->id) }}"
class="btn btn-danger" id='btn_estado'>Inactivo</a>
#endif
</td>
Controller
function updateStatusProducto($id)
{
//get producto status with the help of producto ID
$producto = DB::table('productos')
->select('estado')
->where('id', '=', $id,)
->first();
//Check producto status
if ($producto->estado == '1') {
$estado = '0';
} else {
$estado = '1';
}
//update producto status
$values = array('estado' => $estado);
DB::table('productos')->where('id', $id)->update($values);
return redirect()->route('productos.index');
}
Route
Route::get('/status-update-producto/{id}', [ProductoController::class, 'updateStatusProducto']);
You could add some data attributes to your buttons, and use them in js to send an ajax request
#if ($product->status =='0')
<td>
<button type="button" class="btn btn-success toggle-tr" data-product="{{ $product->id }}" data-status="{{ $product->status }}">active</button>
</td>
#else
<td>
<button type="button" class="btn btn-danger toggle-tr" data-product="{{ $product->id }}" data-status="{{ $product->status }}">Deactive</button>
</td>
#endif
document.querySelectorAll('.toggle-tr').forEach(el => el.addEventListener('click', e => {
const product = e.target.dataset.product;
const status = e.target.dataset.status == 0 ? 1 : 0;
// send ajax or fetch request passing in product_id. If we're going with a RESTful approach,
axiox.patch(`/products/${product}`, { status })
.then(res => { ... })
.catch(err => { ...});
}));
You can use jQuery ajax here:
Pass product id in data-id attribute
#if($product->status =='0')
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">active</button>
</td>
#else
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-danger remove-tr deactive_btn">Deactive</button>
</td>
#endif
then use ajax:
$(document).on('click', '.active_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PUT',
data: {
id: id,
status: 1,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
$(document).on('click', '.deactive_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PUT',
data: {
id: id,
status: 0,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
Now you can handle the request in the controller.

type error : Cannot read value 'name' of undefined

I'm working with Angularjs and firebase to create an app that is a task timer. I create the firebase object and all works fine except when I try to log task.newTask to the console. Here's a bit of my code (please note this is not all the code):
//HTML
<aside id="sidebar">
<section id="widget_1">
<h2 class="titles">Task History:</h2>
<input id="text-field" type="text" ng-model="taskText" />
<input id="task-button" type="button" value="Add Task" ng-click="addTask()" />
</section>
<section id="widget_2">
<h2 class="titles">Task List:</h2>
<table>
<tr ng-repeat="task in timer_tasks | orderBy: '-taskText'">
<td>{{ task.taskText }}</td>
</tr>
</table>
</section>
</aside>
//Controller:
(function() {
'use strict'
function HomeCtrl($scope, $firebaseArray, $interval) {
console.log('I see you home controller ... you are loaded sir!!!')
var timerRef = firebase.database().ref().child('timer_tasks');
$scope.timer_tasks = $firebaseArray(timerRef);
$scope.addTask = function () {
$scope.timer_tasks.$add({
taskText: $scope.taskText,
newTask: true
}).then(function (timerRef) {
$scope.taskText = '';
});
};
console.log(task.newTask);
On page load the error comes up at console.log(task.newTask);
Any help would be great. Thanks in advance!
If i'm understood correctly, you need to retrieve textbook value in controller which will build $scope.timer_tasks and later can be retrieved back to view in ng-repeat.
view
<aside id="sidebar">
<section id="widget_1">
<h4 class="titles">Task History:</h4>
<input id="text-field" type="text" ng-model="taskText" />
<input id="task-button"
type="button"
value="Add Task"
ng-click="addTask(taskText)"/>
</section>
</aside>
Script
myApp.controller('myCtrl', function($scope) {
$scope.addTask = function (value) {
console.log(" clicked : "+value);
};
});
All i did was bind the value upon addTask click from ng-model to ng-click and as you can see now you have that value in addTask function.

g:select using two combobox in grails

Hi I'm trying to list shops using two dropdown combobox. İf you're not select country or city list all shops. Other way list according to city country or both of them. By the way I didn't create controller I generate tehm using generate-all.
here is my view;
<g:form action="index" method="POST">
<div class="fieldcontain">
<g:select name="ddlCountry" noSelection="[null:message(code:'default.select.label',default:'Seçiniz...')]"
from="['UK', 'NL', 'DE']"
value="${params.ddlCountry}"/>
<g:select name="ddlCity"
from="['AMSTERDAM', 'Erfurt', 'Manchester','London']"
value="${params.ddlCity}"/>
<input class="btn btn-danger" type="submit" value="Listele" />
<g:each in="${shopList}" status="i" var="shopInstance">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td>
<g:link controller="shop" action="show" params="[id:shopInstance.id]">
${fieldValue(bean: shopInstance, field: "shopName")}
</g:link>
</td>
<td>${fieldValue(bean: shopInstance, field: "shopAdress1")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopPostcode")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopCity")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopCountry")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopDateEdited")}</td>
</tr>
</g:each>
</div>
</g:form>
and here is Shop controller index
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
if(params.ddlCountry || params.ddlCity) {
def shops = Shop.withCriteria {
if (params.ddlCountry) {
like('shopCountry', '%${params.ddlCountry}%')
}
if (params.ddlCity) {
like('shopCity', '%${params.ddlCity}%')
}
}
[shopList:shops]
}
else{
respond Shop.list(params), model:[shopCount: Shop.count()]
}
}
It's listed all shops every time. When I click button page is refreshing but nothing happening
Seems like there is a lot to learn:
Create a new template/file inside myController folder called _index.gsp
inside it put this bit
<g:each in="${shopList}" status="i" var="shopInstance">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td>
<g:link controller="shop" action="show" params="[id:shopInstance.id]">
${fieldValue(bean: shopInstance, field: "shopName")}
</g:link>
</td>
<td>${fieldValue(bean: shopInstance, field: "shopAdress1")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopPostcode")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopCity")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopCountry")}</td>
<td>${fieldValue(bean: shopInstance, field: "shopDateEdited")}</td>
</tr>
</g:each>
Change this to:
<!-- add onChange function to select you could look up item on change through jquery instead-->
<g:select name="ddlCity"
from="['AMSTERDAM', 'Erfurt', 'Manchester','London']"
value="${params.ddlCity}" onChange="verifyCity(this.value)"/>
<input class="btn btn-danger" type="submit" value="Listele" />
<!-- put a wrapper div ID around actual results -->
<div id="results">
<!-- make it render template now the controller action renders same content for this bit -->
<g:render template="/myController/index" />
</div>
<!-- END Wrapper -->
</div>
<script>
//Write some java script to handle the actions clicked
//VerifyCity will work on city click
//Hopefully this should be all it needs it gets a value builds a data array passes it to load results
function verifyCity(value) {
//
var data={ddlCity:value}
loadResults(data);
}
//Same as above for country
function verifyCountry(value) {
var data={ddlCountry:value}
loadResults(data);
}
//This runs a jquery post to the CONTROLLERNAME - define this and your action
//when it has a success it updates results DIV with the content
function loadResults(data) {
$.ajax({timeout:1000,cache:true,type: 'post',url: "${g.createLink(controller: 'CONTROLLERNAME', action: 'index')}",
data:data,
success: function(output) {
$('#results').html(output);
}
});
</script>
The segment that displayed results now in its own template, when it renders normally it calls in template. When ajax call is made it renders that specific template.
Now some changes to your controller
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
if(params.ddlCountry || params.ddlCity) {
def shops = Shop.withCriteria {
if (params.ddlCountry) {
like('shopCountry', '%${params.ddlCountry}%')
}
if (params.ddlCity) {
like('shopCity', '%${params.ddlCity}%')
}
}
//If request is coming in via ajax load in a template of the actual results so the bit that is within div id='results' should be actually a template.
if (request.xhr) {
render (template: 'myController/index', model:[shopList:shops])
return
}
//This will handle non ajax calls and will load in the index.gsp which includes the site mesh
[shopList:shops]
return
}
//no need for else just use return to stop a if statement in a controller
respond Shop.list(params), model:[shopCount: Shop.count()]
return
}
The controller will behave as it normally did with the exception of if if (request.xhr) which tells the controller if this is an ajax call render template _index.gsp rather than index.gsp
The difference between those two are that index.gsp has layout="main" this is the sitemesh that loads in the styles of the site. the template is plain and can be overloaded a segment of an existing normal page rendered.

Get the parent index of the array angularjs

I searching and I found out that this code is what I need right now. But there is something wrong about it. When I clicked the alertme button it's alert is -1 which is wrong. When I click 1 Brick med it's alert -1 it should be 0 because the no and name is in sectionA. When I want t click 3 Frank Joemar Timbang it should alert 1 because he is in sectionB? Aney help? suggestions? TIA
var stud = angular.module("stud", []);
stud.controller("StudentsController", function ($scope) {
'use strict';
$scope.alertMe = function (studentGroup) {
alert($scope.students.indexOf(studentGroup));
};
$scope.sectionA = [
{
no:1,
name:'Brick Med',
},
{
no:2,
name: 'Colin Christopher',
},
];
$scope.sectionB = [
{
no:3,
name: 'Frank Joemar Timbang',
},
{
no:4,
name: 'Curtis Zaymond',
}
];
$scope.students = [
$scope.sectionA,
$scope.sectionB
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html data-ng-app="stud">
<head lang="en">
<meta charset="utf-8">
<title>Students</title>
</head>
<body data-ng-controller="StudentsController" data-ng-init="init()">
<div id="container">
</div>
<div class="container-table">
<table border="1" width="100%">
<thead>
<tr>
<td>Students</td>
<td>Alert</td>
</tr>
</thead>
<tbody ng-repeat="studentGroup in students">
<tr ng-repeat="student in studentGroup">
<td>{{student.no}} {{student.name}}</td>
<td><button ng-click="alertMe($parent.$index)">Alert me!</button></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
Okay so your problem is that you are using indexOf() when you want to just be using studentGroup as the index.
$scope.alertMe = function (studentGroup) {
alert($scope.students[studentGroup]); // returns object object in your array
alert($scope.students[studentGroup][0]); /// returns sectionA object1
alert($scope.students[studentGroup][0].name); /// returns sectionA object1 name
};
And I fixed up your html so it is easier to read but your original stuff should still work.
<tbody ng-repeat="studentGroup in students" ng-init="studentsIndex = $index">
<tr ng-repeat="student in studentGroup">
<td>{{student.no}} {{student.name}}</td>
<td><button ng-click="alertMe(studentsIndex)">Alert me!</button></td>
</tr>
</tbody>
If this isn't what you want and you really want to alert the index, let me explain how indexOf works. That method will take a search parameter, and that is used to search an array for that element data. It is returning -1 right now because you give it the index and it searches for that index as the element data in index 0 and 1. Steps: Does whatever index = $scope.sectionA , Nope move on, Does whatever index = $scope.sectionB, nope move on. Done, did not find search parameter return -1.
The problem is with the indexOf. studentGroup is set correctly to 0 for the first 2 rows and 1 for the next 2. If you want the section array you should use
$scope.alertMe = function (studentGroup) {
alert($scope.students[studentGroup]);
};

Angular Js get Json not working

I'm trying to make a simple angular app which reads json data , but in response I'm only getting "," not data.Any pointers where I m going wrong ? I have attached index.html and abc.json file that i m keepingat server
PFB the code .
HTML:
<body ng-app="sampleApp" ng-controller="ctrl1">
Name:<input type="text" ng-model="name">
<ul>
<li ng-repeat="student in students | filter:name">{{student.name +","+student.empNo}</li>
</ul>
Name:<input type="text" ng-model="newname">
EmpNo:<input type="text" ng-model="newemp">
<input type="button" value="AddMe" ng-click="add()"/>
<div>
<div ng-view></div>
</div>
Javascript:
var x=angular.module('sampleApp',[])
.factory("simpleFactory",function($http){
var factory={};
factory.getStudents=function(){
return $http.get("abc.json");
}
return factory;
})
.controller("ctrl1",function($scope,simpleFactory){
$scope.students=simpleFactory.getStudents();
$scope.add=function(){
$scope.students.push(
{
name:$scope.newname,empNo:$scope.newemp
}
)
}
})
abc.json
[
{"name":"jack","empNo":"1"},
{"name":"Red","empNo":"2"},
{"name":"Jill","empNo":"3"}
]
getStudents is asynchronous. You should retrieve and assign students through the callback handler.
.factory("simpleFactory",function($http){
var factory={};
factory.getStudents = $http.get("abc.json");
return factory;
})
.controller("ctrl1",function($scope,simpleFactory){
simpleFactory.getStudents().then(function(result) {
$scope.students= result.data;
});
...
})
Nearly right. getStudents in fact returns you a 'Promise', not the data itself. You have to use that Promise to get your data:
simpleFactory.getStudents().then(function(data) {
$scope.students=data
// $scope.students=data.data // you may need this given your feedback
});