I try to add rest api in yii2-advanced for using angularjs. What I have already done
install angular through composer
add angularController for rest
class AngularController extends ActiveController {
public $modelClass = 'frontend\models\Tour';
public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['corsFilter'] = [
'class' => \yii\filters\Cors::className(),
];
$behaviors['contentNegotiator'] = [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
];
return $behaviors;
}
}
Create AngularAsset and in $public js write this
'angular/angular.js',
'angular-route/angular-route.js',
and change AppAsset adding my js file with angular code
'js/app.js',
This is my angular routeProvider in app.js
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'http://localhost/divnaukraina/frontend/views/angular/views/index.html',
controller: 'index'
})
.otherwise({
redirectTo: '/'
});
}])
In result everything works fine except routes. Page where angular must work
tours page.
But view from template url also renders on other pages main page.
I see that this is because "/" in angular routeprovider but how to disable it?
And I couldn't find much detailed information about yii2 rest+angular, so any tutorials,links an so on will be very useful for me! Thanks.
What was the solution: I changed when('/' on when('/angular' and I also changed link to this page in main navbar to .../index.php?r=tour%2Findex#!/angular. It isn't best solution but...
Related
I'm working on migrating little by little a big angular.js application (that uses ui-router) to angular and I opted by using the angular.js application as a base and migrate the different routes one at a time so that once I'm finished I switch everything at once to angular.
These are the steps I've followed:
Bootstap the angular.js application in my main.ts file:
export function bootstrapAngular(extra: StaticProvider[]): any {
setAngularJSGlobal(angular);
if (environment.production) {
enableProdMode();
}
return platformBrowserDynamic(extra)
.bootstrapModule(AppModule)
.catch(err => console.log(err));
}
const downgraded = angular
.module('downgraded', [downgradeModule(bootstrapAngular)])
.directive('appRoot', downgradeComponent({ component: RootComponent, propagateDigest: false }))
;
angular.bootstrap(document, ['app', downgraded.name]);
Inside my index.html
<app id="app"></app>
This works fine.
Inside my main angular.js component I add the tag of my downgraded main Angular component:
<div class="app__view" id="appContent">
<ui-view></ui-view>
<app-root></app-root>
</div>
This is how my main module is configured
const COMPONENTS = [
TestComponent,
RootComponent,
];
#NgModule({
declarations: COMPONENTS,
imports: [
BrowserModule,
NxModule.forRoot(),
RouterModule.forRoot(routes, {
initialNavigation: 'enabled',
useHash: true
})
],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' }
],
entryComponents: COMPONENTS,
exports: COMPONENTS
})
export class AppModule {
ngDoBootstrap(): void { }
}
Everything works fine so far. I can see my angular component inside my angular.js application.
The problem comes when I add the to my main root component
I can see the router-outlet rendering but nothin next to it, eventhough the route matches.
export const routes: Route[] = [
{ path: 'dashboard', component: TestComponent }
];
When I point my browser to /#/dashboard this is the router tracing that I see:
And the test component just doesn't render.
I need some help, can't think of anything else to try.
First of all: if you want to go hybrid and start moving parts of ng1 to ngx, you need to bootstrap your ng1 app from ngx as you did, but not by downgrading:
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
(<any>platformRef.instance).upgrade.bootstrap(document.body, ['nameOfNg1App']);
});
Than you should provide the entry point for ui-router within your app.component.html:
<div ui-view></div>
You also need to provide an url handling strategy to tell angular, which routes to handle. I had an AppRoutingModule, which was imported by the AppModule. And this one provided the handler:
#NgModule({
imports : [
RouterModule.forRoot(routes, {useHash: true})
],
exports : [
RouterModule
],
// provide custom UrlHandlingStrategy to separate AngularJs from Angular routes
providers: [
{
provide : UrlHandlingStrategy,
useClass: Ng1Ng2UrlHandlingStrategy
}
]
})
export class AppRoutingModule {
}
And the Handling strategy, I used path prefixes to separate ng1 from ngx routes, but you can choose a simpler separation if needed:
import { UrlHandlingStrategy } from '#angular/router';
export class Ng1Ng2UrlHandlingStrategy implements UrlHandlingStrategy {
private ng1Urls: string[];
constructor() {
this.ng1Urls = [
'prefix1',
];
}
shouldProcessUrl(url) {
url = url.toString();
return this.ng1Urls.findIndex(ng1Url => new RegExp(`^\/${ng1Url}([|\?|\/](.*)){0,1}$`).test(url)) === -1;
}
extract(url) {
return url;
}
merge(url, whole) {
return url;
}
}
Oh, and for some reasons I had to stick to # URLs, while running hybrid.
With this setup you start an ngx app, that has a container that runs the ui-router. Within your ng1 app you can then use downgraded ngx components and services.
To have ngx-routes and ng1 routes in parallel, your (ngx) app.component.html consists of
<div ui-view></div>
<router-outlet></router-outlet>
You find more details of this strategy here: https://blog.nrwl.io/upgrading-angular-applications-upgrade-shell-4d4f4a7e7f7b
The solution involved:
Getting rid of UrlHandlingStrategy completely
Disabling initialNavigation
Creating empty routes in both route configurations
Injecting the ng2 router in the ng1 application and adding the following logic
angular.module('app').config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider.state('ng2', { });
$urlRouterProvider.otherwise(($injector, $location) => {
const $state = $injector.get('$state');
const ng2Routes = ['dashboard'];
const ng2Router = $injector.get('ng2Router');
const url = $location.url();
if (ng2Routes.some(feature => url.startsWith('/' + feature))) {
$state.go('ng2');
ng2Router.navigate([url]);
} else {
$state.go('messages');
}
});
}]);
I have an AngularJS 1.6 app that loads a list of app modules fetched from the server. Every app has a different list of modules, so it would mean a lot of overhead code to create one unique route per module.
We are using ui-router, and it provides a really cool route config method called componentProvider which allows us to dynamically load a component pages on (in our case) the $routeParams.
Here is the working Angular 1.6 code:
//...
.state('applications.apps.modules', {
url: '/:moduleSlug',
data: {
addToSideMenu: false,
},
// create a dynamic component
componentProvider: ($stateParams: StateParams) => {
// This outputs "application-<name of module>"
return `application${capitalizeFirstLetter(snakeToCamel($stateParams.moduleSlug))}`;
},
resolve: {
application: (applications, $stateParams: StateParams) => {
return applications.filter(app => app.slug === $stateParams.appSlug)[0];
},
},
})
//...
This code will return a string application-<name of module>, and it allows us to load that module dynamically.
We're trying to find a way to do the same thing in that app but in Angular 4.
Any idea?
Ive been trying to setup my Angular webApp to work with prerender.io however my app does not have any of the built in middleware they desire; I have decided I want to work with express.js as most the tutorials set up the project using this.
Example setup
I am able to create a project that works with prerender if I make it new using the express.js generator plugin. Thats all good and dandy but it doesn't really help me out; as I am not creating a project, rather I want to add prerender to my existing project. Is there a way to create a new express.js project and import an entire angular app (with all folder structure; etc, basically moving the app's root directory over to the new express app)into it and have all the routing etc be unaffected?
How would one go about adding Prerender to an already created angularJS webapp? (like the basic one seen below) I've tried a bunch of times throughout the day and just ended up installing a ton of ugly dependancies into my project and having to delete them all; any help is appreciated. ::
app.js:
// app.js
var app = angular.module('myPremadeApp', ['ngRoute'])
$locationProvider.hashPrefix('!');
.config(function($routeProvider, $locationProvider){
$routeProvider.when('/', {
templateUrl : 'views/homeView.html',
controller: 'homeController'
})
.when('/about', {
templateUrl : '/views/aboutView.html',
controller: 'aboutController'
})
.when('/features', {
templateUrl : '/views/featuresView.html',
controller : 'featuresController'
})
.otherwise({
redirectTo : '/'
});
});
function mainController($scope) {
$scope.seo = {
pageTitle : '', pageDescription : ''
};
}
function homeController($scope) {
$scope.$parent.seo = {
pageTitle : 'setup the thing',
pageDescripton: 'stuff for description'
};
}
function aboutController($scope) {
$scope.$parent.seo = { pageTitle : 'About',
pageDescripton: 'We are a content heavy website so we need to be indexed.'
};
}
function featuresController($scope) {
$scope.$parent.seo = { pageTitle : 'Features', pageDescripton: 'Check out some of our awesome features!' };
}
I have a sample MVC6 single page app with one view in which I want to load 2 Angular partials using ngRoute. You can have a look at it at GitHub
There are 3 URLs in the app:
localhost - Index.cshtml
localhost/games - Index.cshtml with Angular's gamelist.html partial
localhost/games/2 - Index.cshtml with Angular's game.html partial
The routes config is the following:
MVC:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
routes.MapRoute("gamelist", "games", new { controller = "Home", action = "Index"});
routes.MapRoute("gameWithId", "games/2", new { controller = "Home", action = "Index" });
});
Angular:
myApp.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/games', {
templateUrl: 'partials/gameslist.html',
controller: 'GameController',
controllerAs: 'ctrl'
})
.when('/games/:gameId', {
templateUrl: 'partials/game.html',
controller: 'GameController',
controllerAs: 'ctrl'
});
$locationProvider.html5Mode(true);
}]);
It all works perfectly fine as long as I start the app from the home page '/' and then navigate to the partials using the links on the page. The problem is that the URL #3 (localhost/games/2) does not work if I start the app from it, by typing it in the address bar. The URL #2 (/games/) does work.
The reason why #3 does not work is that MVC removes '/games' part from the URL and what Angular gets is just '/2'. If you run the sample app, you will see that '$location.path = /2'. Of course Angular cannot map using that path and no partial is rendered. So my question is - how to make MVC return the full path to the client so that Angular can map it?
You can get it to work with HTML5 mode, you just need to ensure that every request maps back to your Index.cshtml view. At that point the AngularJS framework loads, client-side routing kicks in and evaluates the request URI and loads the appropriate controller and view.
We've done this with multiple Angular apps inside MVC with different .cshtml pages, though we use attribute routing with the wildcard character, e.g.
[Route("{*anything}")]
public ActionResult Index()
{
return View("Index");
}
The wildcard operator (*) tells the routing engine that the rest of the URI should be matched to the anything parameter.
I haven't had chance to get to grips with MVC6 yet but I think you can do something like this with the "new" version of attribute routing?
[HttpGet("{*anything:regex(^(.*)?$)}"]
public ActionResult Index()
{
return View("Index");
}
To make link #3 work from the browser's address bar, I turned off "html5Mode" in Angular and made links #-based.
kudos to this blog
I think it is a better solution.
His solution is rewriting the request that doesn't fit to any route and doesn't have any extension to the landing page of angular.
Here is the code.
public class Startup
{
public void Configure(IApplicationBuilder app, IApplicationEnvironment environment)
{
// Route all unknown requests to app root
app.Use(async (context, next) =>
{
await next();
// If there's no available file and the request doesn't contain an extension, we're probably trying to access a page.
// Rewrite request to use app root
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/app/index.html"; // Put your Angular root page here
await next();
}
});
// Serve wwwroot as root
app.UseFileServer();
// Serve /node_modules as a separate root (for packages that use other npm modules client side)
app.UseFileServer(new FileServerOptions()
{
// Set root of file server
FileProvider = new PhysicalFileProvider(Path.Combine(environment.ApplicationBasePath, "node_modules")),
// Only react to requests that match this path
RequestPath = "/node_modules",
// Don't expose file system
EnableDirectoryBrowsing = false
});
}
}
js / chaplin.js and I'm having some dificulties to find documentation about the chaplin routes.
I have the following code (cofeescript), it works in chrome but in firefox it tries to navigate doing a request to the server and it returns a 404 error. The server side is an ASP MVC 3 project.
Routes
define ->
'use strict'
(match) ->
match '', 'page#home'
match 'Home', 'page#home'
match 'Services', 'page#services'
PageController
define [
'controllers/base/controller',
'views/home_view',
'views/services_view'
], (Controller, HomeView, ServicesView) ->
'use strict'
class PageController extends Controller
historyURL: (params) ->
''
home: (params) ->
#view = new HomeView()
return
services: (params) ->
#view = new ServicesView()
return
Navbar (HTML)
Home
Services
PS: Another thing that I would like to know is, when the controller is called:
define [
'controllers/base/controller',
'views/home_view',
'views/services_view'
], (Controller, HomeView, ServicesView) ->
'use strict'
This is loading with AJAX both the Home and service view I would like to load only the one that has been actually called?
Thanks for your help.
Already answer on
https://github.com/chaplinjs/chaplin/issues/180