Fallback (default) image using Angular JS ng-src - image

I'm trying to set an image source using data returned from a modal. This is inside an ng-repeat loop:
<div id="divNetworkGrid">
<div id="{{network.id}}" ng-repeat="network in networks">
<span>
<table>
<tr>
<td class="imgContainer">
<img ng-src="{{ ('assets/img/networkicons/'+ network.actual + '.png') ||
'assets/img/networkicons/default.png' }}"/>
</td>
</tr>
</table>
</span>
</div>
</div>
As is apparent, I want to populate default.png when the network.actual model property is returned as null. But my code is not picking up the default image, though the first image is coming up fine when available.
I'm sure this is some syntax issue, but cant figure out what's wrong.

I have just found out, that you can use both ng-src and src on an img tag and if ng-src fails (variable used does not exists etc.), it will automatically fallback to src attribute.

What I imagine is happening is the src is returning a 404 even if network.actual is null. For example, first value:
('assets/img/networkicons/'+network.actual+'.png')
is evaluating to a 404 src URL, something like ('assets/img/networkicons/null.png'). So in the OR selection, it's truth-y, but the resource is 404. In that case, you can set the fallback via onError with something like:
<img ng-src="{{('assets/img/networkicons/'+network.actual+'.png')}}" onerror="this.src='assets/img/networkicons/default.png'" />
CodePen to demo use cases - http://codepen.io/jpost-vlocity/pen/zqqerL

angular.module('fallback',[]).directive('fallbackSrc', function () {
return{
link: function postLink(scope, element, attrs) {
element.bind('error', function () {
angular.element(this).attr("src", attrs.fallbackSrc);
});
}
}
});
<img ng-src="{{url}}" fallback-src="default-image.jpg"/>
angular.module('fallback', []).directive('actualSrc', function () {
return{
link: function postLink(scope, element, attrs) {
attrs.$observe('actualSrc', function(newVal, oldVal){
if(newVal != undefined){
var img = new Image();
img.src = attrs.actualSrc;
angular.element(img).bind('load', function () {
element.attr("src", attrs.actualSrc);
});
}
});
}
}
});
<img actual-src="{{url}}" ng-src="default.jpg"/>
link

Interesting way to use ng-src. I haven't tested how that expression would be handled but I would suggest doing it a more stable way:
<img ng-src="{{ networkIcon }}" />
And in your controller:
$scope.networkIcon = network.actual ? 'assets/img/networkicons/' + network.actual + '.png' : 'assets/img/networkicons/default.png';
Edit: Just to clarify, I'm not suggesting the best way to do this is to bind the image source directly to the scope but rather to move the image fallback logic outside of the view. In my applications I often do it in the services that retrieve the data or on the server itself and send an image URL so the client only has to worry about a single property.
Edit to address repeater:
angular.forEach($scope.networks, function(network) {
network.icon = network.actual ? 'assets/img/networkicons/' + network.actual + '.png' : 'assets/img/networkicons/default.png';
});
<tr ng-repeat="network in networks">
<td><img ng-src="{{ network.icon }}" /></td>
</tr>

<div id="divNetworkGrid">
<div id="{{network.id}}" ng-repeat="network in networks">
<span>
<table>
<tr>
<td class="imgContainer">
<img ng-src="{{'assets/img/networkicons/'+(network.actual || 'default' )+'.png'}}"/>
</td>
</tr>
</table>
</span>
</div>
</div>
The inline or will work if the the queried variable is undefined. The above will fix your issue

You can supply the alternative image after OR operator like this:
{{ book.images.url || 'Images/default.gif' }}

Related

Vue Component not works even i register it follow the official manual

