Phalcon and Angular JS routing - use Angular templates and Phalcon controllers - javascript

I am using Phalcon with Angular JS and have the problem, that Phalcon is catching all my Angular routes. The target is that the route /dashboard is called first which loads the Angular, jQuery, etc. If the user clicks on the link /people only the ng-view should change and the Angular template should be loaded.
Volt template:
<div ng-app="sampleApp">
// Loads menu with two links to /dashboard and /people
<?php $this->partial("partials/navigation") ?>
<?php echo $this->flashSession->output(); ?>
<div ng-view ng-controller="DashboardController">
{{ content() }}
</div>
</div>
My target is that when the user clicks a link only the ng-view is changed with the routes defined in Angular:
$routeProvider
.when('/dashboard', {
templateUrl: '/partials/dashboard.html',
controller: 'DashboardController'
}).when('/people', {
templateUrl: '/partials/people.html',
controller: 'PeopleController'
});
Instead of that the Phalcon routing tries to load the "PeopleController" with the method "indexAction".
I have tried to disable the Phalcon routing and only load the dashboard route (where the Angular library is loaded):
$di->set('router', function () {
$router = new \Phalcon\Mvc\Router(false);
$router->add("/dashboard", array(
'controller' => 'dashboard',
'action' => 'index'
));
return $router;
});
How can I load some pages/routes with Phalcon like the /dashboard and some pages/routes to change the ng-view in Angular?
Best regards,
Patrick
Edit: It works fine with the normal Angular, but with activated HTML mode it makes problems

Constructing your router passing false as parameter would be enough to not add the default routes. Only the ones you explicitly add will be matched.
However your problems are more likely coming from your client-side router. Please read carefully about the $location service to understand how make angular be able to tell if the current route should be retrieved from your server or not.
I would like to improve this answer if you provide more details about your angular router setup.

Related

AngularJS: How to Update ng-view using $route.reload()

I have searched through a lot of questions regarding $route.reload() and I think I still have a fundamental misunderstanding of how it and Angular are actually working. I really want to understand what is actually going on (not just a quick fix) so I apologize if this question gets rather verbose.
I am trying to create an app that will allow the user to input information into a form and then use ng-view and a controller LivePreviewController to display that form data in a "live preview" of the form which has some unique styling and other information associated with the form that the user does not input themselves.
This is very simple to do if the "live preview" is occurring on the same page as the user is inputting information using something like this
<input type="text" ng-model="form_name" />
<div ng-bind="form_name"></div>
What I would like to do is something like this.
<!--Location is: localhost:8888/form_page.html -->
<input type="text" ng-model="form_name" />
<!-- ng view is displaying the partial view: styled_form.html -->
<div ng-view></div>
Then I have another page styled_form.html which is the partial view that gets displayed by the ng-view div.
<!-- partial view page is styled_form.html -->
<div ng-bind="form_name"></div>
Both of these pages are controlled by the same controller LivePreviewController.
There are a lot of things I imagine that I am probably doing incorrectly. First thing could be the routing. I had some issues with the routing as app is only used on one page of a multi-page website my routes are setup as follows.
var previewApp = angular.module('previewApp', ['ngRoute']);
previewApp.config(['$routeProvider', function($routeProvider){
$routeProvider
.otherwise({
redirectTo:'/styled_view.html',
controller: 'LivePreviewController'
});
}]);
//Controller Code
previewApp.controller('LivePreviewController', [$scope, $route, function($scope, $route){
$scope.form_name = 'temporary name';
}]);
All of this works as expected when the form_page.html is originally loaded, but when the user changes the name in the text input the ng-view displaying my styled_form.html page is not changed. This also makes sense because the styled_form.html needs to be reloaded with the new information.
Here is where the noob starts. My first inclination was to create a refresh button and add a function in my LivePreviewController to refresh the page using $route.reload()
previewApp.controller('LivePreviewController', [$scope, $route, function($scope, $route){
$scope.form_name = 'temporary name';
$scope.refreshPage = function() {
$route.reload();
console.log($scope.form_name) //to ensure scope is updating
};
}]);
And then updated my html to include the button as follows
<!--Location is: localhost:8888/form_page.html -->
<input type="text" ng-model="form_name" />
<button ng-click="refreshPage()"> Update the Page </button>
<!-- ng view is displaying the partial view: styled_form.html -->
<div ng-view></div>
The function is working just fine when the button is clicked and the updated scope variable is displaying in the console, but my live preview is not updating. After looking through the Angular documentation I have a hunch as to what is happening. It states...
Causes $route service to reload the current route even if $location hasn't changed.
As a result of that, ngView creates new scope and re-instantiates the controller.
So if I understand the docs correctly, which I obviously don't, what is actually happening on $route.reload() is that when styled_form.html is getting reloaded the updated $scope.form_name is getting wiped out and the original $scope value is getting shown on the page. So...
Question 1: am I understanding this correctly i.e. is $route.reload() wiping out my $scope and reloading the old $scope variable, or is my $route.reload() just not reloading the page at all?
Now if I am interpreting my issue correctly my guess (seen suggested on here in other topics) is that the solution would be to create some type of service to store my data and then call that service inside of the controller. This workflow would look like
User input --> Data stored in some Service(factory) --> Data Passed to $scope --> >$scope value displayed in partial view (styled_form.html)
But I am lost as to how to dynamically store the user input inside of a service. Ideally this would all occur in realtime (as with the simple first example) as I would like to avoid having a refresh button. Could this be accomplished by creating a function in my controller that updates the values inside my service every few seconds? I need someone with better ideas than myself to point me in the right direction.
Question 2: Is this the correct workflow assuming Q1 is correct and if so what is the correct way to set up my service to handle the user input and pass it to another html page dynamically?
All help much appreciated
Having a service to store data and same controller to serve both view is fine.
Data saving could be done each time, when controller variables are changing.
When new controller is initialized, it my read the date from the service. A separate controller instance will be created for each view.
You may also check this blog to see, how data should be defined within a service, so that changing such data will auto-trigger controller to update view:
http://viralpatel.net/blogs/angularjs-service-factory-tutorial/
Notice: that service returns a reference to the array and controller saves this reference, when it is created.
Ok so I realized my problem was due to the fact that I was misunderstanding how $scope works in Angular not how $route.reload() was working. This topic
$scope variable not changing in controller when changed using directive
and this link in that question
https://github.com/angular/angular.js/wiki/Understanding-Scopes
helped me understand what I was doing wrong. $route.reload() and $templateCache.remove() were working correctly. The problem was that my $scope variable wasn't updating properly because each view was creating a different scope. Paraphrasing one of the links
The $scope is not the model.
The function of the controller is to write from the model to the scope
The function of the view is to display the $scope and write to the model if needed
I solved the problem using the controllerAS syntax discussed in the answers like this,
.controller('LivePreviewController',['$scope', function($scope) {
var vm = this;
vm.form_name = 'temporary name';
}]);
<!-- form_page.html -->
<div ng-controller="LivePreviewController as ctrl">
<input type="text" ng-model="ctrl.form_name>
And in my styled form partial view
<!-- styled_form.html -->
<div> {{ctrl.form_name}} </div>
This causes everything to work as expected

