ui-router fires transition to root state as soon as controller loaded - angular-ui-router

has anyone seen this sort of behaviour before ?
I have a button that is defined like this
<button ui-sref="designer({model: 'foobar' })">
I have a state defined as this
{
name: 'designer',
state: {
url: 'designer/:model/:form',
templateUrl: '/partials/designer.html',
controller: 'DesignerController',
controllerAs: 'designer'
}
}
and a controller like this
(function() {
angular.module('myApp')
.controller('DesignerController', DesignerController)
function DesignerController() {
console.log("-->loaded designer");
}
})();
when I click on the button, I get this in the console (I've added some debugging)
$stateChangeSuccess to designer- fired once the state transition is complete.
-->loaded designer
$viewContentLoaded - fired after dom rendered Object {name: "$viewContentLoaded", targetScope: ChildScope, defaultPrevented: false, currentScope: Object}
$stateChangeStart to undefined- fired when the transition begins. toState,toParams :
Object {url: "/", templateUrl: "/partials/main.html", name: "root", status: "exited", onExit: undefined} Object {}
$stateChangeSuccess to root- fired once the state transition is complete.
as you can see - as soon as the designer controller is loaded, a transition to the root state is fired - what can cause that ?

Related

fetched data disappears after page reload using vue state

Am fetching data from an api
this is the action in store
fetchVisits({commit}) {
axios.get('/api/visits')
.then(res => {
commit('FETCH_VISITS', res.data)
}).catch(err => {
console.log(err)
})
},
}
mutation
FETCH_VISITS(state, visits) {
return state.visits = visits
},
state
state: {
visits:[]
},
getter
getters: {
visits: state => {
return state.visits
}
},
dispatched action
created() {
this.$store.dispatch('fetchVisits')
},
But when i refresh the page the data to be displayed disappears
Actions are triggered with the store.dispatch method. Now in your case you might missed to dispatch the fetchVisit action or possible also, you dispatched that action in your other component which is not your main component. That's why when you hit reload the dispatch method was not invoke. To solve that .
You have to dispatch the fetchVisit action in your main component to invoke it every time you reload the page. Or, you dispatch again that action to the component itself which you said the state is empty. This is how you dispatch
this.store.dispatch('fetchVisits')

create dynamic events for a vue component