just a very new to Vue 2.0, i actually use if for Laravel 5.4, now you can see from below link that i created one component which name is "canvas-chart", what actually i want show is a filterable table, and then to get more Json data from next steps, but now it always show me "Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option." , can not understand that i follow the official documentation to use it why it's not work?
new Vue({
el:'#filterTest',
data:{
keywords:[{"id":"9","name":"missy","phone":"21324234532"},
{"id":"3","name":"Mahama","phone":"345604542"},
{"id":"2","name":"Bernard","phone":"241242542"}]
},
computed: {
filterM: function () {
var self = this
return self.filter(function (word) {
return user.name.indexOf(self.searchQuery) !== -1
})
}
}
});
Vue.component('canvas-chat',{
template:'#canvasChart',
props:['keywordsData']
})
<script type="text/x-template" id="canvasChart">
<table>
<thead>
<tr>
<th v-for="key.id in keywordsData">
<span class="arrow" ></span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="key.name in keywordsData">
<td v-for="key.phone in keywordsData"> {{key.name}}</td>
</tr>
</tbody>
</table>
</script>
<div id="filterTest">
<canvas-chat keywordsData="keywords" ></canvas-chat>
</div>
You have a few issues:
You must register the canvas-chat component first before you use it in the main component (put Vue.component('canvas-chat', ... before new Vue(...)).
Your v-for loops are wrong. They should be like v-for="key in keywordsData", not v-for="key.id in keywordsData".
The v-fors on the tr and td elements don't make sense; you will have to process the keywordsData first (perhaps in a computed property) to get it into the correct format you want, then loop over that.
You must bind the keywordsData prop in the template like this: :keywords-data="keywords".

Write VueJS data binding expression in href of <a> tag

I'm using VueJS 2.0 with Laravel 5.3. I have Vue array data (e.g, items). Now I want to render those data in table using v-for. In table, there is a button to redirect page to item details page. For that, I'm writing my static URL till / in href of tag. Then I'm appending {{item.id}}. But I'm getting this error.
Interpolation inside attributes has been removed. Use v-bind or the
colon shorthand instead. For example, instead of ,
use <div :id="val">
So I can't get that how to write data binding expression of VueJS in <a href="">. If anyone knows the solution, it will be appreciated.
Here is my code.
my-items.blade.php
<div id="app">
....
<tr v-for="item in items">
<td>
view
</td>
</tr>
....
</div>
my-items.js
new Vue({
el: '#app',
data: {
items: [
{id: 1},
{id: 2},
{id: 3}
]
},
})
EDIT
Here is what I was doing in AngularJS with Laravel 5.2. And I want similar like this.
view
It should be like following:
<tr v-for="item in items">
<td>
<a :href="'{{url('/item')}}/' + item.id" />
</td>
</tr>
If you wan't to maintain fixed URL rather than relative URL, you may consider this one:
<div id="app">
<tr v-for="item in items">
<td>
<a :href="'{{ url('/item') }}' + '/' + #{{ item.id }}" />
</td>
</tr>
</div>

Angular 2 doesn't update my view after I add or edit item

I've finally made my app in angular 2. Everything is solved, except one thing. When I add item into my table or edited it, I can't see the change until I refresh page or click for example next page button (I have implemented pagination). I included:
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
in this order. My method for adding item is very simple:
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(departments => this.department = departments.json());
this.getAll();}
Whhen I add item, and put breakpoint on get method, It is called correctly and I get right information from my DB, but I don't know why view isn't refreshed then. Do you have any idea why is it happened? Thanks for suggestions!
EDIT: department is just department: Department, where Department is interface with properties (departmentNo, departmentName, departmentLocation). The view for adding item looks like:
<form [ngFormModel]="myForm"
(ngSubmit)="addDepartment(newItem); showAddView=false" [hidden]="!showAddView" align="center">
<div>
<label for="editAbrv">Department name:</label><br>
<input type="text" [(ngModel)]="newItem.departmentName" [ngFormControl]="myForm.controls['departmentName']" >
<div *ngIf="myForm.controls['departmentName'].hasError('required')" class="ui error message"><b style="color:red;">Name is required</b></div>
</div>
<br/>
<div>
<label for="editAbrv">Department Location:</label><br>
<input type="text" [(ngModel)]="newItem.departmentLocation" [ngFormControl]="myForm.controls['departmentLocation']" >
<div *ngIf="myForm.controls['departmentLocation'].hasError('required')" class="ui error message"><b style="color:red;">Location is required</b></div>
</div>
<br/>
<div>
<button type="submit" [disabled]="!myForm.valid" class="ui button">Add item</button>
<button><a href="javascript:void(0);" (click)="showHide($event)" >
Cancel
</a></button>
</div>
</form>
and my department table is:
<table align="center">
<thead>
<tr>
<td>#</td>
<td><strong>Department</strong></td>
<td><strong>Department Location</strong></td>
<td><strong>Edit</strong></td>
<td><strong>Delete</strong></td>
</tr>
</thead>
<tbody>
<tr *ngFor="#department of departments | searchString:filter.value ; #i = index">
<td>{{i + 1}}.</td>
<td> {{department.departmentName}}</td>
<td>{{department.departmentLocation}}</td>
<td>
<button class="btnEdit" (click)="showEdit(department)">Edit</button>
</td>
<td>
<button class="btnDelete" (click)="deleteDepartment(department)" >Delete</button>
</td>
</tr>
</tbody>
</table>
With this code, you don't wait for the response of the addDepartment request and execute the getAll request directly.
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(departments => this.department = departments.json());
this.getAll();
}
You should move the call to getAll within the callback registered in subscribe. At this moment, the addDepartment is actually done and you can reload the list...
The code could be refactored like this (it's a guess since I haven't the content of addDepartment and getAll methods):
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(addedDepartment => {
this.department = this.getAll();
});
}
This issue occurs because of the way you're using departmant and how change detection works. When you use *ngFor="#department of departments", angular change detection looks for a object reference on departments array. When you update/change one of the items in this array object reference to the array itself doesn't change, so angular doesn't run change detection.
You have few options:
1) change reference of the array by replacing it with new array with updated values
2) tell angular explicitly to run change detection (which I think is easier in your case):
constructor(private _cdRef: ChangeDetectorRef) {...}
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(departments => this.department = departments.json());
this.getAll();
this._cdRef.markForCheck();
}

