I have a search controller that will look up values and render specific views according to the type of report that should be displayed. There is a weird thing happening. When I issue the $this->render the report view is not rendered. The "catch all" redirect line always is rendered... Code as follows:
public function admin_printReport() {
if (isset($this->request->data['Reports'])) {
$nons = $this->request->data['Reports'];
$res = array();
// lets lookup the noncons.....
foreach ($nons as $dat=>$vdat) {
// skip the ones that are not checked
if ($vdat == 0) {
continue;
}
// this is the temporary array that holds all of the selected report numbers > $res[] = $dat;
}
$this->loadModel('Noncon');
$this->Noncon->recursion = 0;
$results = $this->Noncon->find('all', array('conditions'=>array('Noncon.id'=>$res)));
$this->set('results', $results);
// lets do the selection now...
if (isset($this->request->data['PS'])) {
// Print summary
$this->render('summary', 'print');
} elseif (isset($this->request->data['PD'])) {
// Print detail
$this->render('detail', 'print');
} elseif (isset($this->request->data['PDH'])) {
// Print detail with history
$this->render('detailhistory', 'print');
}
}
// catch all if the render does not work....
$this->redirect(array('controller'=>'noncons', 'action'=>'search','admin'=>true));
}
Any Ideas?
I just figured it out....
for each $this->render, add return. For example:
return $this->render('summary', 'print');
I've just add a similar problem.
In a controller, that has an ajax submit, it was not submitting the render.
$this->render($viewName, 'ajax');
Using return didn't helped.
The problem was that I added
$this->render('add');
at the end of the controller, although it was not necessary since the controller name is add and the autoRender is default (true to automatically render the view with the same name of the controller).
Hope this helps someone else.
Related
I have two components in my page. Component A called Dashboard and contains a list of elements via ng-repeat, let's call it systems. Each system will be displayed via a component B, a component especially for this system element it's called SystemView. Each system view has a list of log entries and what i now want, is to display a badge with the amount of unread log entries.
If i open a system entry i'm able to set a log entry to read, wo the number of unread log entries should change.
For your understanding: system.logEntries is a list of log entries.
HTML
<some-accordion-control>
<div ng-repeat="system in systems">
<h3>{{system.name}} <span class="badge">{{system.unreadLogEntries}} of {{system.logEntries.length}}</span>
<system-view system="system"></system-view>
</div>
</some-accordion-control>
JS SystemView Controller
module.exports = {
template: 'some.html',
controller: SystemView,
bindings: {
system: '<'
}
};
function SystemView() {
// ... some code
}
SystemView.prototype = {
setRead: function () {
// this is an example that i change the value of unreadLogEntries
// this did not work in the dashboard component
system.unreadLogEntries--;
// i tried this and it worked in the dashboard component
system.logEntries.push({
message: "Hello World!"
});
}
};
My problem here is, that the list of log entries could change and gets the new information about it's values. But the simple integer field unreadLogEntries not.
Can someone help me here?
Ok, i did the following to hit this:
I removed the unreadLogEntries field and added a controller function to count the unread log entries in the attached list of log entries.
This in the HTML:
<span class="badge">{{$ctrl.countUnread(system.logEntries)}} of {{system.logEntries.length}}</span>
And as part of my controller:
SystemView.prototype = {
setRead: function () {
// ...
},
countUnread: function (logEntries) {
var unread = 0;
for(var i = 0; i < logEntries.length; i++) {
if(logEntries[i].isUnread) { unread++; }
}
return unread;
}
};
This worked for me. Maybe there is a better solution for that, but this solved my problem.
I need to enable/disable all validation rules in Angular form or subform under ng-form="myForm" based on a scope variable $scope.isValidationRequired. So, if isValidationRequired is false, none of the validations set for the designated group of fields will run, and the result will always be myForm.$valid==true, otherwise, the validation rules will run as usual.
I did a lot of research, and realized that this feature is not available out of the box with Angular. However, I found some add-ons or with some customization, it is possible.
For example, I can use the add-on angular-conditional-validation (github and demo) with custom directive enable-validation="isValidationRequired". This will be perfect, except that I cannot apply this feature for a group of fields under ng-form. I have to add this directive for each and every field where applicable.
The other solution is to use custom validation using Angular $validators pipeline. This requires some extra effort and I don't have time since the sprint is almost over and I have to give some results in a few days.
If you have any other suggestions please post an answer.
Use Case:
To clarify the need for this, I will mention the use-case. The end user can fill the form with invalid data and he can click Save button and in this case, the validation rules shouldn't be triggered. Only when the user clicks Validate and Save then the validation rules should be fired.
Solution:
See the final plunker code here.
UPDATE: as per comments below, the solution will cause the browser to hang if inner subforms are used under ng-form. More effort is needed to debug and resolver this issuer. If only one level is used, then it works fine.
UPDATE: The plunker here was updated with a more general solution. Now the code will work with a form that has sub-forms under ng-form. The function setAllInputsDirty() checks if the object is a $$parentForm to stop recursion. Also, the changeValidity() will check if the object is a form using $addControl then it will call itself to validate its child objects. So far, this function works fine, but it needs a bit of additional optimization.
One idea is to reset the errors in the digest loop if the validation flag is disabled. You can iterate through the form errors on change and set them to valid, one by one.
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function() {
if ($scope.isValidationRequired === "false") {
for (var error in $scope.form.$error) {
while ($scope.form.$error[error]) {
$scope.form.$error[error][0].$setValidity(error, true);
}
}
}
}
Here is a plunkr: https://plnkr.co/edit/fH4vGVPa1MwljPFknYHZ
This is the updated answer that will prevent infinite loop and infinite recursion. Also, the code depends on a known root form which can be tweaked a bit to make it more general.
References: Pixelastic blog and Larry's answer
Plunker: https://plnkr.co/edit/ycPmYDSg6da10KdoNCiM?p=preview
UPDATE: code improvements to make it work for multiple errors for each field in each subform, and loop to ensure the errors are cleared on the subform level
var app = angular.module('plunker', []);
app.controller('MainCtrl', ["$scope", function($scope) {
$scope.isValidationRequired = true;
var rootForm = "form";
function setAllInputsDirty(scope) {
angular.forEach(scope, function(value, key) {
// We skip non-form and non-inputs
if (!value || value.$dirty === undefined) {
return;
}
// Recursively applying same method on all forms included in the form except the parent form
if (value.$addControl && key !== "$$parentForm") {
return setAllInputsDirty(value);
}
if (value.$validate){
value.$validate();
}
// Setting inputs to $dirty, but re-applying its content in itself
if (value.$setViewValue) {
//debugger;
return value.$setViewValue(value.$viewValue);
}
});
}
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function(theForm) {
debugger;
//This will check if validation is truned off, it will
// clear all validation errors
if (!theForm) {
theForm = $scope[rootForm];
}
if ($scope.isValidationRequired === "false") {
for (var error in theForm.$error) {
errTypeArr = theForm.$error[error];
angular.forEach (errTypeArr, function(value, idx) {
var theObjName = value.$name;
var theObj = value;
if (theObj.$addControl) {
//This is a subform, so call the function recursively for each of the children
var isValid=false;
while (!isValid) {
$scope.changeValidity(theObj);
isValid = theObj.$valid;
}
} else {
while (theObj.$error[error]) {
theObj.$setValidity(error, true);
}
}
})
}
} else {
setAllInputsDirty($scope);
}
}
}]);
I have a small app that users can use to search for a movie, and then add it to their watchlist. Currently it is possible to add 1 movie multple times for the same user. Which ofcourse isn't expected behaviour.
My solution would be to find the unique id of the movie that's being added and crosscheck that with my movies_users data. If the movie_id value exists, do this, else do this.
At the moment I do have the unique movie id of the movie that's being added,
$scope.movieListID = response;
console.log ($scope.movieListID.id)
Which gets ouputted like a string, like so,
314365
And I got the movie records from the current user,
$scope.moviesCheck = response.data;
console.log ($scope.moviesCheck)
Which looks like this,
[{"id":2,"title":"Black Panther", "movie_id":"284054"}]
So what would be a good way to check if the result from $scope.movieListID.id already exists in the $scope.moviesCheck data?
* update *
Trying a suggestion below does not give the expected result.
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
console.log (movie.movie_id)
console.log (id)
if (movie.movie_id === id)
console.log ('duplicate')
else
console.log ('not duplicate')
});
}
exists($scope.movieListID.id);
The console.log output from this is,
312221
312221
not duplicate
Which clearly are duplicate results.
You can add a function in your controller to check if the movie exists in the list
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
if (movie.id === id)
return true;
});
return false;
}
// and call it
exists($scope.movieListID.id); // true or false
I'm not 100% if this is a good way to do this, but for me it works and I think it's pretty low on performance,
movieService.loadMovies().then(function(response) {
$scope.moviesCheck = response.data;
var arr = $scope.moviesCheck
function myIndexOf(o) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].movie_id == o.exisitingMovie_id) {
return i;
}
}
return -1;
}
var checkDuplicate = (myIndexOf({exisitingMovie_id:movie.id}));
if (checkDuplicate == -1) {
From your question I've understood that, based on the object exists using id in the array of object, you have to do different action.
You can use $filter for this. Inject the filter for your controller and assign it to the scope. So this will be available whenever you want in this controller.
$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
$sope.movies - is the list of movies passed to the filter. You can
send any list based on your need.
{movie_id:$scope.existingMovie.movie_id} - This one is the object
which one we need to find. This can be based on your need. Since we
are searching movie_id, we need to send the object with property
and value. {movie_id:$scope.existingMovie.movie_id}, Here movie_id is
the property and followed by the value with the colon.
true: This indicates that, to search exact matched values. By default
this is false. If this is set to false, then if we want to search 54
in the movie id, this will returns the objects whichever contains 54
as part of the value.
app.controller('controller', ['$filter',function($filter){
$scope.cFilter= $filter;
$scope.Exists=function(){
$scope.movies=[{"id":2,"title":"Black Panther", "movie_id":"284054"},{"id":3,"title":"Black Panther", "movie_id":"32343"},{"id":4,"title":"Black Panther", "movie_id":"98863"}]
$scope.existingMovie={"id":3,"title":"Black Panther", "movie_id":"32343"};
var _obj=$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
if(_obj && _obj[0])
{
Console.log('object exists')
}
else
{
Console.log('Object is not found')
}
}
}])
Many Thanks Jeeva Jsb. This got me on the right track, however I thought I would clarify with a practical example that seems to work as expected.
So I have a function called getData which get the AJAX array but we need to check if the record exist before added to scope (else we get duplicates)
if (d.data.length) {
for (var i = 0; i < d.data.length; i++) {
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
if (doesExist.length == 0){
$scope.notifications.push(d.data[i]);
}
}
}
This should look familier...
when we are iterating through the returned AJAX object we need to check the ID of the (in my case notificiation)
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
This line creates a new array by filtering the existing array in scope ($scope.notifications) and passing in the same value from you interation.
If the value exists the object will be copied to the new array called doesExist.
A simple check of length will determine if the record needs to be written.
I hope this helps someone.
I am trying to retrieve data from another controller to display it in the PagesController's view using an element. I have a table
service_categories(id, service_category);
my ServiceCategoriesController looks like this
public function category() {
$serviceCategories = $this->paginate();
if ($this->request->is('requested')) {
return $servicesCategories;
} else {
$this->set('serviceCategories', $servicesCategories);
}
}
my category.ctp element looks like this
<?php
$serviceCategories = $this->set('serviceCategories/category');
foreach ($serviceCategories as $serviceCategory):
echo $serviceCategory['ServiceCategory']['service_category'];
endforeach;
But I seem to get an undefined value of "$serviceCategories" when I create an alert before the foreach loop. Please assist! What am I missing?
I rewrite the answer after turning on brain:
In your element replace:
$serviceCategories = $this->set('serviceCategories/category');
with
$serviceCategories = $this->requestAction('service_categories/category');
I know that's kinda simple and lame question, but still.
I have a Form which should not show all Model fields, but only some of them. That's why I can't use Form->setModel($m), because that'll automatically add all fields into Form.
So I add Model into page, then add form into page and then use form->importFields like this:
$m = $p->add('Model_Example');
$f = $p->add('Form');
//$f->setModel($m); // can't use this because that'll import all model fields
$f->importFields($m,array('id','description'));
$f->addSubmit('Save');
What I don't understand in this situation is - how to save this data in database, because $f->update() in onSubmit event will not work. Basically nothing I tried will work because Form have no associated Model (with setModel).
How about this way?
$your_form->setModel($model,array('name','email','age'));
I have solution for mixed form. Add custom form fields in form init and manipulate with them by hooks ('afterLoad','beforeSave')
In this case you can use setModel() method
$form->setModel('Some_Model',array('title','description'));
class Form_AddTask extends Form {
function init(){
parent::init();
$this->r=$this->addField('autocomplete/basic','contact');
$this->r->setModel('ContactEntity_My');
}
function setModel($model,$actual_fields=undefined){
parent::setModel($model,$actual_fields);
$this->model->addHook('afterLoad',array($this,'setContactId'));
$this->model->addHook('beforeSave',array($this,'setContactEntityId'));
return $this->model;
}
// set saved value for editing
function setContactId() {
$this->r->set($this->model->get('contact_entity_id'));
}
function setContactEntityId() {
$this->model->set('contact_entity_id',$this->get('contact'));
}
}
There is a hook 'validate' as well in Form_Basic::submitted(), so you can add
$this->addHook('validate',array($this,'validateCustomData'));
and validate your data in Form::validateCustomData()
Why not set the fields to hidden in the model?
I.e.:
class Model_Example extends Model_Table {
public $table='assessment';
function init() {
parent::init();
$grant->addField('hidden_field')->hidden(true);
}
}
And then:
$m = $p->add('Model_Example');
$f = $p->add('Form');
$f->setModel($m);