How to select a DIV and specify a controller for it in AngularJS

In my AngularJs Project I have a DIV tag which contains the main navigation bar in my project.
This navigation bar has it's own specific controller which called "topNavController".
I've recently modified my app in order to load everything -somehow- lazily using RequireJs .
The problem is this controller is not dependant on any view/route and has to be loaded directly after my App.js loads .
I'm bootstrapping my app from my RequireJs main file using :
angular.bootstrap(document, ['myApp']);
If I do like :
<div ng-controller="topNavController">...</div>
I get an error which says :
Argument 'topNavController' is not a function, got undefined
which I think it's because myApp is not bootstrapped at the moment Angular tries to bind this controller to my DIV.
I appreciate any suggestions, workarounds or solutions for this.
Thanks in advance.
You miss ng-app to launch the angular bootstrap method.
<div ng-app="myApp">
<div ng-controller="topNavController"></div>
</div>
You should use directive for this behavior
I've fixed it by removing
ng-controller='topNavController'
and making a directive for it, I put all the code from topNavController to topNavDirective and used directive's controller instead.
Thanks to "iScor" hint in the answer above !
here is how the last version of my main RequireJs file looks like :
require(
[
'app',
'routeResolver',
'services/authService',
],
function () {
require(["directives/topNavDirective"]);
angular.bootstrap(document, ['myApp']);
});

Pure CSS modal (::target) with angular ui-router

My first question here :)
I'm porting my site to Angular (I'm new to it) and wondering if I'd be able to keep my pure CSS modal dialogue box while using ui-router. In HTML it looks like this:
<a id="footerMail" href="#openContact"></a>
<div id="openContact" class="modal">
<div class="modal__container">
X
<form class="contact-form" action="/" enctype="application/x-www-form-urlencoded" method="post">
</form>
</div>
It uses ::target on openContact id. But when I'm trying to do that while using ui-router it, obviously, doesn't allow that path to happen since it's not defined as a state. How should I define it since I don't want to redirect to a new URL just open a modal that is overlay of the current state? My $stateProvider looks like this:
$urlRouterProvider.otherwise("/site/editor");
$stateProvider
.state('site', {
url: "/site",
abstract: true,
templateUrl: "partials/site.html",
controller: 'MainController as ctrl'
})
.state('site.director', {
url: "/director",
templateUrl: "partials/gallery.html",
controller: 'GalleryController'
})
.state('site.editor', {
url: "/editor",
templateUrl: "partials/gallery.html",
controller: 'GalleryController'
});
});
The link and modal code would ideally fire from the main 'site" state since it's common for all subpages. Or maybe that's too much work and I should rewrite it and not use CSS approach?
Thanks in advance!
If your modal is always present you should just be able to use ng-show to control when it's displayed.
I typically have an application level control that I have on my body tag and have it created with something like ng-controller=AppController as app. If you had a property of this controller named showModal that you initialize as false you can have your modal created outside of the div for your ui-view and have ng-show="app.ShowModal" then in any link where you want to display the modal in the function you call from its ng-click set app.showModal to false.
There are a lot of good approaches for modals in Angular though. I've been using mdDialog from Angular Material and this one: http://www.bennadel.com/blog/2806-creating-a-simple-modal-system-in-angularjs.htm is also good. I've used the one from UI Bootstrap as well.

