Use a variable set to ng-click to change template - javascript

Let's say I have a list of links and I want each of them to change the DOM on the current page
using ng-click and templating, how would I do that?
Edit: I guess what I am trying to understand is how to move as much of the logic away from the .html file and into my app.js file. I'm a little new to JS and Angular and don't know where or how to pass "active" to choose what I'd like to place inside
For example:
<ul>
<li>
<h1>foo1</h1>
</li>
<li>
<h1>foo2</h1>
</li>
<li>
<h1>foo3</h1>
</li>
</ul>
<active></active>
Where the active element displays only what template is set to the active one.
ie; the template associated with foo1 is displayed, and then if foo2 is clicked the template for foo2 replaces foo1

I am not sure what is the implementation of <active>, but you can use ng-include to implement what you are expecting
<active ng-include='active'></active>
Now create a client side or server side template with the name which matches variable active defined on scope and corresponding template would get loaded
A template definition could be like
<script type="text/ng-template" id="foo1">
<!-- html template for foo here-->
</script>
See ng-include documentation.

You could use ng-show/ng-hide. Basically something like this.
<div ng-show="foo1">
<p> This will only show when foo1 is true on your scope.
</div>
<div ng-show="foo2">
<p> This will only show when foo2 is true on your scope.
</div>
<div ng-show="foo3">
<p> This will only show when foo3 is true on your scope.
</div>
Also if you're wanting to keep the logic of the active = 'foo1' just change up the logic in the ng-show. Usually you want to keep logic out of the view though, so move that to your controller.

Related

How to hide custom Angular 12 selector in browser view

I'm learning Angular 12 and I have some issues about the framework operation.
I've created a new project, added Bootstrap 5 and created some components.
When I nest a component inside another like this :
<div class="container">
<div class="row">
<div class="col-xs-12">
<h2>Mes appareils</h2>
<ul class="list-group">
<app-appareil [appareilName]="appareilOne"></app-appareil>
<app-appareil [appareilName]="appareilTwo"></app-appareil>
<app-appareil [appareilName]="appareilThree"></app-appareil>
</ul>
</div>
</div>
</div>
I don't understand why I still see the custom selectors in the browser inspector view :
Angular browser view
It breaks several things in my Boostrap style.
Did you know if it's possible to hide/remove these custom components of my browser view to get in this case only the <li> tags directly inside the <ul> instead of these <app-appareil> ?
Thanks :)
Change
#Component({
selector: "app-appareil"
})
to
#Component({
selector: "li[appAppareil]"
})
and then use it as
<ul class="list-group">
<li appAppareil [appareilName]="appareilOne"></li>
</ul>
By using an attribute selector we can avoid the wrapping component tag (which you cannot "remove"), and we preserve semantics of the DOM itself.
Likely to get better semantics you'd want to make further changes and use content projection, but that's unclear from the limited information and beyond the scope of the question anyway.
To make it the "Angular way", the approach needs to be changed.
Supposing that you have a collection of device names (appareilNames) returned from your component:
public get deviceNames(): Array<string> { ... }
The appropriate tags structure can be achieved as follows:
<ul class="list-group">
<li *ngFor="let deviceName of deviceNames"> <!-- iterate on each device name -->
<app-appareil [appareilName]="deviceName"></app-appareil> <!-- use each name to create a component with it -->
</li>
</ul>

Open up content in another div with AngularJS

