Oracle Jet 11 - DetailedRouteConfig canEnter equivalent (Ojet 3.2) - path

In Oracle Jet 3.2 I used to be able to specify is a user use a route or not:
self.routingInfo = ko.observable({
'signin': {label: 'Sign In', canEnter: true, isDefault: true},
'home': {label: 'Home', canEnter: true},
'adminPage': {label: 'Admin', canEnter: self.isAdmin},
});
In Oracle Jet 11 the routes and core router syntax seems slightly different and it doesn't seem like I can use canEnter in the route details (the code comes from the navdrawer default template)
let navData = [
{ path: '', redirect: 'dashboard' },
{ path: 'dashboard', detail: { label: 'Dashboard', iconClass: 'oj-ux-ico-bar-chart' } },
{ path: 'incidents', detail: { label: 'Incidents', iconClass: 'oj-ux-ico-fire' } },
{ path: 'customers', detail: { label: 'Customers', iconClass: 'oj-ux-ico-contact-group'} },
{ path: 'about', detail: { label: 'About', iconClass: 'oj-ux-ico-information-s' } }
];
The oracleJet doc here: https://www.oracle.com/webfolder/technetwork/jet/jsdocs/CoreRouter.html#CreateOptions
doesn't give me the "detail" params I can use (ex: label, iconClass,..).
How can I mimic the behavior of canEnter in OracleJet 11 CoreRouter?

It depends on how you implemented navigation in UI, too, but below solutions can at least give you a starting point if it isn't exactly what you have in your app.
If you're using an <oj-navigation-list> for navigation (with current-item="[[router.stateId]]" selection="{{router.stateId}}"), you can use ojBeforeSelect event to check if user has permissions to access that page.
Another idea is to have a property roles on each route in navData and filter it when building your data provider for the <oj-navigation-list> with the user's permissions.

Related

#nuxt/auth $auth.loggedIn is not reactive

I have two problems with #nuxt/auth, the first is that $auth.loggedIn is not reacting to change, I have the below code for my app and when I login or logout I have to chage the route in order for the header to reacte and change state (hide or show login/register and user info) below is my code for template.
<userdown #logout="$auth.logout()" v-if="$auth.loggedIn"/>
<div v-else>
<nuxt-link :to="localePath('/login')" class="text-white mx-4">
{{$t("links.login")}}
</nuxt-link>
<nuxt-link :to="localePath('/register')" class="text-white mx-4">
{{$t("links.register")}}
</nuxt-link>
</div>
The second one is that the redirect is not working automatically (example when login it should redirect to home page but nothing happen)
auth: {
strategies: {
local: {
token: {
property: 'token'
},
user: {
property: 'user'
},
endpoints: {
login: { url: '/api/auth/login', method: 'post' },
logout: { url: '/api/auth/logout', method: 'post' },
user: { url: '/api/auth/user', method: 'get' }
}
}
},
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/'
}
}
I've encountered the same issue in production. Locally, everything works as expected. Anyways, I've ended up using:
v-show="$store.state.auth.loggedIn"

Preloading Strategy issue for forChild routes

