What are the steps needed to make a redirect to search results without page reload in Nuxt.js? So far, it's been sorted this way:
search() {
location.href = '/search?phrase=' + this.phrase;
},
and I need to make it happen without page refresh. In the page's dir is a search directory with index.vue inside like
/search/index.vue
so far I found some solutions to use $router.on(event) so I have something like this
created() {
this.$nuxt.$on('pushState', params => {
history.pushState(
{},
null,
'search?phrase=' + encodeURIComponent(params)
)
})
},
beforeDestroy() {
this.$nuxt.$off('pushState')
},
methods: {
search() {
this.$nuxt.$emit('pushState', this.phrase)
}
},
However, all I have managed to achieve is to change the URL, but no redirection is happening. So what are the steps necessary to make this redirection to the mainURL + 'search?phrase=' + query so that user would be indeed redirected, and the items were displayed like right now.
In the /search/index.vue items are fetched and displayed, but how do I force router to go to this URL not only display it in the browser?
Related
I am working on my first vue project, and am having an issue with the redirect that I want to have happen. My project is a basic to-do app. In my add todo component I have the following function that fires after I submit the form.
methods: {
addTodo:function(e) {
e.preventDefault()
let newTodo = {
title:this.title,
completed: false,
dueDate: new Date(this.date3)
}
console.log(newTodo)
this.$emit('add-todo', newTodo)
window.location.href = '/';
}
}
The event that is emitted then fires up the component tree until it reaches my app.vue file that fires the following method:
addTodo: function (newTodo) {
this.todos = [...this.todos, newTodo]
this.todos = this.todos.sort((a,b) => a.dueDate - b.dueDate)
}
Unfortunately the issue I am having is that my page redirects to my home page from the window.locatio.href method before my app can update state. I have also tried moving window.location.href='/' to the addTodo method in my app.vue file. However I am still facing the same problem. How can I redirect the user only once the new Todo is added to state?
use created(){} after the last},then add window.location.href="/";
For example I have some page in Nuxt:
data() {
return {
items: []
};
},
asyncData() {
return axios.get('site.com/url')
.then((response) => {
return {
items: response.data
};
});
},
Then I run npm run generate and get statically generated html-page with data (items) from backend server. And when I open this page in browser I see that injected data into the page.
But these items might be updated on the backend so I need to see them once I have got refreshed the page with F5 and without running again npm run generate.
So looks like I should refetch data in mounted() section. Maybe Nuxt has something more suitable for this?
The only option is use crawler: false property in your nuxt.config.js file. It will disable static content generation from you dynamic pages. Here is the documentation.
I m trying to redirect from one URL to another URL using Vue router eg code
{
path: '/verifyemail/:id/:token',
//can i somelogic here when user entered
},
{
path: '/login',
component:login
},
what I m trying to do? when the user registered himself. server send email verification link to his email when a user clicks on verify button from his email then it should first call verifyemail url. where it has an ajax call with a parameter which i m getting from verifyemail url now after the success it should move to login url note:- i don't have any component in my verfiyemail route
is it possible to do this or is there anyother way to achieve this
The route configuration is only data, so there's not really any way to do this exactly as you'd like.
If you create a component to handle the /verifyemail/ route you can just use this.$router.push(redirectToMe) in it. See the Vue Router docs for more information.
Alternatively, this sounds more like something a backend server would handle on it's own, so maybe you don't even need Vue to worry about it.
finally, I arrive with one solution may be this help other
let start, I send the email with verify button which has link something like "localhost:8080/#/verfiyemail/("ACCESSTOKEN")" NOW I my vue part i does something like
in vue-route
path: '/verifyemail/:uid',
beforeEnter:(to, from, next) => {
let uid=to.params.uid;
next({ path: '/', query: { id: uid}})
}
},
{
path: '/',
name: 'Login',
component: Login,
},
and i my login.vue
created(){
this.verfiyemai();
},
methods:{
verfiyemai(){
var _this=this
var submit=0
if(this.$route.query.id!=undefined ){
if(this.$route.query.id.length<=50){
this.$router.push('/');
submit=1;
}
if(submit==0){
this.$http.get('/api/users/confirm?uid='+this.$route.query.id+'')
.then(function (response) {
console.log(response)
})
.catch(function (error) {
console.log(error)
});
}
}
},
}
from email, I redirect the user to verfiyemail with token id as a parameter from verifyemail route, after that I redirect to the login URL passing the parameter as query and in my login vue I had created a method which checks query length if it greater than 50 then it will post axios response to the server
I would love to a #signin route that would open a dialog on top of whatever page there was before.
Let's consider this example app this the following routes:
router.map([
{route: '', moduleId: 'vm/home', title: "Home"},
{route: 'about', moduleId: 'vm/about', title: "About"},
{route: 'signin', moduleId: 'vm/signin', title: 'Sign In'}
]);
Here are example use cases:
User is on # and navigates to #signin: we should see a Sign In dialog on top of Home page
User is on #about and navigates to #signin: we should see a Sign In dialog on top of About page
User navigates to http://localhost:9000/#signin: we should see a Sign In dialog on top of Home page
User is on #signin and closes dialog: we should see a page that was behind the dialog (there's always a page behind).
The dialog and router are both plugins and have no interactions between eachother.
Also having the router display dialog would ignore how the router works - it has a div which it dumps content into. Dialogs exist outside of all of this.
However if you wanted to (I may do this aswell), you could try this.
Add dialog: true to the route map.
Override router.loadUrl method. Check if the route is a dialog route as we marked before, and activate the dialog instead.
I would make the dialog a child route, so then you can know which view to display beneath the dialog. Otherwise you could just have to show the dialog over anything and ignore routing entirely.
Edit: I don't think this would entirely work actually. loadUrl returns a boolean. You could open the dialog and return false to cancel navigation.
Edit2:
My Attempt
The loadUrl method loops through all routes, and each has a callback, so ideally we need to insert our logic into this array.
for (var i = 0; i < handlers.length; i++) {
var current = handlers[i];
if (current.routePattern.test(coreFragment)) {
current.callback(coreFragment, queryString);
return true;
}
}
This array is added to using the routers route method. Durandal calls this method when you map routes, so ideally we could add some extra parameters to the route config and let Durandal handle these. However the configureRoute function is internal to the routing module, so we will need to edit that and make sure we copy changes over when updating Durandal in the future.
I created a new list of dialog routes:
{ route: 'taxcode/add(/:params)', moduleId: 'admin/taxcode/add', title: 'Add Tax Code', hash: '#taxcode/add', nav: false, dialog: true, owner: '#taxcodes' },
{ route: 'taxcode/edit/:id', moduleId: 'admin/taxcode/edit', title: 'Edit Tax Code', hash: '#taxcode/edit', nav: false, dialog: true, owner: '#taxcodes' }
The idea of an owner, is that if there is a case where the initial route is this, we need something behind the dialog.
Now replaced the router.route call in configureRoute with this:
router.route(config.routePattern, function (fragment, queryString) {
if (config.dialog) {
if (!router.activeInstruction()) {
// No current instruction, so load one to sit in the background (and go back to)
var loadBackDrop = function (hash) {
var backDropConfig = ko.utils.arrayFirst(router.routes, function (r) {
return r.hash == hash;
});
if (!backDropConfig) {
return false;
}
history.navigate(backDropConfig.hash, { trigger: false, replace: true });
history.navigate(fragment, { trigger: false, replace: false });
queueInstruction({
fragment: backDropConfig.hash,
queryString: "",
config: backDropConfig,
params: [],
queryParams: {}
});
return true;
};
if (typeof config.owner == 'string') {
if (!loadBackDrop(config.owner)) {
delete config.owner;
}
}
if (typeof config.owner != 'string') {
if (!loadBackDrop("")) {
router.navigate("");
return; // failed
}
}
}
var navigatingAway = false;
var subscription = router.activeInstruction.subscribe(function (newValue) {
subscription.dispose();
navigatingAway = true;
system.acquire(config.moduleId).then(function (dialogInstance) {
dialog.close(dialogInstance);
});
})
// Have a route. Go back to it after dialog
var paramInfo = createParams(config.routePattern, fragment, queryString);
paramInfo.params.unshift(config.moduleId);
dialog.show.apply(dialog, paramInfo.params)
.always(function () {
if (!navigatingAway) {
router.navigateBack();
}
});
} else {
var paramInfo = createParams(config.routePattern, fragment, queryString);
queueInstruction({
fragment: fragment,
queryString: queryString,
config: config,
params: paramInfo.params,
queryParams: paramInfo.queryParams
});
}
});
Make sure you import dialog into the module.
Well maybe all of that is not needed when using a trick with the activation data of your home viewmodel.
Take a look at my Github repo I created as an answer.
The idea is that the route accepts an optional activation data, which the activate method of your Home VM may check and accordingly show the desired modal.
The benefit this way is that you don't need to touch the existing Durandal plugins or core code at all.
I'm though not sure if this fully complies with your request since the requirements didn't specify anything detailed.
UPDATE:
Ok I've updated the repo now to work with the additional requirement of generalization. Essentially now we leverage the Pub/Sub mechanism of Durandal inside the shell, or place it wherever else you want. In there we listen for the router nav-complete event. When this happens inspect the instruction set and search for a given keyword. If so then fire the modal. By using the navigation-complete event we ensure additionally that the main VM is properly and fully loaded.
For those hacks where you want to navigate to #signin, just reroute them manually to wherever you want.
Expanding on my suggestion in the comments, maybe something like this would work. Simply configure an event hook on router:route:activating or one of the other similar events and intercept the activation of /#signin. Then use this hook as a way to display the dialog. Note that this example is for illustrative purposes. I am unable to provide a working example while I'm at work. :/ I can complete it when I get home, but at least this gives you an idea.
router.on('router:route:activating').then(function (instance, instruction) {
// TODO: Inspect the instruction for the sign in route, then show the sign in
// dialog and cancel route navigation.
});
I have a basic PHP app, where the user login is stored in the HTTP Session. The app has one main template, say index.html, that switch sub-view using ngView, like this
<body ng-controller='MainCtrl'>
<div ng-view></div>
</body>
Now, this main template can be protected via basic PHP controls, but i have sub-templates (i.e. user list, add user, edit user, etc.) that are plain html files, included from angular according to my route settings.
While i am able to check for auth what concern the request of http services, one user is able to navigate to the sub-template url and access it. How can i prevent this from happen?
I would create a service like this:
app.factory('routeAuths', [ function() {
// any path that starts with /template1 will be restricted
var routeAuths = [{
path : '/template1.*',
access : 'restricted'
}];
return {
get : function(path) {
//you can expand the matching algorithm for wildcards etc.
var routeAuth;
for ( var i = 0; i < routeAuths.length; i += 1) {
routeAuth = routeAuths[i];
var routeAuthRegex = new RegExp(routeAuth.path);
if (routeAuthRegex.test(path)) {
if (routeAuth.access === 'restricted') {
return {
access : 'restricted',
path : path
};
}
}
}
// you can also make the default 'restricted' and check only for 'allowed'
return {
access : 'allowed',
path : path
};
}
};
} ]);
And in the main/root controller listen for $locationChangeStart events:
app.controller('AppController', ['$scope', '$route', '$routeParams', '$location', 'routeAuths',
function(scope, route, routeParams, location, routeAuths) {
scope.route = route;
scope.routeParams = routeParams;
scope.location = location;
scope.routeAuth = {
};
scope.$on('$locationChangeStart', function(event, newVal, oldVal) {
var routeAuth = routeAuths.get(location.path());
if (routeAuth.access === 'restricted') {
if (scope.routeAuth.allowed) {
event.preventDefault();
}
else {
//if the browser navigates with a direct url that is restricted
//redirect to a default
location.url('/main');
}
scope.routeAuth.restricted = routeAuth;
}
else {
scope.routeAuth.allowed = routeAuth;
scope.routeAuth.restricted = undefined;
}
});
}]);
Demo:
plunker
References:
angularjs services
location
UPDATE:
In order to fully prevent html template access then it's best done on the server as well. Since if you serve the html from a static folder on server a user can access the file directly ex: root_url/templates/template1.html thus circumventing the angular checker.
If you want to block them from going to that page create a service: http://docs.angularjs.org/guide/dev_guide.services.creating_services
This service can be dependency injected by all your controllers that you registered with the routeParams.
In the service you can would have a function that would check to see if the person is logged in or not and then re-route them (back to the login page perhaps?) using http://docs.angularjs.org/api/ng.$location#path. Call this function in each of the controllers like so:
function myController(myServiceChecker){
myServiceChecker.makeSureLoggedIn();
}
The makeSureLoggedIn function would check what current url they're at (using the $location.path) and if it's not one they're allowed to, redirect them back to a page that they are allowed to be.
I'd be interested to know if there's a way to prevent the routeParams from even firing, but at least this will let you do what you want.
Edit: Also see my answer here, you can prevent them from even going to the page:
AngularJS - Detecting, stalling, and cancelling route changes