How to send json object as parameter between states using ui-sref? - angular-ui-router

I have to send list of ids between ui-states, which will not be visible in url.
Following is the link for navigate to another state:
<button class="button button-primary" ui-sref="home.product-campaigns.update-multiple-product-ads({adList:productCampaignDetailCtrl.adList})"> UPDATE</button>
And here is the defined state which will receive list of ids
$stateProvider.state('home.product-campaigns.update-multiple-product-ads', {
url: '/update-multiple-product-ads',
params: {
adList: null,
},
templateUrl: 'update-multiple-product-ads/update-multiple-product-ads.tpl',
controller: 'UpdateMultipleProductAdsCtrl'
});

Related

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');
}

broadcast to others and dont broadcast to current user are not working

In my TaskList.vue I have the code below:
<template>
<div>
<ul>
<li v-for="task in tasks" v-text="task"></li>
</ul>
<input type="text" v-model="newTask" #blur="addTask">
</div>
</template>
<script>
export default {
data(){
return{
tasks: [],
newTask: ''
}
},
created(){
axios.get('tasks')
.then(
response => (this.tasks = response.data)
);
Echo.channel('task').listen('TaskCreated', (data) => {
this.tasks.push(data.task.body);
});
},
methods:{
addTask(){
axios.post('tasks', { body: this.newTask })
this.tasks.push(this.newTask);
this.newTask = '';
}
}
}
</script>
When I hit the axios.post('tasks') end point, I got duplicate result in my current tab that i input the value and the another tab got only 1 value which is correct.
To avoid this, I tried to use
broadcast(new TaskCreated($task))->toOthers();
OR
I put $this->dontBroadcastToCurrentUser() in the construct of my TaskCreated.php
However, both methods are not working. Any idea why?
The image below is from network tab of mine. One of it is pending, is that what caused the problem?
https://ibb.co/jpnsnx (sorry I couldn't post image as it needs more reputation)
I solved this issue on my Laravel Spark project by manually sending the X-Socket-Id with the axios post.
The documentation says the header is added automatically if you're using vue and axios, but for me (because of spark?) it was not.
Below is an example to show how I manually added the X-Socket-Id header to an axios post using the active socketId from Laravel Echo:
axios({
method: 'post',
url: '/api/post/' + this.post.id + '/comment',
data: {
body: this.body
},
headers: {
"X-Socket-Id": Echo.socketId(),
}
})
Laravel looks for the header X-Socket-ID when using $this->dontBroadcastToCurrentUser(); Through this ID, Laravel identifies which user (current user) to exclude from the event.
You can add an interceptor for requests in which you can add the id of your socket instance to the headers of each of your requests:
/**
* Register an Axios HTTP interceptor to add the X-Socket-ID header.
*/
Axios.interceptors.request.use((config) => {
config.headers['X-Socket-ID'] = window.Echo.socketId() // Echo instance
// the function socketId () returns the id of the socket connection
return config
})
window.axios.defaults.headers.common['X-Socket-Id'] = window.Echo.socketId();
this one work for me..!!

Like/Up Vote Button With Vue.Js, axios And Laravel

I am trying to create a like button for a recipe page on my site using Vue.
I am a begginer with vue and combining it with laravel, so i cant find the problem here.
Few things to note about what i am doing.
I dont want the option to like/ up vote a recipe be open just to registered users.
I know that after a page refresh(using the code i have now), that same person can click it again. My logic is that i dont think a person would do such a thing, esspecialy not the crowd my site refers to, so it fine by me.
well, here is the code i have done so far.
<like :recipe="{{$recipe}}" inline-template>
<div>
<button class="button is-medium" #click="iLikeIt":disabled="disabled" >Like</button>
<button class="button is-medium fa fa-thumbs-up" v-text="like"></button>
</div>
</like>
Here is the script:
<script>
export default{
props:['recipe'],
data(){
return{
like:0,
disabled:false
}
},
methods:{
iLikeIt(){
this.like++;
this.disabled=true;
axios.post('/likes/'+this.recipe.id, {
likes: this.like,
id:this.recipe.id
})
.then(function (response) {
console.log(response);
});
}
}
}
</script>
Here are the routes:
Route::post('/likes/{id}','RecipesController#likes');
And this is the likes function:
public function likes(Request $request,$id){
if (request()->expectsJson()) {
$recipe=Recipe::find($id);
$recipe->likes=$request->likes;
return $recipe;
}
}
Edit:
I of course must add, as per the comments:
The increments works fone, and the disabeling of the button also works.
What does not work is the persistend and incrementing on the DB table.
The response i get in the console is this:
`{data: " status: 200, statusText: "OK", headers: {…},
ƒ xhrAdapter(config)
data
:
"{"likes":1,"id":117}"
method
:
"post"
"/likes/117"
request
:
XMLHttpRequest
{readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload}
status
:
200
statusText
:
"OK"
__proto__
:
Object`
That's about it. Thanks for the help.
The error is in your Laravel model code
public function likes($id){
if (request()->expectsJson()) {
$recipe = Recipe::find($id);
$recipe->likes++;
$recipe->save();
return $recipe;
}
}
You forgot to call save() to ensure the updated value gets pushed to the database. I also changed it from receiving a value to incrementing on call as that prevents collision and overwrite issues.
I have solved the problem.
I have a middleware('auth') preventing the request and response the proper data.
Thanks all. :)

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