I have some content I wish to load into a div using Angular. For example:
<div class="number-and-description" ng-controller="thingDetails">
<div class="number" ng-click="loadContent(thing.number, thing.description, thing.status)" ng-cloak>{{ thing.number }}</div>
<p class="description" ng-click="loadContent(thing.number, thing.description, thing.status)" ng-cloak>{{ thing.description }}</p>
</div>
I have another view outside of this div like this:
<!-- Main Content Area -->
<section class="main-content group" ng-controller="thingDetails">
<h1 ng-cloak>{{ thingNumber }}</h1>
<div ng-cloak>{{ thingDescription }}</div>
<div ng-cloak>{{ thingStatus }}</div>
</section>
In my app.js file, I have this code:
thingApp.controller('thingDetails', ["$scope", function($scope) {
$scope.loadContent = function(number, description, status) {
$scope.thingNumber = number;
$scope.thingDescription = description;
$scope.thingStatus = status;
return $scope;
};
}]);
My question is, why doesn't the main content area div update when these values are changed? Right now it remains blank. thing.number, thing.description loads fine in the div I am clicking on - this data is coming from hardcoded JSON which appears fine. The issue is that when I click on the div, the OTHER main content div doesn't show/update the data. Could someone please help me out? Note that when I console.log(number) or console.log(description) etc. I can see the correct values, so they are being passed correctly to the loadContent() function.
You have two separate instances of controller thingDetails , not one. They each have their own scope and do not share anything directly. A simple way to see this is put a console.log() in your controller and you will see it run each time the controller initializes
For controllers to share data you use a service and inject that service wherever it needs to be accessed.
Alternatively perhaps you don't need two instances and can wrap them in one scope in your view
You are basically creating two controllers here with two different scopes. Thus, in the outer div, $scope.thingNumber, description, etc. are undefined as you are only changing the values on the inner scope.
A quick but messy fix to this problem would be to change your $scope's to $rootScopes. The proper way to do this though is via services/factories.

Trigger function on a certain element - ngrepeat - angularjs

Good morning,
I'm trying to change the limitTo filter on a certain list, my issue is:
when I click to the trigger who change the filter limit the filter changes on all ng-repeated categories.
my function inside the main controller
$scope.showMore = function(limit) {
if($scope.limitItems === $scope.itemsPerList) {
$scope.limitItems = limit;
$scope.switchFilterText = 'less';
} else {
$scope.switchFilterText = 'more';
$scope.limitItems = $scope.itemsPerList;
}
}
my scenario (I rewrote it in a simplified version)
<li ng-repeat="item in category.items | limitTo: limitItems ">
{{item.title}}
</li>
<li ng-if="limitItems < (category.items.length)">
<a ng-click="showMore(category.items.length)" >Show {{ switchFilterText }}</a>
</li>
Could you explain me what's wrong with me?
I searched how to select a single element to apply the function but I didn't find anything useful
Update:
I found the way to solve my issue in this way:
No functions inside the controller are involved to make this functionality works properly:
<li ng-repeat="category in maincategories" ng-init="limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: limitItems "> {{item.title}}
</li>
</ul>
<a ng-click="limitItems = category.items.length" href>
<b ng-if="category.items.length > maxItemsPerList && limitItems != category.items.length "> Show more </b>
</a>
I'm not really convinced about Angular (I used it in my past and I was impressed by the performance but now I can see logics senseless):
What I learned:
ng-if and ng-click cannot be used in the same content because ng-if creates new scopes so if you put ng-if on top of the "show more" link it will break the code
ng-init cannot be used in the same element of the ng-repeat otherwise the var initialised will not be available inside the ng-repeat block
I think there is another way to do that, maybe more clean but in this specific case I can't do a lot.
ng-if and ng-click cannot be used in the same content because ng-if
creates new scopes so if you put ng-if on top of the "show more" link
it will break the code
Yes, ng-if creates a new scope, but it is possible to mix ng-if and ng-click (and most other directives). To do that, you'll be safer if you always write to atributes of another object instead of a simple variable. It is plain JavaScript prototypal inheritance in play.
<li ... ng-init="category.limitItems = maxItemsPerList">
ng-init cannot be used in the same element of the ng-repeat otherwise
the var initialised will not be available inside the ng-repeat block
True, in the sense that variables are created in the local scope. But again, refer to an object.
I think there is another way to do that, maybe more clean but in this
specific case I can't do a lot.
You don't need to do a lot, it is quite simple to do it right actually.
Some advices:
Use ng-init with care. I know it will tempt us but always try to put logic inside controllers and services;
Avoid assignments inside templates;
Learn how to use controllerAs syntax. It gives you an object to write your models to (the controller), so solves most problems related to scope inheritance;
Do not inject $scope, put your view models inside controllers.
Full code goes like this:
<li ng-repeat="category in maincategories" ng-init="category.limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: category.limitItems "> {{item.title}}
</li>
</ul>
<a ng-if="category.items.length > maxItemsPerList && category.limitItems != category.items.length" ng-click="category.limitItems = category.items.length" href>
<b> Show more </b>
</a>

