Dynamically Change Page <Head> In Angular2 - angularjs

In AngularJS 1 we simply add ng-app at top of the HTML tag and bind services to change metadata on the fly.
But in Angular2 the quickstart app in official site made index.html completely static (css, meta, ...), only left app tag to bind with bootstrap()
Now how we can do when we want to build many panel with different style and js plugins, meta keyword...

update
There is now also the Meta service that allows to modify meta tags
https://angular.io/docs/ts/latest/api/platform-browser/index/Meta-class.html
original
There is currently no support built in in Angular. There is an open issue though for (almost) full support of meta tags and other stuff in <head>.
Currently there is only built-in support for the <title> tag using the Title service.
constructor(private title:Title) {
}
updateTitle(title:string) {
this.title.setTitle(title);
console.log(this.title.getTitle());
}

I have just released #ngx-meta/core plugin, using that you can add meta information inside the data property of routes:
const routes = [
{
path : 'home',
component : HomeComponent,
data : {
meta : {
title : 'Sweet home',
description : 'Home, home sweet home... and what?',
}
}
},
{
path : 'duck',
component : DuckComponent,
data : {
meta : {
title : 'Rubber duckie',
description : 'Have you seen my rubber duckie?',
}
}
},
{
path : 'toothpaste',
component : ToothpasteComponent,
data : {
meta : {
title : 'Toothpaste',
override : true, // prevents appending/prepending the application name to the title attribute
description : 'Eating toothpaste is considered to be too healthy!',
}
}
}
...
];
If you want to override values supplied at the route configuration, it's possible to set meta information programmatically - just in the component class:
...
import { Component, OnInit } from '#angular/core';
import { MetaService } from '#ngx-meta/core';
...
#Component({
...
})
export class ItemComponent implements OnInit {
...
constructor(private metaService: MetaService) { }
...
ngOnInit() {
this.item = //HTTP GET for "item" in the repository
this.metaService.setTitle(`Page for ${this.item.name}`);
this.metaService.setTag('og:image', this.product.imageUrl);
}
}
You can find detailed instructions at #ngx-meta/core github repository. Also, source files might be helpful to introduce a custom logic.

Related

Gatsby V4: How to implement gatsby-plugin-react-i18next for client only routes

I'm trying to implement client routes in Gatsby together with gatsby-plugin-react-i18next for two languages. I followed officel Gataby documentation and there is no client only path implementation explained.
Below is the code i implemeted.
gatsby-node.js
function langPrefix(page) {
return page.context.language === page.context.i18n.defaultLanguage &&
!page.context.i18n.generateDefaultLanguagePage
? ''
: `/${page.context.language}`
}
exports.onCreatePage = ({ page, actions }) => {
const { createPage } = actions
// Removing the ^ skips an optional /:lang prefix
if (page.path.match(/\/app/)) {
// adding lang if it's not the default page.
page.matchPath = `${langPrefix(page)}/app/*`
createPage(page)
}
}
Appjs
src/app/app.js
function App() {
return (
<>
<Router basepath="/:lang/app">
<PrivateRoute path="/accounthome" component={AccountHome} location=""/>
</Router>
<Router basepath="/app">
<PrivateRoute path="/accounthome" component={AccountHome} location=""/>
</Router>
</>)
}
export default App
Gatsby config
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`, // name given to `gatsby-source-filesystem` plugin.
languages: ["en", "fr"],
defaultLanguage: `en-us`,
fallbackLanguage: `en-us`,
// if you are using Helmet, you must include siteUrl, and make sure you add http:https
siteUrl: `https://my.costco.com/`,
ns: langTranslationConfig,
// you can pass any i18next options
i18nextOptions: {
interpolation: {
escapeValue: false // not needed for react as it escapes by default
},
nsSeparator: false
},
pages: [
{
matchPath: '/:lang/app/accounthome',
getLanguageFromPath: true,
excludeLanguages: ['en-ca']
},
{
matchPath: '/preview',
languages: ['en']
}
]
}
}
Router path : http://localhost:8000/en-us/app/accounthome
When am accessing this rote in browser This code show Gatsby.js development 404 page not found. Any pointer what am missing and am not sure how to access the translation contents in client only route page example account home page. Do i need to write the graph query in account home page or i dont need to ?
I think your regex is leaking your code. I guess it should look like:
if (page.path.match(/^\/app/)) {
page.matchPath = `${langPrefix(page)}/app/*`
createPage(page)
}
Either way, check the created page list by accessing the 404 page in development mode. By default, there should be a list of all created pages. Check the paths there to see if you can spot any mistakes or trailing slash issues.
The problem here is that your page is not being generated properly, that's why it throws a 404, so checking the created pages may help you to spot the mistake or at least, a thread to pull.
After a few research I've seen that you are basing your approach in: Gatsby can't find client routes when using gatsby-plugin-react-i18next
In their case, the langPrefix function is only prefixing the language to the page slug if it's the default one:
function langPrefix(page) {
return page.context.language === page.context.i18n.defaultLanguage &&
!page.context.i18n.generateDefaultLanguagePage
? ''
: `/${page.context.language}`
}
In your case, I'm not sure the plugin supports en-us (it's en-US according to https://github.com/microapps/gatsby-plugin-react-i18next/issues/100) and I think that's the reason why there's a leak in your page creation. Try using en instead of en-us or directly looking for en-US paths.

