I cannot get my template to load from angular when I include a parameter.
The main page template has an unordered list like so which should direct to the appropriate "factorDetails" route. If I set up the main template like so using a path with no parameters
main.html
<ul>
<li ng-repeat="factor in factors">
<span class="result-list-name">
<a ng-href="/factorDetails">
{{factor.FactorName}}
</a>
</span>
</li>
</ul>
and my angular file like so
main.js
var app = angular.module('mainApp',['ngRoute'])
.config(
function($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
templateUrl: 'partials/main.html',
controller: 'totalListCtrl'
}).
when('/factorDetails', {
templateUrl: 'partials/details.html',
controller: 'detailsCtrl'
});
$locationProvider.html5Mode(true);
});
app.controller('totalListCtrl', function($scope, $http){
$http({method: 'get', url: '/api/allfactors'})
.success(function(data, status, headers, config){
$scope.factors = data;
$scope.factorCount = Object.keys(data).length;
})
.error(function(data, status, headers, config){
$scope.factors = {dummyfactor:{"factorname":"there was an error loading data"}};
});
});
app.controller('detailsCtrl', function($scope, $routeParams){
$scope.displayID = $routeParams;
});
I can see the test and and an empty displayID (because I haven't put any params in my URL) when my details.html template is loaded, so all is well here.
<div>
<h1>{{displayID}}</h1>
<h1>test</h1>
</div>
Output in browser is:
{}
test
However if I now make the following changes:
main.html
<ul>
<li ng-repeat="factor in factors">
<span class="result-list-name">
<a ng-href="/factorDetails/{{factor.FactorID}}">
{{factor.FactorName}}
</a>
</span>
</li>
</ul>
main.js
var app = angular.module('mainApp',['ngRoute'])
.config(
function($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
templateUrl: 'partials/main.html',
controller: 'totalListCtrl'
}).
when('/factorDetails/:FactorID', {
templateUrl: 'partials/details.html',
controller: 'detailsCtrl'
});
$locationProvider.html5Mode(true);
});
...etc.
Now my result is that the details.html template is not even loaded, the page stays on the main.html. But the URL changes to localhost/factorDetails/1 and factorDetails/2 in the browser address window depending on which link I'm clicking on.
Any help would be greatly appreciated, definitely feeling stuck here. Or feeling like loading up AngularUI in the hopes that will work.
Well I didn't figure out why $locationProvider wasn't working, but I did get this working with AngularUI. There is a good tutorial here:
http://www.ng-newsletter.com/posts/angular-ui-router.html
For those who might have had this issue, the relevant changes were the following
main.html
<ul >
<li ng-repeat="factor in factors">
<span class="result-list-name">
<a ui-sref="factorDetails({FactorID: {{factor.FactorID}}})">
{{factor.FactorName}}
</a>
</span>
</li>
</ul>
main.js
var app = angular.module('mainApp',['ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
//$urlRouterProvider.when('', '/');
$stateProvider
.state('main', {
url: '',
templateUrl: 'partials/main.html',
controller: 'totalListCtrl'
})
.state('factorDetails', {
url: 'factorDetails/:FactorID',
templateUrl: 'partials/details.html',
controller: 'detailsCtrl'
})
});
app.controller('totalListCtrl', function($scope, $http){
$http({method: 'get', url: '/api/factorsMainPage'})
.success(function(data, status, headers, config){
$scope.factors = data;
$scope.factorCount = Object.keys(data).length;
})
.error(function(data, status, headers, config){
$scope.factors = {dummyfactor:{"factorname":"there was an error loading data"}};
});
});
app.controller('detailsCtrl', function($scope, $stateParams, $http){
$http({method: 'get', url: ('/api/factorDetails/' + $stateParams.FactorID) })
.success(function(data, status, headers, config){
$scope.factor = data.factor1;
})
.error(function(data, status, headers, config){
$scope.factor = {dummyfactor:{"factorname":"there was an error loading data"}};
});
$scope.displayID = $stateParams.FactorID;
});
Related
Having the following snippet trying to fill an array of object with async alpine call but I can not get any result. Hier is what I try.
HTML:
<div x-init="experts.retrieveList" x-data="experts.list">
<ul>
<template x-for="item in experts.list" :key="item">
<li>
<div x-text="await item.address" ></div>
</li>
</template>
</ul>
</div>
external JS file
window.experts = {
apiUrl: 'http://bdb.local:8991/api/',
data: [],
list: [],
expertsForm: null,
retrieveList: () => {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`;
experts.apiCalls(membersUrl)
},
filterByParams: () => {
},
apiCalls: async (url) => {
let response = await fetch(url);
experts.list = await response.json()
return experts.list;
},
}
What is wrong in this case?
There are a few errors in this code:
You cannot use just a part of an Alpine.js component in the x-data="experts.list". If you don't define data variables directly in x-data, then it must be a real component that returns data and methods.
You cannot use an object as the key. It must be a string or number, like item.id or something like this.
The x-text="await item.address" seems incorrect. item.address should be a string, that has been already downloaded from the API.
In the component you need to use the this. prefix to access properties and methods.
Assuming your API returns the correct data format, something like this should work:
<div x-data="experts">
<ul>
<template x-for="item in list" :key="item.id">
<li>
<div x-text="item.address"></div>
</li>
</template>
</ul>
</div>
And the component in an external file:
const experts = {
apiUrl: 'http://bdb.local:8991/api/',
data: [],
list: [],
expertsForm: null,
init() {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`
this.apiCalls(membersUrl)
},
filterByParams() {
},
async apiCalls(url) {
let response = await fetch(url)
this.list = await response.json()
},
}
The init() method is executed automatically by Alpine.js.
In config/index.js i config like this
proxyTable: {
'/api': {
target: 'http://localhost:44322',
changeOrigin: true
}
},
And this code i call get method.
<template>
<div>
<ul v-if="posts && posts.length">
<li v-for="post of posts" v-bind:key="post.employeeId">
<p><strong>{{post.firstName}}</strong></p>
<p>{{post.lastName}}</p>
<p>{{post.phoneNumber}}</p>
<p>{{post.dateOfBirth}}</p>
<p>{{post.email}}</p>
</li>
</ul>
<ul v-if="errors && errors.length">
<li v-for="error of errors" v-bind:key="error.id">
{{error.message}}
</li>
</ul>
</div>
</template>
<script>
import Axios from 'axios'
export default {
name: 'Axios',
data () {
return {
posts: [],
errors: []
}
},
// Fetches posts when the component is created.
created () {
Axios.get('/api/employee')
.then(response => {
// JSON responses are automatically parsed.
this.posts = response.data
})
.catch(e => {
this.errors.push(e)
console.log(e)
})
}
}
</script>
I want when i call
http://localhost:8080/#/axios
the client call to backend :
http://localhost:44322/api/employee
But nothing happen, i see in header of request the url is :
localhost:8080
i do flow the link of vuejs: https://vuejs-templates.github.io/webpack/proxy.html ,part API Proxying During Development. Any idea for this?
Thanks!!
you would see the request url as http://localhost:8080/api/employee in the browser
and finally the request will be transferd to http://localhost: 44322/api/employee which you won't see in network panel of your browser
i have a '/posts' route. This 'posts' route has a created() function which fetches data from an API via GET and outputs the data to the page.
Now I have a navbar which is included on every page. This navbar now has an input field where I can search certain posts by tags. This tag based search function is already working and runs via POST to an api.
Now the problem:
I write some tags into input field in the navigation and search for them. If I'm currently not at the posts route, the search works fine and I get directed to the posts route and see the tag related posts.
If I write some tags in the navbar input field and press the search button, WHILE i'm already on the posts route, nothing happens.
So if I'm in any other route then '/posts', the tag based search works great.
Thats why I think, the problem is, that I'm already on the '/posts' route. But it should also work this way! So I need something like a route link that replaces/refresh the route content?
Here is my code:
Relevant part of my navbar component:
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<router-link to="/posts" class="nav-link">Posts</router-link>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<div v-for="(tag, index) in tags" class="ml-sm-2">
<h6><span class="badge badge-light" #click="removeSearchTags(index)">{{ tag }}</span></h6>
</div>
<input class="form-control ml-1 mr-sm-2" type="text" v-model="tag" v-on:keyup.enter="pushToTags"
placeholder="Search Gaming, Youtube, DrunkSlut" aria-label="Search">
<router-link :to="{name: 'posts', params: { searchTags: tags }}" reload>
<button type="button" v-if="this.tags.length > 0"
class="btn btn-outline-light my-2 my-sm-0">Search
</button>
</router-link>
</form>
Whole posts component logic:
<script>
export default {
name: "posts",
data: function () {
return {
apiUrl: '/getPosts',
posts: '',
submitted: false,
first_page_url: '',
last_page_url: '',
current_page_url: '',
next_page_url: '',
prev_page_url: '',
lastPage: '',
current_page: '',
tags: [],
}
},
methods: {
getPosts: function (url) {
this.$http.get(url).then(function (data) {
this.posts = data.body.data;
this.first_page_url = data.body.first_page_url;
this.last_page_url = data.body.last_page_url;
this.next_page_url = data.body.next_page_url;
this.current_page = data.body.current_page;
this.prev_page_url = data.body.prev_page_url;
this.lastPage = data.body.last_page;
this.current_page_url = '/getPosts?page=' + this.current_page;
});
},
getPostByTags: function (url, tags) {
this.$http.post(url, {
tags: tags
}).then(function (data) {
this.posts = data.body.data;
this.first_page_url = data.body.first_page_url;
this.last_page_url = data.body.last_page_url;
this.next_page_url = data.body.next_page_url;
this.current_page = data.body.current_page;
this.prev_page_url = data.body.prev_page_url;
this.lastPage = data.body.last_page;
this.current_page_url = '/getPostByTags?page=' + this.current_page;
});
},
},
computed: {},
created() {
if (!this.$route.params.searchTags) {
this.getPosts(this.apiUrl);
} else {
this.getPostByTags('/getPostByTags', this.$route.params.searchTags);
}
},
}
</script>
The Main html file, where vueJS starts. There is only the navbar component, thats how it's included on any other route.
<div id="app">
<navbar></navbar>
<router-view></router-view>
</div>
Try to make your created as a watch
Something like:
watch: {
'$route.params.searchTags': {
deep: true,
immediate: true, // this triggers the watch on component creation, so you can remove the created hook content
handler() {
if (!this.$route.params.searchTags) {
this.getPosts(this.apiUrl);
} else {
this.getPostByTags('/getPostByTags', this.$route.params.searchTags);
}
}
}
}
I have created a small validation using laravel and vue js but when I tried to display the errors using vue it won't show up inside a div. When I check using dev tools, the messages are there but didn't display to the specified div.
Sample blade:
<div class="alert alert-danger" v-if="errors">
<ul>
<li v-for="error in errors">#{{ errors[0]}}</li>
</ul>
</div>
Sample script:
var vm = new Vue({
el:'#app',
data: {
errors: false
},
mounted: function() {
$('.fileinput').fileinput();
},
methods: {
submitForm: function() {
vm.errors = null;
var form = document.forms.namedItem("addProjectForm");
var formdata = new FormData(form);
$.ajax({
url: $('#addProjectForm').attr('action'),
data: formdata,
contentType: false,
processData: false,
method: 'post',
error: function(data) {
if (data.status === 422) {
vm.errors = data.responseJSON;
}
},
success: function(data) {
swal("Success", "Project added successfully", "success")
}
});
}
}
})
I think here is problem :
You are overriding v-if="errors" property like this when submit data :
this.errors = null
This mean your errors are now null not true and not false right.
and because of you added v-if vue looking for errors to be true or false.
<div class="alert alert-danger" v-if="errors">
<ul>
<li v-for="error in errors">#{{ errors[0]}}</li>
</ul>
</div>
You can fix like this :
data: {
errors: false,
error_messages: null,
},
After submit data and check error do this :
if (data.status === 422) {
vm.error_messages = data.responseJSON;
}
Then :
<div class="alert alert-danger" v-if="errors">
<ul>
<li v-for="error in error_messages">#{{ error[0]}}</li>
</ul>
</div>
One more thing if you still want to check how it working then use v-show rather then v-if because v-if remove element and v-show will just hide and you can change behaviour by css to display:block and there will be your result. but you have to modify if you want to use with v-if;
Please find attached image which is gives you idea
i want to count review per product
Please find attache code
<li class="%%GLOBAL_AlternateClass%%">
<div class="inner">
<div class="ProductImage QuickView" data-product="%%GLOBAL_ProductId%%">
%%GLOBAL_ProductThumb%%
</div>
<div class="ProductDetails">
%%GLOBAL_ProductName%%
</div>
<em class="p-price">%%GLOBAL_ProductPrice%%</em>
<div class="ProductPriceRating">
<span class="Rating Rating%%GLOBAL_ProductRating%%">
<img src="%%GLOBAL_IMG_PATH%%/IcoRating%%GLOBAL_ProductRating%%.png" alt="" style="%%GLOBAL_HideProductRating%%"/>
</span>
</div>
<div>
<div class="ProductCompareButton" style="display:%%GLOBAL_HideCompareItems%%">
<input type="checkbox" class="CheckBox" name="compare_product_ids" id="compare_%%GLOBAL_ProductId%%" value="%%GLOBAL_ProductId%%" onclick="product_comparison_box_changed(this.checked)"/> <label for="compare_%%GLOBAL_ProductId%%">%%LNG_Compare%%</label> <br>
</div>
<div class="ProductActionAdd" style="display:%%GLOBAL_HideActionAdd%%;">
%%GLOBAL_ProductAddText%%
</div>
</div>
</li>
This jQuery snippet can be inserted on the category page to asynchronously access each product, and determine the number of reviews on the page.
// For each product on the category page...
$('.ProductDetails').each(function(index) {
var $self = $(this); // Make local reference to 'this'.
// Parse the URL from the individual product, and retrieve its Reviews Count:
getProductPageReviewCount($self.find('>:first-child').attr('href')).then(function(count) {
// Insert the number of reviews below the Product Name:
$self.find('>:first-child').after("<a>" +count +" Reviews</a>");
}).catch(function(err) {
// Catch any Ajax Errors:
console.log('Error - ', err);
});
});
/**
* Determines the total number of reviews for a particular
* external product page, according to its URL.
* #param url <string> - The product page's URL.
* #return Promise\<int> - The number of reviews on the page, or error on failed request.
*/
function getProductPageReviewCount(url) {
return new Promise(function(fulfill, reject) {
$.ajax({
method: 'GET',
url: url,
success: function(res) {
fulfill($(res).find('.ProductReviewList > li').length);
},
error: function(err) {
reject(err);
}
});
});
}
$(document).ready(function(){
$('.ProductList').each(function(){
$(this).find('li').each(function(){
var current = $(this);
var mainURL = window.location.href;
var productUrl = current.find('div div a').attr('href');
if(mainURL.indexOf('https')!==-1){
productUrl = current.find('div div a').attr('href').replace('http','https');
}
$.ajax({
url: productUrl,
type: "POST",
dataType: "html",
success: function (data) {
var ht = $(data);
var review = ht.find('#productTotalReview').text();
current.find('div span').append("<br> "+review);//.replace('product reviews','Reviews'));
}
});
});
});
});