窗口未定义角度通用第三库

时间:2017-10-20 13:12:17

标签: javascript angular typescript angular-universal

我正在使用库ng2-mqtt,我在我的组件中使用它:

 import 'ng2-mqtt/mqttws31.js';
declare var Paho: any;

现在我收到以下错误:

ReferenceError: window is not defined
    at Object.<anonymous> (/Users/Picchu/Documents/em3/node_modules/ng2-mqtt/mqttws31.js:2143:4)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.require (module.js:483:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/Users/Picchu/Documents/em3/dist/server.js:18707:18)

如何解决此问题?

6 个答案:

答案 0 :(得分:5)

避免服务器错误的一种可能方法是不渲染使用window的组件(如果是选项)。类似的东西:

<ng-container *ngIf="isBrowser">
   <!-- mqttws31-component -->
   <mqttws31-component></mqttws31-component> 
</ng-container>

可以从( ng2

导入isBrowser
import { isBrowser } from 'angular2-universal';

或者如果 ng4 + ,您也可以在浏览器模块中定义:

// app.browser
@NgModule({
  providers: [
    { provide: 'isBrowser', useValue: true }
  ]
})

然后从构造函数

注入
export class SomeComponent implements OnInit {
  constructor(@Inject('isBrowser') private isBrowser: boolean)
  ngOnInit() { 
    // example usage, this could be anywhere in this Component of course
    if (this.isBrowser) { 
      alert('we're in the browser!');
    }
}

答案 1 :(得分:3)

请遵循以下两个步骤:

  1. 在组件中使用 PLATFORM_ID 注入来检测它是浏览器平台还是服务器平台,如下所示:

    constructor(
        @Inject(DOCUMENT) private document: Document,
        @Inject(PLATFORM_ID) private platformId: any) {}
    
    ngOnInit() {
        this.scrollToTop();
    }
    
    scrollToTop() {
        if (isPlatformBrowser(this.platformId)) {
            // your code
        }
    }
    

但是这种策略适用于我们自己编写的代码,但是如果我们使用访问窗口,文档或其他DOM全局​​变量的第三方库怎么办?

第三方库中的JavaScript可能会由于在服务器渲染期间尝试访问这些全局变量而引发错误,但我们无法控制该库的代码。在这种情况下,我们可以安装诸如domino之类的库,然后为服务器中的窗口和文档对象创建填充程序

  1. 安装多米诺骨牌,并在server.ts文件中将其用作this

答案 2 :(得分:0)

window不应该在服务器端的通用应用程序中使用,因为Node.js没有window,并且假的global.window当前影响了Angular检测全局变量。

如果程序包使用window,则可以在其存储库中打开问题和/或可以将其分叉并更改为不使用window

由于依赖window的软件包通常依赖于特定于客户端的内容,因此即使解决了这个问题,它们也无法在服务器端按预期工作。

Package description说:

  

取决于来自https://eclipse.org/paho/clients/js/

的图书馆

虽然library description说:

  

Paho JavaScript Client是一个用Javascript编写的MQTT 基于浏览器的客户端库,它使用WebSockets连接到MQTT Broker。

通常应该在服务器端按预期工作的第三方Angular模块应该被存根或模拟;带有伪指令和服务的虚拟模块导入app.server.ts而不是真实模块。

答案 3 :(得分:0)

Angular 6在server.ts中使用:

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync('dist/browser/index.html').toString();
const win = domino.createWindow(template);

global['window'] = win;
global['document'] = win.document;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;

答案 4 :(得分:0)

更新

扩展李小龙的答案,如果需要位置或窗口之类的浏览器API,我们可以避免加载无法在服务器端呈现的组件。

This answer解释了如何使用

.squeeze_

只需将PLATFORM_ID注入您的服务中,然后将其传递给class NNP_RNN(nn.Module): def __init__(self): super(NNP_RNN, self).__init__() self.lstm_1 = nn.LSTM(input_size=1, hidden_size=512, batch_first=True) self.lstm_2 = nn.LSTM(input_size=512, hidden_size=512, batch_first=True) self.lstm_3 = nn.LSTM(input_size=512, hidden_size=512, batch_first=True) self.dense_1 = nn.Linear(in_features=512, out_features=256) self.dense_2 = nn.Linear(in_features=256, out_features=58) def forward(self, x): batch_size = x.size(0) h_0 = NNP_RNN.init_hidden((1, batch_size, 512)) c_0 = NNP_RNN.init_hidden((1, batch_size, 512)) x, _ = self.lstm_1(x, (h_0, c_0)) x = F.dropout(x, 0.3) x, _ = self.lstm_2(x, (h_0, c_0)) x = F.dropout(x, 0.2) _, (x, _) = self.lstm_3(x, (h_0, c_0)) x = x.squeeze_(0) x = self.dense_1(x) x = F.dropout(x, 0.1) x = self.dense_2(x) return x @staticmethod def init_hidden(dims): return torch.zeros(dims, device=device) optimizer = torch.optim.Adam(model.parameters(), lr=0.05) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, factor=0.2, verbose=True, patience=5) criterion = nn.CrossEntropyLoss() for epoch in range(1, epochs + 1): epoch_loss = 0 epoch_corrects = 0 for features, labels in tqdm(data, ncols=800): features = features.to(device) labels = labels.to(device) optimizer.zero_grad() batch_size = features.size(0) output = model(features) loss = criterion(output, labels) loss.backward() optimizer.step() corrects = torch.argmax(output, dim=1) corrects = torch.eq(corrects, labels).sum().item() epoch_corrects += corrects epoch_loss += loss.clone() * batch_size epoch_loss /= len(data.dataset) epoch_corrects /= len(data.dataset) print(f'Loss epoch #{epoch} = {epoch_loss:.10f}, Accuracy = {epoch_corrects}') scheduler.step(epoch_loss) 即可获得布尔值。因此,如果依赖于浏览器API,则可以显示/隐藏无法在服务器上呈现的组件。

答案 5 :(得分:0)

为我工作ng9+

// app.module.ts
@NgModule({
  providers: [
    { provide: 'isBrowser', useValue: true }
  ]
})

并替换绝对路径(删除第一个/):

/assets/images/... 到 assets/images/...