1

I am facing an issue while including my child component in my parent component in Angular 8. My child component contains only a dropdown. Now when I am including the same in the parent component and the CSS used in the div of parent component then the display output is coming as expected.

Parent Component:

<div class="col-lg-12">
  <div class="form-group row">
    <label class="col-sm-1 col-form-label-lg font-weight-bold">Country</label>
    <div class="col-sm-5">
      <app-country >
      </app-country>
    </div>
  </div>
</div>

Child Component

<select [(ngModel)]='countryDetails.default' class="form-control form-control-lg">
  <option *ngFor="let province of countryDetails.details" value={{country.id}}>
    {{country.value}}
  </option>
</select>

But now to reduce the coding planned to move the div class="col-sm-5" in the child component.

New Coding which is not working:

Parent Component:

 <div class="col-lg-12">
      <div class="form-group row">
        <label class="col-sm-1 col-form-label-lg font-weight-bold">Country</label>
        <app-country></app-country>
      </div>
    </div>

Child Component:

<div class="col-sm-5">
  <select [(ngModel)]='countryDetails.default' class="form-control form-control-lg">
    <option *ngFor="let province of countryDetails.details" value={{country.id}}>
      {{country.value}}
    </option>
  </select>
</div>

In the second part if I extend the output HTML though the Web Console then found that the CSS is included in the code but then the behavior is different.

Also, note that I am using bootstrap CSS.

Any help would be appreciable.

Ashwin Bhamare
  • 415
  • 2
  • 7
Souvik
  • 1,219
  • 3
  • 16
  • 38

6 Answers6

4

@Souvik, You need to install your bootstrap first and the need to give link of bootstrap files in "angular.json".

The following are the ways to use bootstrap in an angular project, hope so this will work for you.

Method 1: Using Angular CLI (npm install).

For Installing a Bootstrap in your project you can use the following command

npm install bootstrap --save

After that, You need to add it in you "angular.json" file, which is located at the root of the project

"styles": [

              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "src/styles.scss"
          ],

After adding you need to close and run your project once so that it will work fine

Method 2: Using a CDN (Copy and Paste method). You can use an old way to use bootstrap in project, the URL to the path is added to the index.html for global referenced.

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

Method 3: Adding bootstrap CSS files to your project (Using CSS import ): You can directly add bootstrap folders to your asset directory in your angular project and reference the folder directly into your style.css or style.scss using

@import url("bootstrap.min.css"); 
Ashwin Bhamare
  • 415
  • 2
  • 7
2

it is because the last HTML generated now is like this,

<div class="col-lg-12">
      <div class="form-group row">
        <label class="col-sm-1 col-form-label-lg font-weight-bold">Country</label>
         <app-country>
           <div class="col-sm-5">
                <select [(ngModel)]='countryDetails.default' class="form-control form-control-lg">
                  <option *ngFor="let province of countryDetails.details" value= 
                  {{country.id}}>
                  {{country.value}}
                  </option>
                </select>
             </div>
        </app-country>
      </div>
 </div>

But earlier ,

    <div class="col-lg-12">
      <div class="form-group row">
        <label class="col-sm-1 col-form-label-lg font-weight-bold">Country</label>
           <div class="col-sm-5">
              <app-country>
                  <select [(ngModel)]='countryDetails.default' class="form-control form-control-lg">
                     <option *ngFor="let province of countryDetails.details" value= 
                       {{country.id}}>
                       {{country.value}}
                     </option>
                   </select>
              </app-country>
           </div>
      </div>
 </div>

As you can see <app-country> position is different in your approaches. Before col-sm-5 and after col-sm-5

This causes Bootstrap row class to break the css :)

1

If you are using an earlier version of Angular, the encapsulation default setting was different. You can try

@Component({
    // ...
    encapsulation: ViewEncapsulation.None
})

for the child component in your .ts file.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
simplyVlad
  • 11
  • 2
1

Update: Solution 2

Took reference from https://stackoverflow.com/a/52717242/5061139

You can create a directive then to remove the parent element dynamically.

@Directive({
   selector: '[remove-wrapper]'
})