I have a problem with vue routes. I am using laravel 6 with vue#2.6.10
I want to create the actions button in the header dynamically (the actions are different which depends on the component). This AppHeader component is on every component and on the current component I want to create in the header the events for the current component.
For example the component CategoryDetails I want to have two actions in the header (save and exit).
The route foe the category is this:
path: '/',
redirect: 'dashboard',
component: DashboardLayout,
children: [
{
path: '/categories',
component: Category,
name: 'category',
meta: {
requiresAuth: true
}
},
{
path: '/categories/:CategoryID',
component: CategoryDetails,
name: 'category-details',
props: true,
meta: {
requiresAuth: true
}
},
]
In the component CategoryDetails:
<template>
<div>
<app-header :actions="actions"></app-header>
// other code
</div>
</template>
<script>
import AppHeader from "../../layout/AppHeader";
export default {
name: "CategoryDetails",
components: {AppHeader},
data() {
actions: [{label: 'Save', event: 'category.save'}, {label: 'Exit', event: 'category.exit'}],
},
mounted() {
const vm = this;
Event.$on('category.save', function(){
alert('Save Category!');
});
Event.$on('category.exit', function(){
vm.$router.push({name: 'category'});
});
}
}
</script>
I crated the action object which tells the header component what events to emit and listen to them in this component.
In the AppHeader component:
<template>
<div v-if="typeof(actions) !== 'undefined'" class="col-lg-6 col-sm-5 text-right">
{{ btn.label }}
</div>
</template>
<script>
export default {
name: "AppHeader",
props: [
'actions'
],
methods: {
onActionClick(event) {
Event.$emit(event);
}
}
}
</script>
The Event is the "bus event" defined in the app.js
/**
* Global Event Listener
*/
window.Event = new Vue();
So... let`s tested :)
I am in the category component. Click on the category details ... the actions are in the header (save and exit). Click on exit...we area pushed back to the category component... click again to go in the category details and click save ... the alert appears TWICE.
Exit and enter again ... the alert "Save Category!" appears 3 times.....and so on ...
Why ?
I think the issue is not with your routes. I don't know but try testing with your event locally (not globally) in the component of interest. There may be duplicate action (CategoryDetails).
According to this post: https://forum.vuejs.org/t/component-not-destroying/25008/10
I have to destroy the "zombie effect"
destroyed() {
Event.$off('category.save');
Event.$off('category.exit');
}

aframe state component - pass data from state to component

I am making use of aframe-state-component to build a game and tracking the game level and play/pause status through the state variables.
I am looking to understand how to pass state data to a component with properties.
Here is my state -
AFRAME.registerState({
initialState: {
score: 0
},
handlers: {
gamePaused: function(state) {
state.gamePaused = true;
},
gameStarted: function(state, event) {
state.gamePaused = false;
state.level = event.source;
}
}
});
and I pass the level to gameStarted handler by emitting an event in my angular controller -
el.emit('gameStarted', {source: levelnumber}, true);
and here is my html
<a-entity bind__model-subset="target: #orca; gamelevel: level"></a-entity>
the component "model-subset" originally accepted only "target" property. I included gamelevel property in there so that I can pass the state variable level to this component. Thereafter I modified, the model-subset component to include the gamelevel property in the schema as shown below -
AFRAME.registerComponent('model-subset', {
schema: {
target: { default: '', type: 'selector' },
gamelevel: { type: 'number'}
},
init: function() {
var data = this.data;
var el = this.el;
console.log("model-subset level is ", data.gamelevel); //this component does not get executed
}
})
But, it errors out and the component code does not get executed. Could you please give me an example of how to pass state data to different components that already have a few properties in their schema?
good question.
In a bind__ component, you only pass state properties, not any real values. Real values go in the component defininition itself. So move target: #orca out into model-subset since that's just a value.
<a-entity bind__model-subset="gamelevel: level" model-subset="target: #orca"></a-entity>

Vue 2 and child component events with multiple components

I've read this question / answer, and whilst that works when the parent contains a specific component, in my scenario the parent contains this:
<component :is="currentView"></component>
and the value of currentView determines which component is 'loaded' at any particular time. So, using v-on:event="handler" in the component tag here means all child components must use the same event name :(. Is it possible in the created() function of the parent to set up specific handlers which will be called regardless of which component is currently 'in view', and when they each might send different events? For example, I have a login component which sends a 'login' event, and a data view component which sends an 'update' event. In the parent, I want something like this:
this.$on('login', doLogin)
this.$on('update', doUpdate)
However, that's listening to events from itself, not its children. I also tried giving the component a ref:
<component ref="main" :is="currentView"></component>
and using this.$refs.main.$on('login', doLogin), but the $refs object isn't created until the initial render, so that can't go in the mounted() method (too early), and in the updated() method it will be repeatedly called which I'm sure isn't a good idea...
you could set a global event and then pass the name of the action as a part of the payload, i.e
const Login = {
template: `<h1>Login</h1>`,
mounted() {
this.$emit('global-event', {
action: 'login',
data: {
user: 'foo',
},
})
},
}
const Update = {
template: `<h1>Update</h1>`,
mounted() {
this.$emit('global-event', {
action: 'update',
data: {
user: 'foo bar',
},
})
},
}
new Vue({
el: '#app',
data: {
called: {
update: 0,
login: 0,
},
currentView: 'login',
},
components: {
Login,
Update,
},
methods: {
// within doSomething you would process the various events based on their payload.action
doSomething (payload) {
this.called[payload.action]++
},
toggleView () {
this.currentView = this.currentView === 'login' ? 'update' : 'login'
},
},
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<component #global-event="doSomething" :is="currentView"></component>
<button #click="toggleView">toggle view</button>
<pre>{{ called }}</pre>
</div>
Gah! After spending ages searching, finding nothing, then posting here, I of course immediately stumble on the answer.
But this only applies if you have control over the child components (I'm building all of them). In the child, you simply:
this.$parent.$emit('login', {some: 'data'})
And then set up the event listeners with this.$on('login', doLogin) in the parent as normal in the created() method.
I'm sure there will be a scenario where I'm using a third party component that simply calls this.$emit(), and at the moment, I can't see any alternative to v-on:eventName="handler" in the parent.

Angular ui-router with spa using sticky states not preserving scroll position on first back button

I'm creating an Angular mobile SPA app and part of the requirements have a substantial amount of list >> details pages. After they filter to populate the list, they can click on the item to go to a details page for it (with crud options if allowed, which rules out just using a modal). I'm saving the relevant data and things in a service, but this does not help maintain the scroll position and ui-router-extras sticky state seems like it would be ideal. I'm using requirejs as well, in case that's relevant. Also, I am using a Kendo ListView bound with a kendo.dataSource if that is relevant.
I've implemented it, but I'm not sure I've done it correctly. I scroll down and click on a list item and it goes to the details. When I click the back button (a directive that uses $window.history.back();, although I tried using $state.go and it did the same thing), it will go back and used the "cached" version, but will be at the top of the list. If I scroll down and click on another item and then go back, it will maintain the scroll position like I expect.
I turned on $stickyStateProvider.enableDebug(true); and the output looks identical from the first to the subsequent calls.
Here's my states config:
return app.config(function ($stateProvider, $stickyStateProvider, $urlRouterProvider) {
$stickyStateProvider.enableDebug(true);
$urlRouterProvider.otherwise(function ($injector, $location) {
var $state = $injector.get("$state");
$state.go("home");
});
$stateProvider.state('home', {
url: '/home',
templateUrl: 'app/views/home.html',
controller: 'homeController',
controllerAs: "vm"
})
.state('cmmenu', {
url: '/cmmenu',
templateUrl: 'app/views/cmmenu.html',
controller: 'cmmenuController',
controllerAs: "vm"
})
.state('areainquiry', {
//sticky: true,
abstract: true,
url: '/areainquiry',
templateUrl: 'app/views/areainquiry.html'
})
.state('areainquiry.list', {
url: '/areainquirylist',
views: {
'areainquirylist#areainquiry': {
templateUrl: 'app/views/areainquirylist.html',
controller: 'areainquirylistController',
controllerAs: "vm"
}
},
sticky: true,
deepStateRedirect: true
})
.state('areainquiry.details', {
url: '/areainquirydetails/:areaInquiryId',
views: {
'areainquirydetails#areainquiry': {
templateUrl: 'app/views/areainquirydetails.html',
controller: 'areainquirydetailsController',
controllerAs: "vm"
}
}
})
.state('login', {
url: '/login',
templateUrl: 'app/views/login.html',
controller: 'loginController',
controllerAs: "vm"
});
})
Here is the areainquiry.html that is the abstract parent view:
<div ui-view="areainquirylist" ng-show="$state.includes('areainquiry.list')"></div>
<div ui-view="areainquirydetails" ng-show="$state.includes('areainquiry.details')"></div>
Here is the code that is opening this screen. I do not have a named view in my index.html and all views up to areainquiry.html are using the root un-named view (I gave it a name "body" and changed all of them to use it, but it worked the same):
<div class="col-xs-6 col-md-4">
<a class="btn btn-default fullwidth" ui-sref="areainquiry.list">
<span class="{{ vm.areaInquiryButtonIcon }}"></span>
<br />
{{ vm.areaInquiryButtonText }}
</a>
</div>
I was trying to figure out a way to do it without the abstract state and just through a parent areainquiry and child areainquiry.detail, but couldn't get it working that way, so went this route.
Never mind. It is working. I'm not sure how I fixed it. I was working through another issue (had to do with a factory service), so I'm assuming that fixed it.

Resources