My application is a bit large in size. It contains many feature modules and most are lazily loaded. I want to preload a lazily loaded module, which is included in forChild routes.
For this, I have referred to Angular documentation and followed their steps. I have provided a custom preloading strategy service mentioned below.
This is my custom preloading strategy file:
#Injectable()
export class CustomPreloadingWithDelayStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
if (route.data && route.data['preload']) {
return load();
} else {
return Observable.of(null);
}
}
}
app-routing file,
const routes: Routes =
[
XXX,
{
path: '',
data: {
base: true
},
component: MyComp,
children: [
{
path: 'page1/:id',
loadChildren: 'XXXXXXX'
},
{
path: 'page2',
loadChildren: 'XXXXXXXX'
},
{
path: 'page3',
loadChildren: 'app/feature-modules/folder1/my-folder1-module#Folder1Module'
}];
#NgModule({
imports: [RouterModule.forRoot(routes, {useHash: true, preloadingStrategy: CustomPreloadingWithDelayStrategy})],
exports: [RouterModule],
entryComponents: [ ]
})
export class AppRoutingModule {}
My Folder1Module's routing file:
const routes: Routes = [{
path: 'sub-page1/:data1/:data2',
loadChildren: 'app/feature-modules/sub-pages/pages/sub-page1.module#SubPage1Module'
}, {
path: 'sub-page2/:data1',
loadChildren: 'app/feature-modules/sub-pages/pages/sub-page2.module#SubPage2Module',
data: {preload: true}
}];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class Folder1RoutingModule {
}
So when I open this route /page3/sub-page1/data1/data2, SubPage2Module is to be preloaded. But that is not happening.
I have spent nearly 2 hours to understand why the module is not pre-loaded even when I was doing everything right.
The problem lies with children, any route that is defined and further distributed with children is considered as a normal route even if you've defined your modules within the children with the help of loadChildren. The preloadingStrategy just takes modules to preload and not path/routes. The moment you've defined your routes under children it considers it as normal route and moves on to scan other routes that are provided with loadChildren. Following is the interpretation of Angular with your routes:
const routes: Routes =
[
XXX, // Normal path
{
path: '',
data: {
base: true
},
component: MyComp,
children: [ // Normal path (no module) as children is used, move on
{
path: 'page1/:id',
loadChildren: 'XXXXXXX'
},
{
path: 'page2',
loadChildren: 'XXXXXXXX'
},
{
path: 'page3',
loadChildren: 'app/feature-modules/folder1/my-folder1-module#Folder1Module'
},
],
},
{ path: 'abc', loadChildren: '../path/to/module#AbcModule', data: { preload: true }} // Module found, preload it!
];
If you debug closely in your custom CustomPreloadingWithDelayStrategy then you will observe that your route /page3/sub-page1/data1/data2 can't even make up to the route parameter of preload() method because preloading is all about loading module and not about loading routes. However, our route abc does make an appearance! Hope it helps :)

Redirect to requested URL if already authorized in VueJS Router

I'm trying to understand the VueJS router model by building this tutorial app. The app redirects you to the home page if you try to open a direct link like https://example.com/meetups even if you already logged in and authorized. Why is that and how to open the requested URL instead?
auth-guard.js
import {store} from '../store'
export default (to, from, next) => {
if (store.getters.user) {
next()
} else {
next('/signin')
}
}
index.js
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/signup',
name: 'Signup',
component: Signup
},
{
path: '/signin',
name: 'Signin',
component: Signin
},
{
path: '/meetups',
name: 'Meetups',
component: Meetups,
beforeEnter: AuthGuard
},
{
path: '/meetup/new',
name: 'CreateMeetup',
component: CreateMeetup,
beforeEnter: AuthGuard
},
{
path: '/meetups/:id',
name: 'Meetup',
props: true,
component: Meetup,
beforeEnter: AuthGuard
},
{
path: '/profile',
name: 'Profile',
component: Profile,
beforeEnter: AuthGuard
}
],
mode: 'history'
})
The auth-guard.js will redirect someone to the sign in page if the store user is null.
When does the store user get updated?
Inside main.js:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.$store.dispatch('autoSignIn', user)
}
})
the onAuthStateChanged method is async, meaning that it might finish executing after you have asked the browser to go to a page that is guarded by auth-guard.js.
This is mostly what happens if you open a new tab or window and type in the url of a guarded page.
This behaviour would work properly if first, the site is fully loaded on an unguarded page (let's say /signin) and then the user would try to click on a guarded page (let's say /meetups). By the time the user clicks on the link, there is a good chance the onAuthStateChanged method will have returned and commited to the store ( this.$store.dispatch('autoSignIn', user) ), allowing onAuthStateChanged to execute properly.
EDIT
One way to solve this problem would be to instanciate the Vue app only once the onAuthStateChanged has returned a user like so:
main.js
firebase.initializeApp({
apiKey: 'AIzaSyCFYWd6FpR53u4hSPXQSjOYeZNPF1FxG2M',
authDomain: 'yt-devmeetup.firebaseapp.com',
databaseURL: 'https://yt-devmeetup.firebaseio.com',
projectId: 'yt-devmeetup',
storageBucket: 'gs://yt-devmeetup.appspot.com'
})
firebase.auth().onAuthStateChanged((user) => {
if (user) {
store.dispatch('autoSignIn', user)
store.dispatch('fetchUserData')
}
new Vue({
el: '#app',
router,
store,
render: h => h(App),
created() {
this.$store.dispatch('loadMeetups')
}
})
})

How to pass a Hash to Grape API method?