Hiding DIV in TPL file based on if an image exists?

<div><div style="margin-left:67px"><table style="border:1px #80A0BB solid;" padding="5px"><tr><td><img src="{$smarty.const.DOC_ROOT}/images/thumbs/{$link.ID}-300x225.png" alt="" /></td></tr></table></div></div>
I'm trying to hide a div based on if an image exists on my server. How would I check to see if an image exists and hide the div if it doesn't exist? Or is there a better way to do this?
The easiest way is just to use write a function in PHP and then use it in Smarty.
In PHP:
function linkImageExists($link){
//Check to see if image for link exists and return true if it does.
// otherwise:
return false;
}
In Smarty template:
{if linkImageExists($link)}
<div>
<div style="margin-left:67px">
<table style="border:1px #80A0BB solid;" padding="5px">
<tr>
<td>
<a href="{$link.URL|trim}" target="_blank">
<img src="{$smarty.const.DOC_ROOT}/images/thumbs/{$link.ID}-300x225.png" alt="" />
</a>
</td>
</tr>
</table>
</div>
</div>
{else}
{* image doesn't exist *}
{/if}
You may want to consider turning $link into an object and then you can call functions on it, rather than having to use global functions which may producer cleaner code in the future.

_Layout styles not applied to Partial View rendered in a popup

I am loading a partial view in the popup using following code:
<div id="Mydiv" title="Modify" class="ModifyRule" style="overflow: hidden" />
<script type="text/javascript">
$(document).ready(function () {
//define config object
var dialogOpts = {
title: "Modify Rule",
modal: true,
autoOpen: false,
height: 300,
width: 700,
open: function () {
//display correct dialog content
$("#Mydiv").load("Modify", { SelectedRow: $('#MyParam').val() });
}
};
$("#Mydiv").dialog(dialogOpts); //end dialog
$('#Modify').click(
function () {
$("#Mydiv").dialog("open");
return false;
}
);
});
</script>
Here is the code from partial view:
#Code
Using (Html.BeginForm())
#<div id="Master">
<table>
<tr>
<td>
#Html.LabelFor(Function(model) model.InputAuthorityGridDetail.TcmAccount)
</td>
<td>#Html.EditorFor(Function(model) model.InputAuthorityGridDetail.TcmAccount)
</td>
<td>
#Html.LabelFor(Function(model) model.InputAuthorityGridDetail.Amount)
</td>
<td>#Html.EditorFor(Function(model) model.InputAuthorityGridDetail.Amount)
</td>
</tr>
<tr align="right">
<td>
<input name="button" type="submit" value="Save" class="btn" />
</td>
</tr>
</table>
</div>
End Using
End Code
the controller method modify returns a partial view named _Modify, the view is rendered correctly in the popup but I notice that the CSS styles are not applied to the controls in the partial view can someone help me?
When a partial is loaded into a layout page, the layout page contain the reference to the css that is used by the partial view.
However, from what I gather here (my javascript is not that great), you are loading the partial directly into a popup display and not using the layout page? If this is correct then your partial will not know about the css. And to correct this you would need to add a reference to the css at the top of the partial.
My partial views dont start with #Code and dont end with End Code. Try removing them and see if it works then.

Resources