Data binding (AngularJS) inside JS

I'm trying to use data binding inside a <script>.
<ol class="breadcrumb"></ol>
<div class=heading><h2>{{current.name}}</h2>
...
<script>
$(function(){
$(".breadcrumb").append('<li> Ledige stillinger</li>');
$(".breadcrumb").append('<li><a href=#!/categories/{{current.keyname}}>{{current.name}}</a></li>');
});
</script>
</div>
My result is:
Ledige stillinger (this is ok!) / {{current.name}} (here should it be the name, it works inside h2)
What am I doing wrong?
my answer would be to use AngularJS as it is intended and 1-way bind your DOM to the model values.
if there are multiple elements with the "breadcrumb" class then add these to a collection in your controller's scope and use ng-repeat directive to render them all out then append the "li" tags to this template.
Please see this plunkr example: http://plnkr.co/edit/0T6ldRzSTCnVIKnn3WRh
look at app.js and the following markup:
<ul class="breadcrumb">
<li>{{current.name}}</li>
</ul>

knockout: accessing the View Model through an iframe?

The app I'm building requires the use of iframes for wysiwyg text editing, and these iframes need to be wired up to the viewModel, but I'm finding that this clashes with knockout... or at least knockout seems not to apply bindings when I try to access them through the parent object.
Here's some code...
<script type="text/javascript">
$(function(){
ko.applyBindings(parent.model.project, $('#root')[0]);
});
</script>
<ul id="root" data-bind="template: {name: function(){return type()},
foreach: populate() }"></ul>
<script id="document" type="text/html">
<li class="draft" draft="${draft()}" data-bind="css: {expanded: $data.expanded}">
<span data-bind="click: function(){parent.model.project.expand($data, 'draft')}">
${ordinal(draft())} Draft
<img src="icons/close-black.png"
data-bind="click: function(){parent.model.project.deleteDraft($data)},
css:{ only: function() {parent.model.project.drafts > 1} }"/>
</span>
<div>
<ul data-bind="css: {expanded: $data.expanded},
template: {
name: 'draft',
foreach: $data.draftItems,
}"
>
</ul>
</div>
</li>
</script>
<script id="draft" type="text/html">
{{if $data.name}}
<li class="${name}">${name}</li>
{{/if}}
</script>
OK, this isn't a wysiwyg text editor, but it still illustrates my point.
Now the thing is, when I wrote this it worked perfectly. I had the part of viewModel that all the bindings refer to defined in a js file accessed only by this html... but I need the same ViewModel to be accessed by the parent window, as I would with a wysiwyg editor for toolbar buttons and other external controls, so I moved that part of the viewModel to the file where the rest of it was defined... and now it doesn't work!
In the external file that I had previously I was still accessing the parent view model using parent.model, but now not having direct exclusive access to that model it doesn't seem to work. The thing is though that I can access the view model with console.log, I can document.write from it too, it fires off events back to the viewModel, and my view updates initially, but after that initial one it no longer updates.
Is there a way to solve this?
iframes won't inherit bindings from parent elements.
You can't make it work that way, as iframes really are separate pages within another page.
Each iframe will need to have its own view model. If that viewmodel needs to come from another view model, you'll need to share that data via global JS objects or message passing or some other mechanism.

Categories

Resources