What is the difference between an ng-controller directive and a controller in the route?

I worked through the tutorial on the AngularJS website and I noticed that in at step 7, they change how a controller is introduced into the application. Initially, they use a directive:
<body ng-controller="PhoneListCtrl">
...
</body>
However, it later gets changed to use a controller attribute as part of an ng-route.
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
/* rest of routes here */
Here's the git diff where the change is made. Is there a difference between these two techniques?
Controller using a ng-controller directive:
A new $scope is created on ng-controller element.
Explicit view-to-controller connection
Visible with inspect element, etc
Controller in a route:
A new $scope is created per route on the ng-view element.
The controller can request dependencies defined in the route resolve.
Optional view-to-controller connection. Recommended to have a naming convention that maps routes to controllers to views.
One of well-known feature of Angularjs is Single-Page Applications.
If you assign ng-controller attribute directly on the page:
<body ng-controller="PhoneListCtrl">
...
</body>
you can't switch controllers easily for other tasks.
So, use route to switch controllers is one of important step in learning Angular Single-Page feature.
You can have same layout and one different element by using route and ng-view directive.
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/tablets', {
templateUrl: 'partials/tablet-list.html',
controller: 'TabletListCtrl'
}).
If '/phones'
<div ng-view></div>
will include your 'partials/phone-list.html' template
and set 'PhoneListCtrl' as div controller
The same:
If '/tablets'
<div ng-view></div>
will include your 'partials/tablet-list.html' template
and set 'TabletListCtrl' as div controller
This is the difference between two.
ng-view is the cause of the difference. You can't really do this
<div ng-view ng-controller="PhoneListCtrl">
As you'd need to change that controller as the route changed. So basically the router does that for you, and uses the controller you specified when you defined your routes.
You probably can do this:
<div ng-view>
and then in your template:
<div ng-controller="PhoneListCtrl">
and leave out the controller declaration in your routes. Which I suspect would have essentially the same effect, although I've never tried that. Probably better to go with convention here though.
In the 1st case the controller is directly on the page.
Once they change it, that controller is only on the page if the route is /phones otherwise it is some other controller based on some other route.
Yes - the change is this:
if you want to display a specific controller on the page, you can use
<body ng-controller>
BUT
if you want to do routing (application with more than one controller) - you will need to use routing + change the body to:
<body ng-view></body>

Index page separate from rest of AngularJS app?

I'm working on two projects right now using AngularJS, and I'm running into the same problem with both of them.
The problem is that I have an index page that looks completely different from any of the inner pages, which means that my ng-view has to consist of the entire page. This makes it so that any time a route changes, the whole page has to reload instead of just the main content area. This causes things like the header or sidebar to flash briefly.
The only good approach I can think of to make my index page separate from my app is to literally have a separate, static index.html and then all my angularJS pages inside a separate folder so that I can use a more focused ng-view.
Is this the only/best approach there is? Has anyone achieved this, or have any ideas on how to? thanks.
A way to solve this problem would be using UI-Router.
For example:
You could have an app.html which is a page that holds all of your application views. In it add a:
<body>
<div ui-view></div>
</body>
and styles/scripts required by the entire application.
All of your views will go there including the index.html view.
Assuming that the pages except the index have some sort of header/body/footer layout in which the body changes according to the actual page you can use a configuration as follows:
var app = angular.module('app', [])
.config(['$stateProvider', function ($stateProvider)
{
$stateProvider
.state('index', {
url: '/index',
templateUrl: 'index.html',
controller: 'IndexController'
})
.state('root', {
templateUrl: 'root.html',
controller: 'RootController'
})
.state('root.somePage', {
url: '/some-page',
templateUrl: 'some-page.html',
controller: 'SomePageController'
})
.state('root.anotherPage', {
url: '/another-page',
templateUrl: 'another-page.html',
controller: 'AnotherPageController'
});
}
The root.html will be like a masterpage in ASP.NET Webforms so it would be in the form:
<!-- header markup here -->
<div ui-view></div>
<!-- footer markup here -->

Categories