Window.OneSignal showing 404 error when i am trying to use it with next.js

I am trying to implement OneSignal web push notifications with the next.js web app. I followed this article
to implement it. But it is not implementing properly as it shows an error. I have doubt that where should I place the window.OnseSignal code shown in step 7.
What I did?
I built a component name NewOneSignal instead of pasting it in App.js (because there is no App.js file in next.js) whose code is given below:
import React, { useEffect } from "react";
const NewOneSignal=()=>{
useEffect(()=>{
window.OneSignal = window.OneSignal || [];
const OneSignal = window.OneSignal;
},[]);
return (
OneSignal.push(()=> {
OneSignal.init(
{
appId: "i pasted my app id here", //STEP 9
promptOptions: {
slidedown: {
enabled: true,
actionMessage: "We'd like to show you notifications for the latest Jobs and updates about the following categories.",
acceptButtonText: "OMG YEEEEESS!",
cancelButtonText: "NAHHH",
categories: {
tags: [
{
tag: "governmentJobs",
label: "Government Jobs",
},
{
tag: "PrivateJobs",
label: "Private Jobs",
}
]
}
}
},
welcomeNotification: {
"title": "The website",
"message": "Thanks for subscribing!",
}
},
//Automatically subscribe to the new_app_version tag
OneSignal.sendTag("new_app_version", "new_app_version", tagsSent => {
// Callback called when tag has finished sending
console.log('new_app_version TAG SENT', tagsSent);
})
);
})
)
}
export default NewOneSignal;
And imported this component in the document.js file. According to this article, I have to put step 8 code in the useEffect but didn't work also, I have tried that also
I am very much sure that the problem is in this file. I paste the OneSignalsdk script in head section of the _document.js file.Also, i moved the two service worker files in a public folder as shown in the article. Please help me to make this code work

Handlebars custom helper - migrating to Webpack

We have an app based on backbone, marionette and handlebars, without import/export or require methods, managed with grunt and we are trying to migrate to webpack.
I am having an issue with a custom helper for handlebars.
The code of our helper :
'use strict';
function I18n() {
this.constructor(arguments);
}
I18n.prototype = {
constructor: function () {
...some stuff
}
get: function () {
...some stuff
}
...some other functions
}
ourNameSpace.I18n = new I18n();
And it's included with this function in a file to load it globally :
Handlebars.registerHelper('i18n', _.bind(ourNameSpace.I18n.get, ourNameSpace.I18n));
Then we are using it in the template like this :
{{i18n "LblEmail"}}
I tried to use handlebars-loader and I added this query object into webpack.config to add it to the bundle :
{
test: /\.hbs$/,
use: {
loader: 'handlebars-loader',
query: {
helperDirs: [
path.resolve(__dirname, 'public/assets/js/common/i18n/')
]
}
}
}
Webpack add our helper code in the bundle, but when it's supposed to be called in the template I have this error :
Uncaught TypeError: __default(...).call is not a function
Webpack generated code of the bundle where is the call :
...
+ alias2(__default(__webpack_require__(2)).call(alias1,"LblEmail",{"name":"i18n","hash":{},"data":data}))
...
In a second time I also tried to add an export in the helper, even though we don't use the import/export method (yet) in our app. Adding this at the end of helper file :
export default I18n
That fix the error but the helper doesn't seem to work because all texts on the page are empty (instead of displaying i18n translation or keys)
Does someone did the same kind of migration with handlebars custom helper or would know how I can refactor that so Webpack can handle it properly and the bundle can execute it correctly ?
So after few months I will reply to my own question, I managed to fix our problem like this :
I rewrite our old legacy helper (with custom functions) by creating more modern ones (three, for our three functions that was in legacy helper) relying on I18nJS:
import I18nJs from 'i18n-js';
const I18n = key => I18nJs.t(key);
export default I18n;
It is loaded by webpack with handlebars loaders like this :
{
test: /\.hbs$/,
use: {
loader: 'handlebars-loader?runtime=handlebars/runtime',
query: {
helperDirs: [path.resolve(__dirname, 'src/js/common/i18n/helper')],
inlineRequires: '/images/',
precompileOptions: {
knownHelpersOnly: false,
},
},
},
}
And in our template we did not have to change anything to use it :
<label>{{i18n "LblEmail"}}</label>
To use localisation on javascript files however we had to make some changes :
I created a "helper" (not handlebar helper) implementing same logic than handlebars helper :
import I18nJs from 'i18n-js';
const I18n = {
get(key) {
return I18nJs.t(key);
},
... some other functions
};
export default I18n;
We import this file and use its function as usual in modern stacks :
import I18n from '../common/i18n/I18nSt';
...
console.log(I18n.get('PasswordMissing'));
So we had to do minor refactor when we call our translations function in our js files, It was like this before :
console.log(OurNamespace.I18n.get('PasswordMissing'));