I'm having problems with the Grape gem and the parameters validation.
The idea behind this is to create a complex entity using nested attributes through an API service.
I have a method to create a trip, trip have many destinations and i want to pass that destinations using a hash (using the accepts_nested_attributes_for helper).
I have this grape restriction over the parameter:
requires :destinations, type: Hash
And I'm trying to send something like this:
{ destinations => [
{ destination: { name => 'dest1'} },
{ destination: { name => 'dest2'} },
{ destination: { name => 'dest3'} }
]}
In order to build something like the structure below inside the method and get the trip created:
{ trip: {
name: 'Trip1', destinations_attributes: [
{ name: 'dest1' },
{ name: 'dest2' },
{ name: 'dest3' }
]
}}
I'm using POSTMAN chrome extension to call the API method.
Here's a screen capture:
If someone can help me i would be very grateful.
By the looks of what you are trying to send, you need to change the Grape restriction, because destinations is an Array, not a Hash:
requires :destinations, type: Array
You don't need the "destination" hash when sending the request:
{ destinations => [
{ name => 'dest1', other_attribute: 'value', etc... },
{ name => 'dest2', other_attribute: 'value', etc... },
{ name => 'dest3', other_attribute: 'value', etc... }
]}
This creates an Array of hashes.
In order to send this through POSTMAN, you'll need to modify that destinations param your sending and add multiple lines in POSTMAN. Something like:
destinations[][name] 'dest1'
destinations[][other_attribute] 'value1'
destinations[][name] 'dest2'
destinations[][other_attribute] 'value2'
destinations[][name] 'dest3'
destinations[][other_attribute] 'value3'
Hope this answers your questions. Let me know if this is what you were looking for.

Sencha Touch 2: Trying to create a login form

I am a 100% newb to Sencha and am trying to take a stab at re-factoring my company's mobile app.
Here is my app.js:
Ext.application({
name: 'RecruitTalkTouch',
views: ['Login'],
launch: function () {
Ext.Viewport.add([
{ xtype: 'loginview' }
]);
}
});
Login.js View:
Ext.define('RecruitTalkTouch.view.Login', {
extend: 'Ext.Container',
alias: "widget.loginview",
xtype: 'loginForm',
id: 'loginForm',
requires: ['Ext.form.FieldSet', 'Ext.form.Password', 'Ext.Label', 'Ext.Button' ],
config: {
title: 'Login',
items: [
{
xtype: 'label',
html: 'Login failed. Please enter the correct credentials.',
itemId: 'signInFailedLabel',
hidden: true,
hideAnimation: 'fadeOut',
showAnimation: 'fadeIn',
style: 'color:#990000;margin:5px 0px;'
},
{
xtype: 'fieldset',
title: 'Login Example',
items: [
{
xtype: 'textfield',
placeHolder: 'Email',
itemId: 'userNameTextField',
name: 'userNameTextField',
required: true
},
{
xtype: 'passwordfield',
placeHolder: 'Password',
itemId: 'passwordTextField',
name: 'passwordTextField',
required: true
}
]
},
{
xtype: 'button',
itemId: 'logInButton',
ui: 'action',
padding: '10px',
text: 'Log In'
}
]
}
});
Login.js Controller:
Ext.define('RecruitTalkTouch.controller.Login', {
extend: 'Ext.app.Controller',
config: {
refs: {
loginForm: 'loginForm'
},
control: {
'#logInButton': {
tap: 'onSignInCommand'
}
}
},
onSignInCommand: function(){
console.log("HELLO WORLD");
}
});
When I click the submit button, nothing happens. How can I hook up my submit button to listen for events (click, tap, etc) along with submitting the information to a backend API?
In app.js file of your application, add:
controllers: [
'Login'
]
in your application class. And for submitting information, call a Ajax request like this:
Ext.Ajax.request({
url: // api url..,
method: 'POST',
params: {
username: // get user name from form and put here,
password: // get password and ...
},
success: function(response) {
do something...
},
failure: function(err) {do ..}
});
from inside onSignInCommand() function.
You must activate your controller by adding it to the controllers option of your application class.
Then, to submit your data to the backend, you've got several options. You can use a form panel instead of your raw container, and use its submit method. Alternatively, you can use the Ext.Ajax singleton. In this case, you'll have to build the payload yourself. Finally, you can create a model with a configured proxy, and use its save method. This last way is probably the best for long term maintainability... Even if in the case of a simple login form, that may be a little bit overkill.
Can u please refer this sample app to create login form. Its very simple app please go through it.
http://miamicoder.com/2012/adding-a-login-screen-to-a-sencha-touch-application/

Resources