export class RemoveWrapperDirective {
   constructor(private el: ElementRef) {
     const parentElement = el.nativeElement.parentElement;
     const element = el.nativeElement;
     parentElement.removeChild(element);
     if(parentElement.parentNode){
       parentElement.parentNode.insertBefore(element, parentElement.nextSibling);
       parentElement.parentNode.removeChild(parentElement);
     }
   }
}

And to use it like this,

<div class="col-lg-12">
  <div class="form-group row">
    <label class="col-sm-1 col-form-label-lg font-weight-bold">Country</label>
    <app-country></app-country>
  </div>
</div>

And child component like this:

<div class="col-sm-5" remove-wrapper>
  <select [(ngModel)]='countryDetails.default' class="form-control form-control-lg">
    <option *ngFor="let province of countryDetails.details" value= . {{country.id}}>
      {{country.value}}
    </option>
  </select>
</div>

This will meet your requirements.

-----><------

So, the issue with your approach is there is an extra element wrapper of your child component when it gets rendered in the DOM. So, the rendered HTML is like

<div class="col-lg-12">
  <div class="form-group row">
    <label class="col-sm-1 col-form-label-lg font-weight-bold">Country</label>
     <app-country>
       <div class="col-sm-5">
            <select [(ngModel)]='countryDetails.default' class="form-control form-control-lg">
              <option *ngFor="let province of countryDetails.details" value= 
              {{country.id}}>
              {{country.value}}
              </option>
            </select>
         </div>
    </app-country>
  </div>

As you can see there is extra <app-country></app-country> wrapper in the HTML. To tackle this problem you can use attribute selector for your child component.

@Component({
  selector: '[app-country]',
  template:  './app-country.component.html',
  styleUrls: ['./app-country.component.css']
})

And in your parent component use this as:

<div class="col-lg-12">
  <div class="form-group row">
    <label class="col-sm-1 col-form-label-lg font-weight-bold">Country</label>
    <div class="col-sm-5" app-country>
    </div>
  </div>
</div>

Now, you will not get any extra element wrapper and your bootstrap css will work fine. Hope this will help solve your problem.

Ankit Agarwal
  • 1,350
  • 10
  • 15
  • What I am trying is to move the total div into my child component. Because wherever this component will be imported will be with same column size and details. My intention is to reduce the code while importing. – Souvik Nov 06 '19 at 19:19
  • @Souvik I have updated the answer and proposed another solution for your case – Ankit Agarwal Nov 07 '19 at 06:28
  • Hey this solved my initial problem of the drop down CSS but as the label will be same also how can I move the label in the component. With this code it's removing the label if I put it in component. – Souvik Nov 09 '19 at 07:15
0

The answer by @ankit-agarwal is relevant. Still, if you want to force the class col-sm-5 onto your component, inside it, you should use HostBinding. HostBinding allows you to attach attributes/classes/styles to the Dom element hosting the component or directive.

So, in your example, and keeping the app-country selector (I don't really like attribute selectors for components) you would need:

/* country.component.css */
/* This is necessary in order to have the app-country element behave as a
 * div in the page layout. */
:host {
  display: block;
}
<!-- country.component.html -->
<!-- you don't need the div, because we are going to attach the class
     to the app-country tag itself. -->
<select [(ngModel)]='countryDetails.default' class="form-control form-control-lg">
  <option *ngFor="let province of countryDetails.details" value={{country.id}}>
    {{country.value}}
  </option>
</select>
@Component({
  selector: 'app-country',
  ...
})
export class CountryComponent {
  @HostBinding('class.col-sm-5') cssWidthClass = true;
}

The true will instruct Angular to output the col-sm-5 class in the list of classes present in the hosting tag (app-country).
You can have an additional Input property used to set the cssWidthClass field (the name is really not important, by the way) to null, in order to not output the col-sm-5 class, if the need arises.

Alberto Chiesa
  • 7,022
  • 2
  • 26
  • 53
  • This solution also works for me. But can you tell me how can I add the label also under the component. – Souvik Nov 09 '19 at 07:28
0

Your child component will be rendered inside the parent component tag (as below).

 <app-country>
<div class="col-sm-5">
  ...
</div>
</app-country>

So modify your css accordingly or use the component using attribute selector in the parent

eg. <div class="col-sm-5" app-country></div>
Santhosh S
  • 782
  • 5
  • 17