Angular 2 (Ionic 2) call a function in a page when ever page is displayed

When ever my home page in angular 2(ionic 2) app is loaded I want call service/function. How to achieve this?
For the first time when the app is loaded(home page is loaded) I can write this in the constructor, but when user start using the app and push new pages into nav controller and pop them to come back to home page, the constructor wont get called again.
I am stuck here.
I would like to know what is the best way to achieve this feature?
I am new to angular2 and ionic2 framework (also don't have experiences in angular1 and ionic1), please help me out.
Many Many Thanks.
update
sample code of what I tried, but didn't worked.
import {Page, NavController, Platform, Storage, SqlStorage} from 'ionic-angular';
#Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
static get parameters(){
return [[NavController],[Platform]];
}
ngOnInit() {
console.log("Showing the first page!");
}
constructor(nav, platform){
this.nav = nav;
this.platform = platform;
}
}
onPageWillEnter() worked for me.
import {Page, NavController, Platform, Storage, SqlStorage} from 'ionic-angular';
#Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
static get parameters(){
return [[NavController],[Platform]];
}
onPageWillEnter() {
console.log("Showing the first page!");
}
constructor(nav, platform){
this.nav = nav;
this.platform = platform;
}
}
Ionic lifecycle hook
IONIC 2 RC1 Update
ionViewWillEnter() {
console.log("this function will be called every time you enter the view");
}
You can leverage the lifeCycle-hooks, specifically ngOnInit() or ngAfterViewInit().
Here is a simple tutorial.
For Example:
// Annotation section
#Component({
selector: 'street-map',
template: '<map-window></map-window><map-controls></map-controls>',
})
// Component controller
class StreetMap {
ngOnInit() { //here you can call the function wanted
// Properties are resolved and things like
// this.mapWindow and this.mapControls
// had a chance to resolve from the
// two child components <map-window> and <map-controls>
}
}
Update: it works for pure Angular2 applications, for IonicFramework specific solution see
#DeepakChandranP's answer.

play framework route that matches all

I'm working on an angular app using play framework for my rest-services. Everything in the public folder is an angular app (stylesheets, javascripts, images and html). I want every request that is not for something in the stylesheets, javascripts, templates or images folder to be routed to the index.html page. This is so that angular routing can take over from there...
As a side note i can mention that I am going to place every restservice under /services/ which links to my own java controllers.
Is it possible in play framework 2.3.4 to define a route that catches all without having to use the matching elements?
This is my route defs so far:
GET / controllers.Assets.at(path="/public", file="index.html")
GET /stylesheets/*file controllers.Assets.at(path="/public/stylesheets", file)
GET /javascripts/*file controllers.Assets.at(path="/public/javascripts", file)
GET /templates/*file controllers.Assets.at(path="/public/templates", file)
GET /images/*file controllers.Assets.at(path="/public/images", file)
#this line fails
GET /* controllers.Assets.at(path="/public", file="index.html")
It's not possible to omit usage of matching elements but you can route a client via controller. The route definition looks like this:
GET /*path controllers.Application.matchAll(path)
And the corresponding controller can be implemented as follows:
public class Application extends Controller {
public static Result matchAll(String path) {
return redirect(controllers.routes.Assets.at("index.html"));
}
}
Update
If you don't want to redirect a client you can return a static resource as a stream. In this case a response MIME type is required.
public class Application extends Controller {
public static Result matchAll(String path) {
return ok(Application.class.getResourceAsStream("/public/index.html")).as("text/html");
}
}
For this task you can use onHandlerNotFound in Global class which will render some page without redirect:
import play.*;
import play.mvc.*;
import play.mvc.Http.*;
import play.libs.F.*;
import static play.mvc.Results.*;
public class Global extends GlobalSettings {
public Promise<Result> onHandlerNotFound(RequestHeader request) {
return Promise.<Result>pure(notFound(
views.html.notFoundPage.render(request.uri())
));
}
}
Answer for scala developers using playframework :)
Similar to above one about creating controller which will accept parameters and then omit them.
Example routing:
GET / controllers.Assets.at(path="/public", file="index.html")
GET /*ignoredPath ui.controller.AssetsWithIgnoredWildcard.at(path="/public", file="index.html", ignoredPath: String)
controller with assets injected by framework:
class AssetsWithIgnoredWildcard #Inject() (assets: Assets) {
def at(
path: String,
file: String,
wildcardValueToIgnore: String,
aggressiveCaching: Boolean = false): Action[AnyContent] = {
assets.at(path, file, aggressiveCaching)
}
}

Resources